From 20098b5a549820ad27282bb5760890674535bbdb Mon Sep 17 00:00:00 2001 From: Robert Greig Date: Wed, 28 Feb 2007 11:05:20 +0000 Subject: (Patch submitted by Rupert Smith). java_iop.diff This adds some interop tests (which are just copies of the existing topic tests) in a new integrationtests module. The java 1.4. client build has been updated to be able to run these tests. They succesfully talk to each other and the .net code. This is intended to be a starting point of a more in depth interop test as per the spec, but does not implement what is in the draft spec yet. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@512699 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/README.txt | 13 + qpid/java/integrationtests/pom.xml | 127 +++++++++ .../java/org/apache/qpid/interop/Listener.java | 291 +++++++++++++++++++++ .../java/org/apache/qpid/interop/Publisher.java | 244 +++++++++++++++++ 4 files changed, 675 insertions(+) create mode 100644 qpid/java/integrationtests/README.txt create mode 100644 qpid/java/integrationtests/pom.xml create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Listener.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Publisher.java (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/README.txt b/qpid/java/integrationtests/README.txt new file mode 100644 index 0000000000..00a21883a9 --- /dev/null +++ b/qpid/java/integrationtests/README.txt @@ -0,0 +1,13 @@ +This module contains integration tests, for testing a java client againt *any* broker +implementation or against other clients. These tests must not rely on starting the +Java broker in-vm but must depend on a broker being started independantly before running +the tests in this module. By default tests in this module will expect the broker to be +started on localhost on the default port, but this can be overridden by passing in a +sys property to maven. Interop tests are in this module. Java broker specific tests that +use an in-vm broker should go in the systests module. + +Don't set the tests in this module to run by default as part of the maven build, until +there is a script to start and stop the broker; needed to fully automate these tests. +Interop tests will always be run using a seperate script (not from maven) but it might +be worthwile to script into the maven build starting of the Java broker, and running +these tests against it. \ No newline at end of file diff --git a/qpid/java/integrationtests/pom.xml b/qpid/java/integrationtests/pom.xml new file mode 100644 index 0000000000..79d587f360 --- /dev/null +++ b/qpid/java/integrationtests/pom.xml @@ -0,0 +1,127 @@ + + + + 4.0.0 + org.apache.qpid + qpid-integrationtests + jar + 1.0-incubating-M2-SNAPSHOT + Qpid Integration Tests + + + org.apache.qpid + qpid + 1.0-incubating-M2-SNAPSHOT + + + + .. + + + + + + + org.apache.qpid + qpid-client + + + + + junit + junit + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + + org.codehaus.mojo + retrotranslator-maven-plugin + + + retro-client + package + + translate + + + + ${project.build.directory}/${project.build.finalName}-java14.jar + ${retrotranslator.verify} + + ${retrotranslator.1.4-rt-path} + ${retrotranslator.1.4-jce-path} + ${retrotranslator.1.4-jsse-path} + ${retrotranslator.1.4-sasl-path} + + false + + + ${project.build.directory} + ${project.build.finalName}.jar + + + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + package + + attach-artifact + + + + + ${project.build.directory}/${project.build.finalName}-java14.jar + jar + java14 + + + + + + + + + + + + + + + diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Listener.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Listener.java new file mode 100644 index 0000000000..dbd07958fd --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Listener.java @@ -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; + +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 Publisher}. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Count messages received on a topic. {@link Publisher} + *
Send reports on messages received, when requested to. {@link Publisher} + *
Shutdown, when requested to. {@link Publisher} + *
+ * + * @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 true if it is a termination request control message, false 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 true if it is a report request control message, false 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 trueIf the specified field has the specified value, fals 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); + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Publisher.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Publisher.java new file mode 100644 index 0000000000..cab679876f --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Publisher.java @@ -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; + +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 Listener}. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
+ * + * @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(); + } +} -- cgit v1.2.1 From a9ae509e39fba83366572c06cb8ede2c33e29609 Mon Sep 17 00:00:00 2001 From: Robert Greig Date: Wed, 7 Mar 2007 11:25:26 +0000 Subject: (Patch submitted by Rupert Smith) Updated to latest snapshot of retrotranslator plugin. Concrete release to be made soon from this snapshot, will change over to conrete version once this happens. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@515535 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/pom.xml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/pom.xml b/qpid/java/integrationtests/pom.xml index 79d587f360..1231918f26 100644 --- a/qpid/java/integrationtests/pom.xml +++ b/qpid/java/integrationtests/pom.xml @@ -45,10 +45,11 @@ qpid-client - + junit junit + compile @@ -82,12 +83,12 @@ ${retrotranslator.1.4-sasl-path} false - - - ${project.build.directory} - ${project.build.finalName}.jar - - + + ${project.build.directory}> + + ${project.build.finalName}.jar + + -- cgit v1.2.1 From 522381c8740130e34811c476ec4024b1f302a0d8 Mon Sep 17 00:00:00 2001 From: Robert Greig Date: Tue, 13 Mar 2007 12:10:14 +0000 Subject: Fixed problems with new retrotranslator plugin snapshot. Upgraded to use the new translate-project goal. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@517660 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/pom.xml | 50 ++++++++++++++------------------------ 1 file changed, 18 insertions(+), 32 deletions(-) (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/pom.xml b/qpid/java/integrationtests/pom.xml index 1231918f26..5e22cc7990 100644 --- a/qpid/java/integrationtests/pom.xml +++ b/qpid/java/integrationtests/pom.xml @@ -45,6 +45,13 @@ qpid-client + + uk.co.thebadgerset + junit-toolkit + 0.6-SNAPSHOT + compile + + junit @@ -52,6 +59,13 @@ compile + + + net.sf.retrotranslator + retrotranslator-runtime + provided + + @@ -67,10 +81,10 @@ retrotranslator-maven-plugin - retro-client + retro-intergration-tests package - translate + translate-project @@ -83,41 +97,13 @@ ${retrotranslator.1.4-sasl-path} false - - ${project.build.directory}> - - ${project.build.finalName}.jar - - + java14 + true - - - org.codehaus.mojo - build-helper-maven-plugin - - - attach-artifacts - package - - attach-artifact - - - - - ${project.build.directory}/${project.build.finalName}-java14.jar - jar - java14 - - - - - - - -- cgit v1.2.1 From b2d2ef3ac26a2c7080c1f04e9797ebaefe62e878 Mon Sep 17 00:00:00 2001 From: Robert Greig Date: Tue, 13 Mar 2007 12:21:36 +0000 Subject: Skeleton of interop testing code added. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@517664 13f79535-47bb-0310-9956-ffa450edef68 --- .../java/org/apache/qpid/interop/Listener.java | 291 --------------------- .../java/org/apache/qpid/interop/Publisher.java | 244 ----------------- .../interop/coordinator/CoordinatingTestCase.java | 116 ++++++++ .../qpid/interop/coordinator/Coordinator.java | 168 ++++++++++++ .../interop/coordinator/TestClientDetails.java | 35 +++ .../java/org/apache/qpid/interop/old/Listener.java | 291 +++++++++++++++++++++ .../org/apache/qpid/interop/old/Publisher.java | 244 +++++++++++++++++ .../interop/testclient/InteropClientTestCase.java | 95 +++++++ .../apache/qpid/interop/testclient/TestClient.java | 213 +++++++++++++++ .../testclient/testcases/TestCase1DummyRun.java | 75 ++++++ .../testclient/testcases/TestCase2BasicP2P.java | 133 ++++++++++ .../org/apache/qpid/util/ClasspathScanner.java | 146 +++++++++++ .../org/apache/qpid/util/ConversationHelper.java | 167 ++++++++++++ 13 files changed, 1683 insertions(+), 535 deletions(-) delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Listener.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Publisher.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Listener.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Listener.java deleted file mode 100644 index dbd07958fd..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Listener.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * - * 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; - -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 Publisher}. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Count messages received on a topic. {@link Publisher} - *
Send reports on messages received, when requested to. {@link Publisher} - *
Shutdown, when requested to. {@link Publisher} - *
- * - * @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 true if it is a termination request control message, false 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 true if it is a report request control message, false 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 trueIf the specified field has the specified value, fals 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); - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Publisher.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Publisher.java deleted file mode 100644 index cab679876f..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/Publisher.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * - * 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; - -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 Listener}. - * - *

- *
CRC Card
Responsibilities Collaborations - *
- * - * @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(); - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java new file mode 100644 index 0000000000..12faa64528 --- /dev/null +++ b/qpid/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). + * + *

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: + * + *

+ * 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.");
+ *   }
+ * }
+ *
+ * 
+ * + *

+ *
CRC Card
Responsibilities Collaborations + *
Coordinate the test sequence amongst participants. {@link ConversationHelper} + *
+ */ +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 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 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/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java new file mode 100644 index 0000000000..535b4ae014 --- /dev/null +++ b/qpid/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; + +/** + *

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. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Find out what test clients are available. + *
Decorate available tests to run all available clients. + *
Attach XML test result logger. + *
Terminate the interop testing framework. + *
+ */ +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 null, 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 null. + */ + 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: + * + *

+ *
-b The broker URL. Mandatory. + *
-h The virtual host. Optional. + *
name=value Trailing argument define name/value pairs. Added to system properties. Optional. + *
+ * + * @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/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java new file mode 100644 index 0000000000..3a201b6899 --- /dev/null +++ b/qpid/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; + +/** + *

+ *
CRC Card
Responsibilities Collaborations + *
+ */ +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/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java new file mode 100644 index 0000000000..5545f8d2dc --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java @@ -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}. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Count messages received on a topic. {@link org.apache.qpid.interop.old.Publisher} + *
Send reports on messages received, when requested to. {@link org.apache.qpid.interop.old.Publisher} + *
Shutdown, when requested to. {@link org.apache.qpid.interop.old.Publisher} + *
+ * + * @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 true if it is a termination request control message, false 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 true if it is a report request control message, false 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 trueIf the specified field has the specified value, fals 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); + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java new file mode 100644 index 0000000000..f3a545f580 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java @@ -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}. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
+ * + * @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(); + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java new file mode 100644 index 0000000000..57726285f9 --- /dev/null +++ b/qpid/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. + * + *

+ *
CRC Card
Responsibilities + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Perform test case actions. + *
Generate test reports. + *
+ */ +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 true to accept the invitation, false 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/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java new file mode 100644 index 0000000000..2c04a8e52b --- /dev/null +++ b/qpid/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}. + * + *

+ *
Messages Handled by TestClient
Message Action + *
Invite(compulsory) Reply with Enlist. + *
Invite(test case) Reply with Enlist if test case available. + *
AssignRole(test case) Reply with Accept Role if matches an enlisted test. Keep test parameters. + *
Start Send test messages defined by test parameters. Send report on messages sent. + *
Status Request Send report on messages received. + *
+ * + *

+ *
CRC Card
Responsibilities Collaborations + *
Handle all incoming control messages. {@link InteropClientTestCase} + *
Configure and look up test cases by name. {@link InteropClientTestCase} + *
+ */ +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 null, 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: + * + *

+ *
-b The broker URL. Mandatory. + *
-h The virtual host. Optional. + *
name=value Trailing argument define name/value pairs. Added to system properties. Optional. + *
+ * + * @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/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java new file mode 100644 index 0000000000..570e4ff25c --- /dev/null +++ b/qpid/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. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Perform test case actions. + *
Generate test reports. + *
+ */ +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/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java new file mode 100644 index 0000000000..c3c05d8fd9 --- /dev/null +++ b/qpid/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. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages. + *
Generate test reports. + *
+ */ +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 true to accept the invitation, false 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/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java new file mode 100644 index 0000000000..1dd00da53b --- /dev/null +++ b/qpid/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. + * + *

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. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Find all classes matching type and name pattern on the classpath. + *
+ */ +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> 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 splitClassPath(String classPath) + { + List result = new LinkedList(); + 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/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java new file mode 100644 index 0000000000..bda089045a --- /dev/null +++ b/qpid/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. + * + *

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): + * + *

+ * 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();
+ *  }
+ * }
+ *
+ * 
+ * + *

+ *
CRC Card
Responsibilities Collaborations + *
Associate messages to a conversation using correlation ids. + *
Auto manage sessions for conversations. + *
Store messages not in conversation in dead letter box. + *
+ * + * @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 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 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) + { } + } +} -- cgit v1.2.1 From 5495e89711087f5f5896ebc30e25dae222a72da0 Mon Sep 17 00:00:00 2001 From: Robert Greig Date: Thu, 15 Mar 2007 10:12:57 +0000 Subject: Commit of interop test stuff prior to M2 branch. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@518559 13f79535-47bb-0310-9956-ffa450edef68 --- .../interop/coordinator/CoordinatingTestCase.java | 7 +- .../interop/coordinator/TestClientDetails.java | 2 + .../apache/qpid/interop/testclient/TestClient.java | 269 +++++++++++++++------ .../org/apache/qpid/util/ClasspathScanner.java | 121 +++++++-- .../org/apache/qpid/util/ConversationHelper.java | 144 +++++++++-- 5 files changed, 427 insertions(+), 116 deletions(-) (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java index 12faa64528..efeda78abf 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java @@ -67,8 +67,8 @@ public abstract class CoordinatingTestCase extends TestCase * @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 allClients, Properties testProperties) + public void TestCase(TestClientDetails sender, TestClientDetails receiver, Collection allClients, + Properties testProperties) { } /** @@ -83,8 +83,7 @@ public abstract class CoordinatingTestCase extends TestCase * * @return The test results from the senders and receivers. */ - protected Object[] sequenceTest(TestClientDetails sender, TestClientDetails receiver, - Collection allParticipatingClients, Properties testProperties) + protected Object[] sequenceTest(TestClientDetails sender, TestClientDetails receiver, Properties testProperties) { // Check if the sender and recevier did not accept the invite to this test. { diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java index 3a201b6899..fcfb5a08fd 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java @@ -28,8 +28,10 @@ package org.apache.qpid.interop.coordinator; public class TestClientDetails { /** The test clients name. */ + public String clientName; /* The test clients unqiue sequence number. Not currently used. */ /** The routing key of the test clients control topic. */ + public String privateControlKey; } diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java index 2c04a8e52b..a623687a0f 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java @@ -20,12 +20,22 @@ */ package org.apache.qpid.interop.testclient; +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import java.util.Properties; -import javax.jms.Message; -import javax.jms.MessageListener; +import javax.jms.*; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import org.apache.log4j.Logger; + +import org.apache.qpid.util.ClasspathScanner; import org.apache.qpid.util.CommandLineParser; +import org.apache.qpid.util.PropertiesUtils; /** * Implements a test client as described in the interop testing spec @@ -49,76 +59,26 @@ import org.apache.qpid.util.CommandLineParser; */ public class TestClient implements MessageListener { + private static Logger log = Logger.getLogger(TestClient.class); + /** Holds the URL of the broker to run the tests on. */ String brokerUrl; /** Holds the virtual host to run the tests on. If null, 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. + /** Holds all the test cases loaded from the classpath. */ + Map testCases = new HashMap(); - // Reply by accepting the invite in an Enlist message. - } - } - }, - ASSIGN_ROLE - { - public void onMessage(Message message) - { - // Extract the test properties. + InteropClientTestCase currentTestCase; - // Reply by accepting the role in an Accept Role message. - } - }, - START - { - public void onMessage(Message message) - { - // Start the current test case. + public static final String CONNECTION_PROPERTY = "connectionfactory.broker"; + public static final String CONNECTION_NAME = "broker"; + public static final String CLIENT_NAME = "java"; + public static final String DEFAULT_CONNECTION_PROPS_RESOURCE = "org/apache/qpid/interop/client/connection.properties"; - // 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); - } + private MessageProducer producer; + private Session session; public TestClient(String brokerUrl, String virtualHost) { @@ -172,42 +132,199 @@ public class TestClient implements MessageListener // Create a test client and start it running. TestClient client = new TestClient(brokerUrl, virtualHost); - client.start(); + + try + { + client.start(); + } + catch (Exception e) + { + log.error("The test client was unable to start.", e); + System.exit(1); + } } - private void start() + private void start() throws JMSException { // Use a class path scanner to find all the interop test case implementations. + Collection> testCaseClasses = + ClasspathScanner.getMatches(InteropClientTestCase.class, "^TestCase.*", true); // Create all the test case implementations and index them by the test names. + for (Class nextClass : testCaseClasses) + { + try + { + InteropClientTestCase testCase = nextClass.newInstance(); + testCases.put(testCase.getName(), testCase); + } + catch (InstantiationException e) + { + log.warn("Could not instantiate test case class: " + nextClass.getName(), e); + // Ignored. + } + catch (IllegalAccessException e) + { + log.warn("Could not instantiate test case class due to illegal access: " + nextClass.getName(), e); + // Ignored. + } + } // Open a connection to communicate with the coordinator on. + Connection connection = createConnection(DEFAULT_CONNECTION_PROPS_RESOURCE, brokerUrl, virtualHost); + + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); // Set this up to listen for control messages. + MessageConsumer consumer = session.createConsumer(session.createTopic("iop.control." + CLIENT_NAME)); + consumer.setMessageListener(this); // Create a producer to send replies with. + producer = session.createProducer(null); + + // Start listening for incoming control messages. + connection.start(); } /** - * Handles all incoming control messages. + * Establishes a JMS connection using a properties file and qpids built in JNDI implementation. This is a simple + * convenience method for code that does anticipate handling connection failures. All exceptions that indicate + * that the connection has failed, are wrapped as rutime exceptions, preumably handled by a top level failure + * handler. * - * @param message The incoming message. + * @todo Make username/password configurable. Allow multiple urls for fail over. Once it feels right, move it + * to a Utils library class. + * + * @param connectionPropsResource The name of the connection properties file. + * @param brokerUrl The broker url to connect to, null to use the default from the properties. + * @param virtualHost The virtual host to connectio to, null to use the default. + * + * @return A JMS conneciton. */ - public void onMessage(Message message) + private static Connection createConnection(String connectionPropsResource, String brokerUrl, String virtualHost) { - // Delegate the message handling to the message type specific handler. - extractMessageType(message).onMessage(message); + try + { + Properties connectionProps = + PropertiesUtils.getProperties(TestClient.class.getClassLoader().getResourceAsStream( + connectionPropsResource)); + + String connectionString = + "amqp://guest:guest/" + ((virtualHost != null) ? virtualHost : "") + "?brokerlist='" + brokerUrl + "'"; + connectionProps.setProperty(CONNECTION_PROPERTY, connectionString); + + Context ctx = new InitialContext(connectionProps); + + ConnectionFactory cf = (ConnectionFactory) ctx.lookup(CONNECTION_NAME); + Connection connection = cf.createConnection(); + + return connection; + } + catch (IOException e) + { + throw new RuntimeException(e); + } + catch (NamingException e) + { + throw new RuntimeException(e); + } + catch (JMSException e) + { + throw new RuntimeException(e); + } } /** - * Determines the control messsage type of incoming messages. - * - * @param message The message to determine the type of. + * Handles all incoming control messages. * - * @return The control message type of the message. + * @param message The incoming message. */ - protected ControlMessages extractMessageType(Message message) + public void onMessage(Message message) { - return null; + try + { + String controlType = message.getStringProperty("CONTROL_TYPE"); + String testName = message.getStringProperty("TEST_NAME"); + + // Check if the message is a test invite. + if ("INVITE".equals(controlType)) + { + String testCaseName = message.getStringProperty("TEST_NAME"); + + // Flag used to indicate that an enlist should be sent. Only enlist to compulsory invites or invites + // for which test cases exist. + boolean enlist = false; + + if (testCaseName != null) + { + // Check if the requested test case is available. + InteropClientTestCase testCase = testCases.get(testCaseName); + + if (testCase != null) + { + // Make the requested test case the current test case. + currentTestCase = testCase; + enlist = true; + } + } + else + { + enlist = true; + } + + if (enlist) + { + // Reply with the client name in an Enlist message. + Message enlistMessage = session.createMessage(); + enlistMessage.setStringProperty("CONTROL_TYPE", "ENLIST"); + enlistMessage.setStringProperty("CLIENT_NAME", CLIENT_NAME); + enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + CLIENT_NAME); + enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + producer.send(message.getJMSReplyTo(), enlistMessage); + } + } + else if ("ASSIGN_ROLE".equals(controlType)) + { + // Assign the role to the current test case. + String roleName = message.getStringProperty(""); + InteropClientTestCase.Roles role = Enum.valueOf(InteropClientTestCase.Roles.class, roleName); + + currentTestCase.assignRole(role, message); + + // Reply by accepting the role in an Accept Role message. + Message acceptRoleMessage = session.createMessage(); + acceptRoleMessage.setStringProperty("CONTROL_TYPE", "ACCEPT_ROLE"); + acceptRoleMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + producer.send(message.getJMSReplyTo(), acceptRoleMessage); + } + else if ("START".equals(controlType) || "STATUS_REQUEST".equals(controlType)) + { + if ("START".equals(controlType)) + { + // Start the current test case. + currentTestCase.start(); + } + + // Generate the report from the test case and reply with it as a Report message. + Message reportMessage = currentTestCase.getReport(session); + reportMessage.setStringProperty("CONTROL_TYPE", "REPORT"); + reportMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + producer.send(message.getJMSReplyTo(), reportMessage); + } + else + { + // Log a warning about this but otherwise ignore it. + log.warn("Got an unknown control message: " + message); + } + } + catch (JMSException e) + { + // Log a warning about this, but otherwise ignore it. + log.warn("A JMSException occurred whilst handling a message."); + log.debug("Got JMSException whilst handling message: " + message, e); + } } } diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java index 1dd00da53b..35946e6c4e 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java @@ -22,6 +22,10 @@ package org.apache.qpid.util; import java.io.File; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.log4j.Logger; /** * An ClasspathScanner scans the classpath for classes that implement an interface or extend a base class and have names @@ -38,10 +42,12 @@ import java.util.*; *

Responsibilities Collaborations *
Find all classes matching type and name pattern on the classpath. *
+ * + * @todo Add logic to scan jars as well as directories. */ public class ClasspathScanner { - static final int SUFFIX_LENGTH = ".class".length(); + private static final Logger log = Logger.getLogger(ClasspathScanner.class); /** * Scans the classpath and returns all classes that extend a specified class and match a specified name. @@ -49,58 +55,124 @@ public class ClasspathScanner * 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 matchingRegexp The regular 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> getMatches(Class matchingClass, String matchingRegexp, boolean beanOnly) + public static Collection> getMatches(Class matchingClass, String matchingRegexp, + boolean beanOnly) { + // Build a compiled regular expression from the pattern to match. + Pattern matchPattern = Pattern.compile(matchingRegexp); + String classPath = System.getProperty("java.class.path"); - Map result = new HashMap(); + Map> result = new HashMap>(); + // Find matching classes starting from all roots in the classpath. for (String path : splitClassPath(classPath)) { - gatherFiles(new File(path), "", result); + gatherFiles(new File(path), "", result, matchPattern, matchingClass); } return result.values(); } - private static void gatherFiles(File classRoot, String classFileName, Map result) + /** + * Finds all matching classes rooted at a given location in the file system. If location is a directory it + * is recursively examined. + * + * @param classRoot The root of the current point in the file system being examined. + * @param classFileName The name of the current file or directory to examine. + * @param result The accumulated mapping from class names to classes that match the scan. + * + * @todo Recursion ok as file system depth is not likely to exhaust the stack. Might be better to replace with + * iteration. + */ + private static void gatherFiles(File classRoot, String classFileName, Map> result, + Pattern matchPattern, Class matchClass) { File thisRoot = new File(classRoot, classFileName); + // If the current location is a file, check if it is a matching class. if (thisRoot.isFile()) { - if (matchesName(classFileName)) + // Check that the file has a matching name. + if (matchesName(classFileName, matchPattern)) { String className = classNameFromFile(classFileName); - result.put(className, className); + + // Check that the class has matching type. + try + { + Class candidateClass = Class.forName(className); + + Class matchedClass = matchesClass(candidateClass, matchClass); + + if (matchedClass != null) + { + result.put(className, matchedClass); + } + } + catch (ClassNotFoundException e) + { + // Ignore this. The matching class could not be loaded. + log.debug("Got ClassNotFoundException, ignoring.", e); + } } return; } - - String[] contents = thisRoot.list(); - - if (contents != null) + // Otherwise the current location is a directory, so examine all of its contents. + else { - for (String content : contents) + String[] contents = thisRoot.list(); + + if (contents != null) { - gatherFiles(classRoot, classFileName + File.separatorChar + content, result); + for (String content : contents) + { + gatherFiles(classRoot, classFileName + File.separatorChar + content, result, matchPattern, matchClass); + } } } } - private static boolean matchesName(String classFileName) + /** + * Checks if the specified class file name corresponds to a class with name matching the specified regular expression. + * + * @param classFileName The class file name. + * @param matchPattern The regular expression pattern to match. + * + * @return true if the class name matches, false otherwise. + */ + private static boolean matchesName(String classFileName, Pattern matchPattern) { - return classFileName.endsWith(".class") && (classFileName.indexOf('$') < 0) && (classFileName.indexOf("Test") > 0); + String className = classNameFromFile(classFileName); + Matcher matcher = matchPattern.matcher(className); + + return matcher.matches(); } - private static boolean matchesInterface() + /** + * Checks if the specified class to compare extends the base class being scanned for. + * + * @param matchingClass The base class to match against. + * @param toMatch The class to match against the base class. + * + * @return The class to check, cast as an instance of the class to match if the class extends the base class, or + * null otherwise. + */ + private static Class matchesClass(Class matchingClass, Class toMatch) { - return false; + try + { + return matchingClass.asSubclass(toMatch); + } + catch (ClassCastException e) + { + return null; + } } /** @@ -125,17 +197,22 @@ public class ClasspathScanner } /** - * convert /a/b.class to a.b + * Translates from the filename of a class to its fully qualified classname. Files are named using forward slash + * seperators and end in ".class", whereas fully qualified class names use "." sperators and no ".class" ending. * - * @param classFileName + * @param classFileName The filename of the class to translate to a class name. * - * @return + * @return The fully qualified class name. */ private static String classNameFromFile(String classFileName) { + // Remove the .class ending. + String s = classFileName.substring(0, classFileName.length() - ".class".length()); - String s = classFileName.substring(0, classFileName.length() - SUFFIX_LENGTH); + // Turn / seperators in . seperators. String s2 = s.replace(File.separatorChar, '.'); + + // Knock off any leading . caused by a leading /. if (s2.startsWith(".")) { return s2.substring(1); diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java index bda089045a..631cab9f35 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java @@ -20,13 +20,13 @@ */ package org.apache.qpid.util; -import java.util.Collection; +import java.util.*; import java.util.Queue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicLong; -import javax.jms.Connection; -import javax.jms.Destination; -import javax.jms.Message; -import javax.jms.MessageListener; +import javax.jms.*; /** * A conversation helper, uses a message correlation id pattern to match up sent and received messages as a conversation @@ -89,7 +89,8 @@ import javax.jms.MessageListener; * 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. + * to restrict receives on that session to prevent it picking up messages bound for other conversations. Or use + * a temporary response queue, with only that session listening to it. * * @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 @@ -101,6 +102,32 @@ import javax.jms.MessageListener; */ public class ConversationHelper { + /** Holds a map from correlation id's to queues. */ + private Map> idsToQueues = new HashMap>(); + + private Session session; + private MessageProducer producer; + private MessageConsumer consumer; + + Class> queueClass; + + BlockingQueue deadLetterBox = new LinkedBlockingQueue(); + + ThreadLocal threadLocals = + new ThreadLocal() + { + protected PerThreadSettings initialValue() + { + PerThreadSettings settings = new PerThreadSettings(); + settings.conversationId = conversationIdGenerator.getAndIncrement(); + + return settings; + } + }; + + /** Generates new coversation id's as needed. */ + AtomicLong conversationIdGenerator = new AtomicLong(); + /** * Creates a conversation helper on the specified connection with the default sending destination, and listening * to the specified receiving destination. @@ -109,19 +136,53 @@ public class ConversationHelper * @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. + * + * @throws JMSException All undelying JMSExceptions are allowed to fall through. */ public ConversationHelper(Connection connection, Destination sendDestination, Destination receiveDestination, - Class queueClass) - { } + Class> queueClass) throws JMSException + { + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + producer = session.createProducer(sendDestination); + consumer = session.createConsumer(receiveDestination); + + consumer.setMessageListener(new Receiver()); + + this.queueClass = 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. + * + * @throws JMSException All undelying JMSExceptions are allowed to fall through. + */ + public void send(Message message) throws JMSException + { + PerThreadSettings settings = threadLocals.get(); + long conversationId = settings.conversationId; + message.setJMSCorrelationID(Long.toString(conversationId)); + + // Ensure that the reply queue for this conversation exists. + initQueueForId(conversationId); + + producer.send(message); + } + + /** + * Ensures that the reply queue for a conversation exists. + * + * @param conversationId The conversation correlation id. */ - public void send(Message message) - { } + private void initQueueForId(long conversationId) + { + if (!idsToQueues.containsKey(conversationId)) + { + idsToQueues.put(conversationId, ReflectionUtils.>newInstance(queueClass)); + } + } /** * Gets the next message in an ongoing conversation. This method may block until such a message is received. @@ -130,7 +191,22 @@ public class ConversationHelper */ public Message receive() { - return null; + PerThreadSettings settings = threadLocals.get(); + long conversationId = settings.conversationId; + + // Ensure that the reply queue for this conversation exists. + initQueueForId(conversationId); + + BlockingQueue queue = idsToQueues.get(conversationId); + + try + { + return queue.take(); + } + catch (InterruptedException e) + { + return null; + } } /** @@ -138,7 +214,18 @@ public class ConversationHelper * conversation are no longer valid, and any incoming messages using them will go to the dead letter box. */ public void end() - { } + { + // Ensure that the thread local for the current thread is cleaned up. + PerThreadSettings settings = threadLocals.get(); + long conversationId = settings.conversationId; + threadLocals.remove(); + + // Ensure that its queue is removed from the queue map. + BlockingQueue queue = idsToQueues.remove(conversationId); + + // Move any outstanding messages on the threads conversation id into the dead letter box. + queue.drainTo(deadLetterBox); + } /** * Clears the dead letter box, returning all messages that were in it. @@ -147,7 +234,10 @@ public class ConversationHelper */ public Collection emptyDeadLetterBox() { - return null; + Collection result = new LinkedList(); + deadLetterBox.drainTo(result); + + return result; } /** @@ -162,6 +252,32 @@ public class ConversationHelper * @param message The incoming message. */ public void onMessage(Message message) - { } + { + try + { + Long conversationId = Long.parseLong(message.getJMSCorrelationID()); + + // Find the converstaion queue to place the message on. If there is no conversation for the message id, + // the the dead letter box queue is used. + BlockingQueue queue = idsToQueues.get(conversationId); + queue = (queue == null) ? deadLetterBox : queue; + + queue.put(message); + } + catch (JMSException e) + { + throw new RuntimeException(e); + } + catch (InterruptedException e) + { + throw new RuntimeException(e); + } + } + } + + protected class PerThreadSettings + { + /** Holds the correlation id for the current threads conversation. */ + long conversationId; } } -- cgit v1.2.1 From 690ac2b87d7cfab7463039972943a0f8b9cb20a9 Mon Sep 17 00:00:00 2001 From: Robert Greig Date: Tue, 20 Mar 2007 17:23:47 +0000 Subject: Merged revisions 520415 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/M2 ........ r520415 | rgreig | 2007-03-20 14:59:07 +0000 (Tue, 20 Mar 2007) | 1 line Improvements made to interop tests. ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@520487 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/pom.xml | 9 + .../interop/coordinator/CoordinatingTestCase.java | 112 +++++++++--- .../qpid/interop/coordinator/Coordinator.java | 197 ++++++++++++++++---- .../interop/coordinator/InvitingTestDecorator.java | 199 +++++++++++++++++++++ .../qpid/interop/coordinator/OptOutTestCase.java | 52 ++++++ .../interop/coordinator/TestClientDetails.java | 52 +++++- .../apache/qpid/interop/testclient/TestClient.java | 25 ++- .../testclient/testcases/TestCase1DummyRun.java | 14 ++ .../org/apache/qpid/util/ClasspathScanner.java | 15 +- .../org/apache/qpid/util/ConversationHelper.java | 36 +++- .../org/apache/qpid/interop/connection.properties | 2 + 11 files changed, 636 insertions(+), 77 deletions(-) create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java create mode 100644 qpid/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/pom.xml b/qpid/java/integrationtests/pom.xml index 5e22cc7990..fd7ac8b039 100644 --- a/qpid/java/integrationtests/pom.xml +++ b/qpid/java/integrationtests/pom.xml @@ -107,6 +107,15 @@ + + + + false + src/resources + + **/* + + diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java index efeda78abf..f2c0acbc16 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java @@ -23,6 +23,9 @@ package org.apache.qpid.interop.coordinator; import java.util.Collection; import java.util.Properties; +import javax.jms.JMSException; +import javax.jms.Message; + import junit.framework.TestCase; import org.apache.qpid.util.ConversationHelper; @@ -55,61 +58,120 @@ import org.apache.qpid.util.ConversationHelper; * *

*
CRC Card
Responsibilities Collaborations + *
Accept notification of test case participants. {@link InvitingTestDecorator} *
Coordinate the test sequence amongst participants. {@link ConversationHelper} + *
Supply test properties *
*/ public abstract class CoordinatingTestCase extends TestCase { + /** Holds the contact details for the sending test client. */ + TestClientDetails sender; + + /** Holds the contact details for the receving test client. */ + TestClientDetails receiver; + + ConversationHelper conversation; + /** + * Creates a new coordinating test case with the specified name. * - * @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. + * @param name The test case name. + */ + public CoordinatingTestCase(String name) + { + super(name); + } + + /** + * Sets the sender test client to coordinate the test with. + * + * @param sender The contact details of the sending client in the test. + */ + public void setSender(TestClientDetails sender) + { + this.sender = sender; + } + + /** + * Sets the receiving test client to coordinate the test with. + * + * @param receiver The contact details of the sending client in the test. + */ + public void setReceiver(TestClientDetails receiver) + { + this.receiver = receiver; + } + + /** + * Supplies the sending test client. + * + * @return The sending test client. + */ + public TestClientDetails getSender() + { + return sender; + } + + /** + * Supplies the receiving test client. + * + * @return The receiving test client. */ - public void TestCase(TestClientDetails sender, TestClientDetails receiver, Collection allClients, - Properties testProperties) - { } + public TestClientDetails getReceiver() + { + return receiver; + } /** * 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. + * @param testProperties The test case definition. * * @return The test results from the senders and receivers. + * + * @throws JMSException All underlying JMSExceptions are allowed to fall through. */ - protected Object[] sequenceTest(TestClientDetails sender, TestClientDetails receiver, Properties testProperties) + protected Object[] sequenceTest(Properties testProperties) throws JMSException { - // 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. + Message assignSender = conversation.getSession().createMessage(); + assignSender.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); + assignSender.setStringProperty("ROLE", "SENDER"); + + conversation.send(assignSender); // Assign the receiver role the receiving client. + Message assignReceiver = conversation.getSession().createMessage(); + assignReceiver.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); + assignReceiver.setStringProperty("ROLE", "RECEIVER"); + + conversation.send(assignReceiver); // Wait for the senders and receivers to confirm their roles. + conversation.receive(); + conversation.receive(); // Start the test. + Message start = conversation.getSession().createMessage(); + start.setStringProperty("CONTROL_TYPE", "START"); + + conversation.send(start); // Wait for the test sender to return its report. + Message senderReport = conversation.receive(); + + // Ask the receiver for its report. + Message statusRequest = conversation.getSession().createMessage(); + statusRequest.setStringProperty("CONTROL_TYPE", "STATUS_REQUEST"); - // As the receiver for its report. + conversation.send(statusRequest); // Wait for the receiver to send its report. + Message receiverReport = conversation.receive(); - return null; + return new Message[] { senderReport, receiverReport }; } - - /*protected void setUp() - { }*/ - - /*protected void tearDown() - { }*/ } diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java index 535b4ae014..3f87becc3d 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java @@ -20,14 +20,26 @@ */ package org.apache.qpid.interop.coordinator; -import java.util.Properties; +import java.util.*; +import java.util.concurrent.LinkedBlockingQueue; + +import javax.jms.*; import junit.framework.Test; import junit.framework.TestResult; +import junit.framework.TestSuite; + +import org.apache.log4j.Logger; +import org.apache.qpid.interop.testclient.InteropClientTestCase; +import org.apache.qpid.interop.testclient.TestClient; +import org.apache.qpid.util.ClasspathScanner; import org.apache.qpid.util.CommandLineParser; +import org.apache.qpid.util.ConversationHelper; +import org.apache.qpid.util.PrettyPrintingUtils; import uk.co.thebadgerset.junit.extensions.TestRunnerImprovedErrorHandling; +import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; /** *

Implements the coordinator client described in the interop testing specification @@ -36,20 +48,27 @@ import uk.co.thebadgerset.junit.extensions.TestRunnerImprovedErrorHandling; * *

*
CRC Card
Responsibilities Collaborations - *
Find out what test clients are available. - *
Decorate available tests to run all available clients. + *
Find out what test clients are available. {@link ConversationHelper} + *
Decorate available tests to run all available clients. {@link InvitingTestDecorator} *
Attach XML test result logger. *
Terminate the interop testing framework. *
*/ public class Coordinator extends TestRunnerImprovedErrorHandling { + private static final Logger log = Logger.getLogger(Coordinator.class); + + public static final String DEFAULT_CONNECTION_PROPS_RESOURCE = "org/apache/qpid/interop/connection.properties"; + /** Holds the URL of the broker to coordinate the tests on. */ String brokerUrl; /** Holds the virtual host to coordinate the tests on. If null, then the default virtual host is used. */ String virtualHost; + /** Holds the list of all clients that enlisted, when the compulsory invite was issued. */ + Set enlistedClients = new HashSet(); + /** * Creates an interop test coordinator on the specified broker and virtual host. * @@ -58,6 +77,8 @@ public class Coordinator extends TestRunnerImprovedErrorHandling */ Coordinator(String brokerUrl, String virtualHost) { + log.debug("Coordinator(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost + "): called"); + // Retain the connection parameters. this.brokerUrl = brokerUrl; this.virtualHost = virtualHost; @@ -76,42 +97,51 @@ public class Coordinator extends TestRunnerImprovedErrorHandling */ 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); - } + // Use the command line parser to evaluate the command line. + CommandLineParser commandLine = + new CommandLineParser(new String[][] + { + { "b", "The broker URL.", "broker", "false" }, + { "h", "The virtual host to use.", "virtual host", "false" } + }); - // Extract the command line options. - String brokerUrl = options.getProperty("b"); - String virtualHost = options.getProperty("h"); + // Capture the command line arguments or display errors and correct usage and then exit. + Properties options = null; - // Add all the trailing command line options (name=value pairs) to system properties. Tests may pick up - // overridden values from there. - commandLine.addCommandLineToSysProperties(); + try + { + options = commandLine.parseCommandLine(args); + } + catch (IllegalArgumentException e) + { + System.out.println(commandLine.getErrors()); + System.out.println(commandLine.getUsage()); + System.exit(1); + } - // Scan for available test cases using a classpath scanner. - String[] testClassNames = null; + // Extract the command line options. + String brokerUrl = options.getProperty("b"); + String virtualHost = options.getProperty("h"); - // Create a coordinator and begin its test procedure. - try - { + // 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. + Collection> testCaseClasses = + ClasspathScanner.getMatches(CoordinatingTestCase.class, "^Test.*", true); + + int i = 0; + String[] testClassNames = new String[testCaseClasses.size()]; + + for (Class testClass : testCaseClasses) + { + testClassNames[i++] = testClass.getName(); + } + + // Create a coordinator and begin its test procedure. Coordinator coordinator = new Coordinator(brokerUrl, virtualHost); TestResult testResult = coordinator.start(testClassNames); @@ -127,31 +157,92 @@ public class Coordinator extends TestRunnerImprovedErrorHandling catch (Exception e) { System.err.println(e.getMessage()); + log.error("Top level handler caught execption.", e); System.exit(EXCEPTION_EXIT); } } + /** + * Starts all of the test classes to be run by this coordinator running. + * + * @param testClassNames An array of all the coordinating test case implementations. + * + * @return A JUnit TestResult to run the tests with. + * + * @throws Exception Any underlying exceptions are allowed to fall through, and fail the test process. + */ public TestResult start(String[] testClassNames) throws Exception { + log.debug("public TestResult start(String[] testClassNames = " + PrettyPrintingUtils.printArray(testClassNames) + + "): called"); + // Connect to the broker. + Connection connection = TestClient.createConnection(DEFAULT_CONNECTION_PROPS_RESOURCE, brokerUrl, virtualHost); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + Destination controlTopic = session.createTopic("iop.control"); + Destination responseQueue = session.createQueue("coordinator"); + + ConversationHelper conversation = + new ConversationHelper(connection, controlTopic, responseQueue, LinkedBlockingQueue.class); // Broadcast the compulsory invitation to find out what clients are available to test. + Message invite = session.createMessage(); + invite.setStringProperty("CONTROL_TYPE", "INVITE"); + invite.setJMSReplyTo(responseQueue); + + conversation.send(invite); // Wait for a short time, to give test clients an opportunity to reply to the invitation. + Collection enlists = conversation.receiveAll(0, 10000); - // Retain the list of all available clients. + enlistedClients = extractEnlists(enlists); // 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. + Message terminate = session.createMessage(); + terminate.setStringProperty("CONTROL_TYPE", "TERMINATE"); + + conversation.send(terminate); return result; } + /** + * For a collection of enlist messages, this method pulls out of the client details for the enlisting clients. + * + * @param enlists The enlist messages. + * + * @return A set of enlisting clients, extracted from the enlist messages. + * + * @throws JMSException Any underlying JMSException is allowed to fall through. + */ + public static Set extractEnlists(Collection enlists) throws JMSException + { + log.debug("public static Set extractEnlists(Collection enlists = " + enlists + + "): called"); + + Set enlistedClients = new HashSet(); + + // Retain the list of all available clients. + for (Message enlist : enlists) + { + TestClientDetails clientDetails = new TestClientDetails(); + clientDetails.clientName = enlist.getStringProperty("CLIENT_NAME"); + clientDetails.privateControlKey = enlist.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); + + enlistedClients.add(clientDetails); + } + + return enlistedClients; + } + /** * 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. + * in any test decorators needed to add in the coordinators ability to invite test clients to participate in + * tests. * * @param test The test to run. * @param wait Undocumented. Nothing in the JUnit javadocs to say what this is for. @@ -160,9 +251,39 @@ public class Coordinator extends TestRunnerImprovedErrorHandling */ 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. + log.debug("public TestResult doRun(Test \"" + test + "\", boolean " + wait + "): called"); + + // Wrap all tests in the test suite with WrappedSuiteTestDecorators. This is quite ugly and a bit baffling, + // but the reason it is done is because the JUnit implementation of TestDecorator has some bugs in it. + WrappedSuiteTestDecorator targetTest = null; + + if (test instanceof TestSuite) + { + log.debug("targetTest is a TestSuite"); + + TestSuite suite = (TestSuite) test; + + int numTests = suite.countTestCases(); + log.debug("There are " + numTests + " in the suite."); + + for (int i = 0; i < numTests; i++) + { + Test nextTest = suite.testAt(i); + log.debug("suite.testAt(" + i + ") = " + nextTest); + + if (nextTest instanceof CoordinatingTestCase) + { + log.debug("nextTest is a CoordinatingTestCase"); + } + } + + targetTest = new WrappedSuiteTestDecorator(suite); + log.debug("Wrapped with a WrappedSuiteTestDecorator."); + } + + // Wrap the tests in an inviting test decorator, to perform the invite/test cycle. + targetTest = new InvitingTestDecorator(targetTest, enlistedClients); - return null; + return super.doRun(targetTest, wait); } } diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java new file mode 100644 index 0000000000..64770be654 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java @@ -0,0 +1,199 @@ +/* + * + * 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.*; + +import javax.jms.JMSException; +import javax.jms.Message; + +import junit.framework.Test; +import junit.framework.TestResult; +import junit.framework.TestSuite; + +import org.apache.log4j.Logger; + +import org.apache.qpid.util.ConversationHelper; + +import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; + +/** + *

+ *
CRC Card
Responsibilities Collaborations + *
Broadcast test invitations and collect enlists. {@link ConversationHelper}. + *
Output test failures for clients unwilling to run the test case. {@link Coordinator} + *
Execute coordinated test cases. {@link CoordinatingTestCase} + *
+ */ +public class InvitingTestDecorator extends WrappedSuiteTestDecorator +{ + private static final Logger log = Logger.getLogger(InvitingTestDecorator.class); + + Set allClients; + ConversationHelper conversation; + + WrappedSuiteTestDecorator testSuite; + + /** + * Creates a wrappred suite test decorator from a test suite. + * + * @param suite The test suite. + * @param allClients The list of all clients that responded to the compulsory invite. + */ + /*public InvitingTestDecorator(TestSuite suite, Collection allClients, ConversationHelper conversation) + { + super(suite); + }*/ + + /** + * Creates a wrapped suite test decorator from another one. + * + * @param suite The test suite. + * @param allClients The list of all clients that responded to the compulsory invite. + */ + public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set allClients) + { + super(suite); + + log.debug("public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set allClients = " + + allClients + "): called"); + } + + /** + * Broadcasts a test invitation and accetps enlisting from participating clients. The wrapped test case is + * then repeated for every combination of test clients (provided the wrapped test case extends + * {@link CoordinatingTestCase}. + * + *

Any JMSExceptions during the invite/enlist conversation will be allowed to fall through as runtime exceptions, + * resulting in the non-completion of the test run. + * + * @todo Better error recovery for failure of the invite/enlist conversation could be added. + * + * @param testResult The the results object to monitor the test results with. + */ + public void run(TestResult testResult) + { + log.debug("public void run(TestResult testResult): called"); + + Collection tests = testSuite.getAllUnderlyingTests(); + + for (Test test : tests) + { + CoordinatingTestCase coordTest = (CoordinatingTestCase) test; + + // Broadcast the invitation to find out what clients are available to test. + Set enlists = null; + try + { + Message invite = conversation.getSession().createMessage(); + invite.setStringProperty("CONTROL_TYPE", "INVITE"); + invite.setStringProperty("TEST_NAME", coordTest.getName()); + + conversation.send(invite); + + // Wait for a short time, to give test clients an opportunity to reply to the invitation. + Collection replies = conversation.receiveAll(allClients.size(), 10000); + enlists = Coordinator.extractEnlists(replies); + } + catch (JMSException e) + { + throw new RuntimeException("There was a JMSException during the invite/enlist conversation.", e); + } + + // Compare the list of willing clients to the list of all available. + Set optOuts = new HashSet(allClients); + optOuts.removeAll(enlists); + + // Output test failures for clients that will not particpate in the test. + Set> failPairs = allPairs(optOuts, allClients); + + for (List failPair : failPairs) + { + CoordinatingTestCase failTest = new OptOutTestCase(""); + failTest.setSender(failPair.get(0)); + failTest.setReceiver(failPair.get(1)); + + failTest.run(testResult); + } + + // Loop over all combinations of clients, willing to run the test. + Set> enlistedPairs = allPairs(enlists, enlists); + + for (List enlistedPair : enlistedPairs) + { + // Set the sending and receiving client details on the test case. + coordTest.setSender(enlistedPair.get(0)); + coordTest.setReceiver(enlistedPair.get(1)); + + // Execute the test case. + coordTest.run(testResult); + } + } + } + + /** + * Produces all pairs of combinations of elements from two sets. The ordering of the elements in the pair is + * important, that is the pair is distinct from ; both pairs are generated. For any element, i, in + * both the left and right sets, the reflexive pair is not generated. + * + * @param left The left set. + * @param right The right set. + * + * @return All pairs formed from the permutations of all elements of the left and right sets. + */ + private Set> allPairs(Set left, Set right) + { + log.debug("private Set> allPairs(Set left = " + left + ", Set right = " + right + "): called"); + + Set> results = new HashSet>(); + + // Form all pairs from left to right. + // Form all pairs from right to left. + for (E le : left) + { + for (E re : right) + { + if (!le.equals(re)) + { + results.add(new Pair(le, re)); + results.add(new Pair(re, le)); + } + } + } + + log.debug("results = " + results); + + return results; + } + + /** + * A simple implementation of a pair, using a list. + */ + private class Pair extends ArrayList + { + public Pair(T first, T second) + { + super(); + super.add(first); + super.add(second); + } + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java new file mode 100644 index 0000000000..27e2c42a9a --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java @@ -0,0 +1,52 @@ +/* + * + * 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 junit.framework.Assert; + +/** + * An OptOutTestCase is a test case that automatically fails. It is used when a list of test clients has been generated + * from a compulsory invite, but only some of those clients have responded to a specific test case invite. The clients + * that did not respond, are automatically given a fail for the test. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Fail the test with a suitable reason. + *
+ */ +public class OptOutTestCase extends CoordinatingTestCase +{ + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public OptOutTestCase(String name) + { + super(name); + } + + /** Generates an appropriate test failure assertion. */ + public void testOptOut() + { + Assert.fail("One of " + getSender() + " and " + getReceiver() + " opted out of the test."); + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java index fcfb5a08fd..80abd4d4c9 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java @@ -30,8 +30,58 @@ public class TestClientDetails /** The test clients name. */ public String clientName; - /* The test clients unqiue sequence number. Not currently used. */ + /* The test clients unique sequence number. Not currently used. */ /** The routing key of the test clients control topic. */ public String privateControlKey; + + /** + * Two TestClientDetails are considered to be equal, iff they have the same client name. + * + * @param o The object to compare to. + * + * @return If the object to compare to is a TestClientDetails equal to this one, false otherwise. + */ + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + + if (!(o instanceof TestClientDetails)) + { + return false; + } + + final TestClientDetails testClientDetails = (TestClientDetails) o; + + if ((clientName != null) ? (!clientName.equals(testClientDetails.clientName)) + : (testClientDetails.clientName != null)) + { + return false; + } + + return true; + } + + /** + * Computes a hash code compatible with the equals method; based on the client name alone. + * + * @return A hash code for this. + */ + public int hashCode() + { + return ((clientName != null) ? clientName.hashCode() : 0); + } + + /** + * Outputs the client name and address details. Mostly used for debugging purposes. + * + * @return The client name and address. + */ + public String toString() + { + return "clientName = " + clientName + ", privateControlKey = " + privateControlKey; + } } diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java index a623687a0f..6d7ab9c978 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java @@ -75,13 +75,16 @@ public class TestClient implements MessageListener public static final String CONNECTION_PROPERTY = "connectionfactory.broker"; public static final String CONNECTION_NAME = "broker"; public static final String CLIENT_NAME = "java"; - public static final String DEFAULT_CONNECTION_PROPS_RESOURCE = "org/apache/qpid/interop/client/connection.properties"; + public static final String DEFAULT_CONNECTION_PROPS_RESOURCE = "org/apache/qpid/interop/connection.properties"; private MessageProducer producer; private Session session; public TestClient(String brokerUrl, String virtualHost) { + log.debug("public TestClient(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost + + "): called"); + // Retain the connection parameters. this.brokerUrl = brokerUrl; this.virtualHost = virtualHost; @@ -104,7 +107,7 @@ public class TestClient implements MessageListener CommandLineParser commandLine = new CommandLineParser(new String[][] { - { "b", "The broker URL.", "broker", "true" }, + { "b", "The broker URL.", "broker", "false" }, { "h", "The virtual host to use.", "virtual host", "false" } }); @@ -146,6 +149,8 @@ public class TestClient implements MessageListener private void start() throws JMSException { + log.debug("private void start(): called"); + // Use a class path scanner to find all the interop test case implementations. Collection> testCaseClasses = ClasspathScanner.getMatches(InteropClientTestCase.class, "^TestCase.*", true); @@ -201,17 +206,23 @@ public class TestClient implements MessageListener * * @return A JMS conneciton. */ - private static Connection createConnection(String connectionPropsResource, String brokerUrl, String virtualHost) + public static Connection createConnection(String connectionPropsResource, String brokerUrl, String virtualHost) { + log.debug("public static Connection createConnection(String connectionPropsResource = " + connectionPropsResource + + ", String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost + "): called"); + try { Properties connectionProps = PropertiesUtils.getProperties(TestClient.class.getClassLoader().getResourceAsStream( connectionPropsResource)); - String connectionString = - "amqp://guest:guest/" + ((virtualHost != null) ? virtualHost : "") + "?brokerlist='" + brokerUrl + "'"; - connectionProps.setProperty(CONNECTION_PROPERTY, connectionString); + if (brokerUrl != null) + { + String connectionString = + "amqp://guest:guest/" + ((virtualHost != null) ? virtualHost : "") + "?brokerlist='" + brokerUrl + "'"; + connectionProps.setProperty(CONNECTION_PROPERTY, connectionString); + } Context ctx = new InitialContext(connectionProps); @@ -241,6 +252,8 @@ public class TestClient implements MessageListener */ public void onMessage(Message message) { + log.debug("public void onMessage(Message message = " + message + "): called"); + try { String controlType = message.getStringProperty("CONTROL_TYPE"); diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java index 570e4ff25c..aa31a59b06 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java @@ -24,6 +24,8 @@ import javax.jms.JMSException; import javax.jms.Message; import javax.jms.Session; +import org.apache.log4j.Logger; + import org.apache.qpid.interop.testclient.InteropClientTestCase; /** @@ -41,29 +43,41 @@ import org.apache.qpid.interop.testclient.InteropClientTestCase; */ public class TestCase1DummyRun implements InteropClientTestCase { + private static final Logger log = Logger.getLogger(TestCase1DummyRun.class); + public String getName() { + log.debug("public String getName(): called"); + return "TC1_DummyRun"; } public boolean acceptInvite(Message inviteMessage) throws JMSException { + log.debug("public boolean acceptInvite(Message inviteMessage): called"); + // Test parameters don't matter, accept all invites. return true; } public void assignRole(Roles role, Message assignRoleMessage) throws JMSException { + log.debug("public void assignRole(Roles role, Message assignRoleMessage): called"); + // Do nothing, both roles are the same. } public void start() { + log.debug("public void start(): called"); + // Do nothing. } public Message getReport(Session session) throws JMSException { + log.debug("public Message getReport(Session session): called"); + // Generate a dummy report, the coordinator expects a report but doesn't care what it is. return session.createTextMessage("Dummy Run, Ok."); } diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java index 35946e6c4e..cd8e0a80a1 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java @@ -63,12 +63,17 @@ public class ClasspathScanner public static Collection> getMatches(Class matchingClass, String matchingRegexp, boolean beanOnly) { + log.debug("public static Collection> getMatches(Class matchingClass = " + matchingClass + + ", String matchingRegexp = " + matchingRegexp + ", boolean beanOnly = " + beanOnly + "): called"); + // Build a compiled regular expression from the pattern to match. Pattern matchPattern = Pattern.compile(matchingRegexp); String classPath = System.getProperty("java.class.path"); Map> result = new HashMap>(); + log.debug("classPath = " + classPath); + // Find matching classes starting from all roots in the classpath. for (String path : splitClassPath(classPath)) { @@ -92,15 +97,19 @@ public class ClasspathScanner private static void gatherFiles(File classRoot, String classFileName, Map> result, Pattern matchPattern, Class matchClass) { + log.debug("private static void gatherFiles(File classRoot = " + classRoot + ", String classFileName = " + + classFileName + ", Map> result, Pattern matchPattern = " + matchPattern + + ", Class matchClass = " + matchClass + "): called"); + File thisRoot = new File(classRoot, classFileName); // If the current location is a file, check if it is a matching class. if (thisRoot.isFile()) { // Check that the file has a matching name. - if (matchesName(classFileName, matchPattern)) + if (matchesName(thisRoot.getName(), matchPattern)) { - String className = classNameFromFile(classFileName); + String className = classNameFromFile(thisRoot.getName()); // Check that the class has matching type. try @@ -206,6 +215,8 @@ public class ClasspathScanner */ private static String classNameFromFile(String classFileName) { + log.debug("private static String classNameFromFile(String classFileName = " + classFileName + "): called"); + // Remove the .class ending. String s = classFileName.substring(0, classFileName.length() - ".class".length()); diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java index 631cab9f35..9b0bc07ba9 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java @@ -21,7 +21,6 @@ package org.apache.qpid.util; import java.util.*; -import java.util.Queue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicLong; @@ -109,7 +108,7 @@ public class ConversationHelper private MessageProducer producer; private MessageConsumer consumer; - Class> queueClass; + Class queueClass; BlockingQueue deadLetterBox = new LinkedBlockingQueue(); @@ -140,7 +139,7 @@ public class ConversationHelper * @throws JMSException All undelying JMSExceptions are allowed to fall through. */ public ConversationHelper(Connection connection, Destination sendDestination, Destination receiveDestination, - Class> queueClass) throws JMSException + Class queueClass) throws JMSException { session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); producer = session.createProducer(sendDestination); @@ -180,7 +179,7 @@ public class ConversationHelper { if (!idsToQueues.containsKey(conversationId)) { - idsToQueues.put(conversationId, ReflectionUtils.>newInstance(queueClass)); + idsToQueues.put(conversationId, ReflectionUtils.newInstance(queueClass)); } } @@ -209,6 +208,28 @@ public class ConversationHelper } } + /** + * Gets many messages in an ongoing conversation. If a limit is specified, then once that many messages are + * received they will be returned. If a timeout is specified, then all messages up to the limit, received within + * that timespan will be returned. + * + * @param num The number of messages to receive, or all if this is less than 1. + * @param timeout The timeout in milliseconds to receive the messages in, or forever if this is less than 1. + * + * @return All messages received within the count limit and the timeout. + */ + public Collection receiveAll(int num, long timeout) + { + Collection result = new ArrayList(); + + for (int i = 0; i < num; i++) + { + result.add(receive()); + } + + return result; + } + /** * 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. @@ -234,7 +255,7 @@ public class ConversationHelper */ public Collection emptyDeadLetterBox() { - Collection result = new LinkedList(); + Collection result = new ArrayList(); deadLetterBox.drainTo(result); return result; @@ -280,4 +301,9 @@ public class ConversationHelper /** Holds the correlation id for the current threads conversation. */ long conversationId; } + + public Session getSession() + { + return session; + } } diff --git a/qpid/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties b/qpid/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties new file mode 100644 index 0000000000..357b33bb8a --- /dev/null +++ b/qpid/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties @@ -0,0 +1,2 @@ +java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory +connectionfactory.broker = amqp://guest:guest@clientid/?brokerlist='tcp://localhost:5672' -- cgit v1.2.1 From 6aaec799e2dd38f39601fe5fe458cadf3834aae8 Mon Sep 17 00:00:00 2001 From: Robert Greig Date: Thu, 5 Apr 2007 11:47:50 +0000 Subject: Merged revisions 522994-523245 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/M2 ........ r522994 | rgreig | 2007-03-27 17:48:23 +0100 (Tue, 27 Mar 2007) | 1 line Test added for durability of messages under broker failure. ........ r523245 | rgreig | 2007-03-28 10:30:49 +0100 (Wed, 28 Mar 2007) | 1 line Reversed accidental replacing of the word 'initialize' in comments to 'establishConnection' through a method refactoring. ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@525800 13f79535-47bb-0310-9956-ffa450edef68 --- .../src/main/java/org/apache/qpid/util/ClasspathScanner.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java index cd8e0a80a1..bad49060ca 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java @@ -61,10 +61,10 @@ public class ClasspathScanner * @return All the classes that match this collector. */ public static Collection> getMatches(Class matchingClass, String matchingRegexp, - boolean beanOnly) + boolean beanOnly) { log.debug("public static Collection> getMatches(Class matchingClass = " + matchingClass - + ", String matchingRegexp = " + matchingRegexp + ", boolean beanOnly = " + beanOnly + "): called"); + + ", String matchingRegexp = " + matchingRegexp + ", boolean beanOnly = " + beanOnly + "): called"); // Build a compiled regular expression from the pattern to match. Pattern matchPattern = Pattern.compile(matchingRegexp); @@ -95,11 +95,11 @@ public class ClasspathScanner * iteration. */ private static void gatherFiles(File classRoot, String classFileName, Map> result, - Pattern matchPattern, Class matchClass) + Pattern matchPattern, Class matchClass) { log.debug("private static void gatherFiles(File classRoot = " + classRoot + ", String classFileName = " - + classFileName + ", Map> result, Pattern matchPattern = " + matchPattern - + ", Class matchClass = " + matchClass + "): called"); + + classFileName + ", Map> result, Pattern matchPattern = " + matchPattern + + ", Class matchClass = " + matchClass + "): called"); File thisRoot = new File(classRoot, classFileName); -- cgit v1.2.1 From 3f3a7f6af8d3ceb4b48fdea7605a57b5f7bff2ee Mon Sep 17 00:00:00 2001 From: Robert Greig Date: Wed, 2 May 2007 16:47:36 +0000 Subject: Removed accidentally included copyright and author tags. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@534539 13f79535-47bb-0310-9956-ffa450edef68 --- .../interop/coordinator/CoordinatingTestCase.java | 5 +- .../qpid/interop/coordinator/Coordinator.java | 65 +++++++++++----------- .../interop/coordinator/InvitingTestDecorator.java | 32 +++++------ .../testcases/CoordinatingTestCase2BasicP2P.java | 48 ++++++++++++++++ .../org/apache/qpid/util/ConversationHelper.java | 27 ++++----- 5 files changed, 112 insertions(+), 65 deletions(-) create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java index f2c0acbc16..36c270ef11 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java @@ -1,3 +1,4 @@ +/* Copyright Rupert Smith, 2005 to 2006, all rights reserved. */ /* * * Licensed to the Apache Software Foundation (ASF) under one @@ -125,7 +126,7 @@ public abstract class CoordinatingTestCase extends TestCase /** * 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 + * loop of Use Case 5. It consists of assigning the test roles, begining the test and gathering the test reports * from the participants. * * @param testProperties The test case definition. @@ -134,7 +135,7 @@ public abstract class CoordinatingTestCase extends TestCase * * @throws JMSException All underlying JMSExceptions are allowed to fall through. */ - protected Object[] sequenceTest(Properties testProperties) throws JMSException + protected Message[] sequenceTest(Properties testProperties) throws JMSException { // Assign the sender role to the sending test client. Message assignSender = conversation.getSession().createMessage(); diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java index 3f87becc3d..5e0f5b4941 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java @@ -31,8 +31,11 @@ import junit.framework.TestSuite; import org.apache.log4j.Logger; +import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase2BasicP2P; import org.apache.qpid.interop.testclient.InteropClientTestCase; import org.apache.qpid.interop.testclient.TestClient; +import org.apache.qpid.interop.testclient.testcases.TestCase1DummyRun; +import org.apache.qpid.interop.testclient.testcases.TestCase2BasicP2P; import org.apache.qpid.util.ClasspathScanner; import org.apache.qpid.util.CommandLineParser; import org.apache.qpid.util.ConversationHelper; @@ -69,6 +72,10 @@ public class Coordinator extends TestRunnerImprovedErrorHandling /** Holds the list of all clients that enlisted, when the compulsory invite was issued. */ Set enlistedClients = new HashSet(); + /** Holds the conversation helper for the control conversation. */ + private ConversationHelper conversation; + private Connection connection; + /** * Creates an interop test coordinator on the specified broker and virtual host. * @@ -99,39 +106,34 @@ public class Coordinator extends TestRunnerImprovedErrorHandling { try { - // Use the command line parser to evaluate the command line. - CommandLineParser commandLine = - new CommandLineParser(new String[][] - { - { "b", "The broker URL.", "broker", "false" }, - { "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); - } + // Use the command line parser to evaluate the command line with standard handling behaviour (print errors + // and usage then exist if there are errors). + Properties options = + CommandLineParser.processCommandLine(args, + new CommandLineParser( + new String[][] + { + { "b", "The broker URL.", "broker", "false" }, + { "h", "The virtual host to use.", "virtual host", "false" } + })); // 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. Collection> testCaseClasses = - ClasspathScanner.getMatches(CoordinatingTestCase.class, "^Test.*", true); + new ArrayList>(); + // ClasspathScanner.getMatches(CoordinatingTestCase.class, "^Test.*", true); + // Hard code the test classes till the classpath scanner is fixed. + Collections.addAll(testCaseClasses, new Class[] { CoordinatingTestCase2BasicP2P.class }); + + // Check that some test classes were actually found. + if ((testCaseClasses == null) || testCaseClasses.isEmpty()) + { + throw new RuntimeException( + "No test classes implementing CoordinatingTestCase were found on the class path."); + } int i = 0; String[] testClassNames = new String[testCaseClasses.size()]; @@ -174,17 +176,16 @@ public class Coordinator extends TestRunnerImprovedErrorHandling public TestResult start(String[] testClassNames) throws Exception { log.debug("public TestResult start(String[] testClassNames = " + PrettyPrintingUtils.printArray(testClassNames) - + "): called"); + + "): called"); // Connect to the broker. - Connection connection = TestClient.createConnection(DEFAULT_CONNECTION_PROPS_RESOURCE, brokerUrl, virtualHost); + connection = TestClient.createConnection(DEFAULT_CONNECTION_PROPS_RESOURCE, brokerUrl, virtualHost); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); Destination controlTopic = session.createTopic("iop.control"); Destination responseQueue = session.createQueue("coordinator"); - ConversationHelper conversation = - new ConversationHelper(connection, controlTopic, responseQueue, LinkedBlockingQueue.class); + conversation = new ConversationHelper(connection, controlTopic, responseQueue, LinkedBlockingQueue.class); // Broadcast the compulsory invitation to find out what clients are available to test. Message invite = session.createMessage(); @@ -222,7 +223,7 @@ public class Coordinator extends TestRunnerImprovedErrorHandling public static Set extractEnlists(Collection enlists) throws JMSException { log.debug("public static Set extractEnlists(Collection enlists = " + enlists - + "): called"); + + "): called"); Set enlistedClients = new HashSet(); @@ -282,7 +283,7 @@ public class Coordinator extends TestRunnerImprovedErrorHandling } // Wrap the tests in an inviting test decorator, to perform the invite/test cycle. - targetTest = new InvitingTestDecorator(targetTest, enlistedClients); + targetTest = new InvitingTestDecorator(targetTest, enlistedClients, conversation); return super.doRun(targetTest, wait); } diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java index 64770be654..2975082631 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java @@ -21,6 +21,7 @@ package org.apache.qpid.interop.coordinator; import java.util.*; +import java.util.concurrent.LinkedBlockingQueue; import javax.jms.JMSException; import javax.jms.Message; @@ -47,34 +48,33 @@ public class InvitingTestDecorator extends WrappedSuiteTestDecorator { private static final Logger log = Logger.getLogger(InvitingTestDecorator.class); + /** Holds the contact information for all test clients that are available and that may take part in the test. */ Set allClients; + + /** Holds the conversation helper for the control level conversation for coordinating the test through. */ ConversationHelper conversation; + /** Holds the underlying {@link CoordinatingTestCase}s that this decorator wraps. */ WrappedSuiteTestDecorator testSuite; - /** - * Creates a wrappred suite test decorator from a test suite. - * - * @param suite The test suite. - * @param allClients The list of all clients that responded to the compulsory invite. - */ - /*public InvitingTestDecorator(TestSuite suite, Collection allClients, ConversationHelper conversation) - { - super(suite); - }*/ - /** * Creates a wrapped suite test decorator from another one. * - * @param suite The test suite. - * @param allClients The list of all clients that responded to the compulsory invite. + * @param suite The test suite. + * @param availableClients The list of all clients that responded to the compulsory invite. + * @param controlConversation The conversation helper for the control level, test coordination conversation. */ - public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set allClients) + public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set availableClients, + ConversationHelper controlConversation) { super(suite); log.debug("public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set allClients = " - + allClients + "): called"); + + availableClients + ", ConversationHelper controlConversation = " + controlConversation + "): called"); + + testSuite = suite; + allClients = availableClients; + conversation = controlConversation; } /** @@ -127,7 +127,7 @@ public class InvitingTestDecorator extends WrappedSuiteTestDecorator for (List failPair : failPairs) { - CoordinatingTestCase failTest = new OptOutTestCase(""); + CoordinatingTestCase failTest = new OptOutTestCase("testOptOut"); failTest.setSender(failPair.get(0)); failTest.setReceiver(failPair.get(1)); diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java new file mode 100644 index 0000000000..4fb9729c4a --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java @@ -0,0 +1,48 @@ +package org.apache.qpid.interop.coordinator.testcases; + +import java.util.Properties; + +import javax.jms.Message; + +import junit.framework.Assert; + +import org.apache.qpid.interop.coordinator.CoordinatingTestCase; + +/** + *

+ *
CRC Card
Responsibilities Collaborations + *
Setup p2p test parameters and compare with test output. {@link CoordinatingTestCase} + *
+ */ +public class CoordinatingTestCase2BasicP2P extends CoordinatingTestCase +{ + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public CoordinatingTestCase2BasicP2P(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + */ + public void testBasicP2P() throws Exception + { + Properties testConfig = new Properties(); + testConfig.setProperty("TEST_CASE", "TC2_BasicP2P"); + testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); + testConfig.setProperty("P2P_NUM_MESSAGES", "50"); + + Message[] reports = sequenceTest(testConfig); + + // Compare sender and receiver reports. + int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); + Assert.assertEquals("Sender and receiver messages sent did not match up.", messagesSent, messagesReceived); + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java index 9b0bc07ba9..1fd1fee377 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java @@ -31,19 +31,16 @@ import javax.jms.*; * 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. * - *

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): + *

One use of this is 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 conversation + * 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): * *

- * MessageListener conversation = new ConversationHelper(java.util.concurrent.LinkedBlockingQueue.class),
- *                                                       sendDesitination, replyDestination);
+ * ConversationHelper conversation = new ConversationHelper(connection, sendDesitination, replyDestination,
+ *                                                          java.util.concurrent.LinkedBlockingQueue.class);
  *
  * initiateConversation()
  * {
@@ -79,9 +76,9 @@ import javax.jms.*;
  *
  * 

*
CRC Card
Responsibilities Collaborations - *
Associate messages to a conversation using correlation ids. + *
Associate messages to an ongoing conversation using correlation ids. *
Auto manage sessions for conversations. - *
Store messages not in conversation in dead letter box. + *
Store messages not in a conversation in dead letter box. *
* * @todo Non-transactional, can use shared session. Transactional, must have session per-thread. Session pool? In @@ -91,9 +88,9 @@ import javax.jms.*; * to restrict receives on that session to prevent it picking up messages bound for other conversations. Or use * a temporary response queue, with only that session listening to it. * - * @todo Want something convenient that hides many details. Write out some example use cases to get the best feel for + * @todo Want something convenient that hides 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 + * 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). * @@ -139,7 +136,7 @@ public class ConversationHelper * @throws JMSException All undelying JMSExceptions are allowed to fall through. */ public ConversationHelper(Connection connection, Destination sendDestination, Destination receiveDestination, - Class queueClass) throws JMSException + Class queueClass) throws JMSException { session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); producer = session.createProducer(sendDestination); -- cgit v1.2.1 From eaf3e6ce46ac764666dc030b17beed644e4fb44c Mon Sep 17 00:00:00 2001 From: Robert Greig Date: Tue, 8 May 2007 12:04:37 +0000 Subject: Merged revisions 534903-535308,535310-535808,535810-536007,536009-536140,536142-536164 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/M2 ........ r534903 | rgreig | 2007-05-03 16:09:18 +0100 (Thu, 03 May 2007) | 1 line More interop test stuff. ........ r535254 | rgreig | 2007-05-04 15:27:53 +0100 (Fri, 04 May 2007) | 1 line First two test cases completed. Still to do pub/sub. ........ r535874 | rgreig | 2007-05-07 14:13:06 +0100 (Mon, 07 May 2007) | 1 line Added remaining test case. ........ r536163 | rgreig | 2007-05-08 12:21:35 +0100 (Tue, 08 May 2007) | 1 line Added XML logging of test results. ........ r536164 | rgreig | 2007-05-08 12:39:51 +0100 (Tue, 08 May 2007) | 1 line Added inclusion of sender and receiver names in results. ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@536167 13f79535-47bb-0310-9956-ffa450edef68 --- .../integrationtests/jar-with-dependencies.xml | 29 ++ qpid/java/integrationtests/pom.xml | 13 + .../interop/coordinator/CoordinatingTestCase.java | 125 +++++- .../qpid/interop/coordinator/Coordinator.java | 136 +++++- .../interop/coordinator/InvitingTestDecorator.java | 43 +- .../qpid/interop/coordinator/OptOutTestCase.java | 13 + .../interop/coordinator/TestClientDetails.java | 2 +- .../qpid/interop/coordinator/XMLTestListener.java | 381 ++++++++++++++++ .../testcases/CoordinatingTestCase1DummyRun.java | 64 +++ .../testcases/CoordinatingTestCase2BasicP2P.java | 31 +- .../CoordinatingTestCase3BasicPubSub.java | 71 +++ .../interop/testclient/InteropClientTestCase.java | 4 +- .../apache/qpid/interop/testclient/TestClient.java | 105 +++-- .../testclient/testcases/TestCase1DummyRun.java | 2 + .../testclient/testcases/TestCase2BasicP2P.java | 94 +++- .../testclient/testcases/TestCase3BasicPubSub.java | 224 ++++++++++ .../org/apache/qpid/util/ConversationFactory.java | 479 +++++++++++++++++++++ .../org/apache/qpid/util/ConversationHelper.java | 306 ------------- 18 files changed, 1722 insertions(+), 400 deletions(-) create mode 100644 qpid/java/integrationtests/jar-with-dependencies.xml create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/XMLTestListener.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase1DummyRun.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase3BasicPubSub.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationFactory.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/jar-with-dependencies.xml b/qpid/java/integrationtests/jar-with-dependencies.xml new file mode 100644 index 0000000000..62978ee864 --- /dev/null +++ b/qpid/java/integrationtests/jar-with-dependencies.xml @@ -0,0 +1,29 @@ + + + all-test-deps + + jar + + false + + + + + true + test + + + + + target/classes + + + + target/test-classes + + + + diff --git a/qpid/java/integrationtests/pom.xml b/qpid/java/integrationtests/pom.xml index fd7ac8b039..3afdf48204 100644 --- a/qpid/java/integrationtests/pom.xml +++ b/qpid/java/integrationtests/pom.xml @@ -103,6 +103,19 @@ + + + org.apache.maven.plugins + maven-assembly-plugin + 2.2-SNAPSHOT + + + jar-with-dependencies.xml + + target + target/assembly/work + + diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java index 36c270ef11..ef69d14be8 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java @@ -22,14 +22,17 @@ package org.apache.qpid.interop.coordinator; import java.util.Collection; +import java.util.Map; import java.util.Properties; +import java.util.concurrent.LinkedBlockingQueue; -import javax.jms.JMSException; -import javax.jms.Message; +import javax.jms.*; import junit.framework.TestCase; -import org.apache.qpid.util.ConversationHelper; +import org.apache.log4j.Logger; + +import org.apache.qpid.util.ConversationFactory; /** * An CoordinatingTestCase is a JUnit test case extension that knows how to coordinate test clients that take part in a @@ -60,19 +63,24 @@ import org.apache.qpid.util.ConversationHelper; *

*
CRC Card
Responsibilities Collaborations *
Accept notification of test case participants. {@link InvitingTestDecorator} - *
Coordinate the test sequence amongst participants. {@link ConversationHelper} + *
Accpet JMS Connection to carry out the coordination over. + *
Coordinate the test sequence amongst participants. {@link ConversationFactory} *
Supply test properties *
*/ public abstract class CoordinatingTestCase extends TestCase { + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(CoordinatingTestCase.class); + /** Holds the contact details for the sending test client. */ TestClientDetails sender; /** Holds the contact details for the receving test client. */ TestClientDetails receiver; - ConversationHelper conversation; + /** Holds the conversation factory over which to coordinate the test. */ + ConversationFactory conversationFactory; /** * Creates a new coordinating test case with the specified name. @@ -91,6 +99,8 @@ public abstract class CoordinatingTestCase extends TestCase */ public void setSender(TestClientDetails sender) { + log.debug("public void setSender(TestClientDetails sender = " + sender + "): called"); + this.sender = sender; } @@ -101,6 +111,8 @@ public abstract class CoordinatingTestCase extends TestCase */ public void setReceiver(TestClientDetails receiver) { + log.debug("public void setReceiver(TestClientDetails receiver = " + receiver + "): called"); + this.receiver = receiver; } @@ -124,6 +136,46 @@ public abstract class CoordinatingTestCase extends TestCase return receiver; } + /** + * Returns the name of the current test method of this test class, with the sending and receiving client names + * appended on to it, so that the resulting name unqiuely identifies the test and the clients that participated + * in it. + * + * @return The unique test and client name. + */ + public String getName() + { + if ((sender == null) || (receiver == null)) + { + return super.getName(); + } + else + { + return super.getName() + "_sender_" + sender.clientName + "_receiver_" + receiver.clientName; + } + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public abstract String getTestCaseNameForTestMethod(String methodName); + + /** + * Accepts the conversation factory over which to hold the test coordinating conversation. + * + * @param conversationFactory The conversation factory to coordinate the test over. + */ + public void setConversationFactory(ConversationFactory conversationFactory) + { + this.conversationFactory = conversationFactory; + } + /** * 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 assigning the test roles, begining the test and gathering the test reports @@ -135,44 +187,81 @@ public abstract class CoordinatingTestCase extends TestCase * * @throws JMSException All underlying JMSExceptions are allowed to fall through. */ - protected Message[] sequenceTest(Properties testProperties) throws JMSException + protected Message[] sequenceTest(Map testProperties) throws JMSException { + log.debug("protected Message[] sequenceTest(Object... testProperties = " + testProperties + "): called"); + + Session session = conversationFactory.getSession(); + Destination senderControlTopic = session.createTopic(sender.privateControlKey); + Destination receiverControlTopic = session.createTopic(receiver.privateControlKey); + + ConversationFactory.Conversation senderConversation = conversationFactory.startConversation(); + ConversationFactory.Conversation receiverConversation = conversationFactory.startConversation(); + // Assign the sender role to the sending test client. - Message assignSender = conversation.getSession().createMessage(); + Message assignSender = conversationFactory.getSession().createMessage(); + setPropertiesOnMessage(assignSender, testProperties); assignSender.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); assignSender.setStringProperty("ROLE", "SENDER"); - conversation.send(assignSender); + senderConversation.send(senderControlTopic, assignSender); // Assign the receiver role the receiving client. - Message assignReceiver = conversation.getSession().createMessage(); + Message assignReceiver = session.createMessage(); + setPropertiesOnMessage(assignReceiver, testProperties); assignReceiver.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); assignReceiver.setStringProperty("ROLE", "RECEIVER"); - conversation.send(assignReceiver); + receiverConversation.send(receiverControlTopic, assignReceiver); // Wait for the senders and receivers to confirm their roles. - conversation.receive(); - conversation.receive(); + senderConversation.receive(); + receiverConversation.receive(); // Start the test. - Message start = conversation.getSession().createMessage(); + Message start = session.createMessage(); start.setStringProperty("CONTROL_TYPE", "START"); - conversation.send(start); + senderConversation.send(senderControlTopic, start); // Wait for the test sender to return its report. - Message senderReport = conversation.receive(); + Message senderReport = senderConversation.receive(); + + try + { + Thread.sleep(500); + } + catch (InterruptedException e) + { } // Ask the receiver for its report. - Message statusRequest = conversation.getSession().createMessage(); + Message statusRequest = session.createMessage(); statusRequest.setStringProperty("CONTROL_TYPE", "STATUS_REQUEST"); - conversation.send(statusRequest); + receiverConversation.send(receiverControlTopic, statusRequest); // Wait for the receiver to send its report. - Message receiverReport = conversation.receive(); + Message receiverReport = receiverConversation.receive(); return new Message[] { senderReport, receiverReport }; } + + /** + * Sets properties of different types on a JMS Message. + * + * @param message The message to set properties on. + * @param properties The property name/value pairs to set. + * + * @throws JMSException All underlying JMSExceptions are allowed to fall through. + */ + public void setPropertiesOnMessage(Message message, Map properties) throws JMSException + { + for (Map.Entry entry : properties.entrySet()) + { + String name = entry.getKey(); + Object value = entry.getValue(); + + message.setObjectProperty(name, value); + } + } } diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java index 5e0f5b4941..de5faeac0f 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.interop.coordinator; +import java.io.*; import java.util.*; import java.util.concurrent.LinkedBlockingQueue; @@ -31,18 +32,18 @@ import junit.framework.TestSuite; import org.apache.log4j.Logger; +import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase1DummyRun; import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase2BasicP2P; -import org.apache.qpid.interop.testclient.InteropClientTestCase; +import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase3BasicPubSub; import org.apache.qpid.interop.testclient.TestClient; -import org.apache.qpid.interop.testclient.testcases.TestCase1DummyRun; -import org.apache.qpid.interop.testclient.testcases.TestCase2BasicP2P; -import org.apache.qpid.util.ClasspathScanner; import org.apache.qpid.util.CommandLineParser; -import org.apache.qpid.util.ConversationHelper; +import org.apache.qpid.util.ConversationFactory; import org.apache.qpid.util.PrettyPrintingUtils; -import uk.co.thebadgerset.junit.extensions.TestRunnerImprovedErrorHandling; +import uk.co.thebadgerset.junit.extensions.TKTestResult; +import uk.co.thebadgerset.junit.extensions.TKTestRunner; import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; +import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; /** *

Implements the coordinator client described in the interop testing specification @@ -51,13 +52,13 @@ import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; * *

*
CRC Card
Responsibilities Collaborations - *
Find out what test clients are available. {@link ConversationHelper} + *
Find out what test clients are available. {@link ConversationFactory} *
Decorate available tests to run all available clients. {@link InvitingTestDecorator} *
Attach XML test result logger. *
Terminate the interop testing framework. *
*/ -public class Coordinator extends TestRunnerImprovedErrorHandling +public class Coordinator extends TKTestRunner { private static final Logger log = Logger.getLogger(Coordinator.class); @@ -73,9 +74,20 @@ public class Coordinator extends TestRunnerImprovedErrorHandling Set enlistedClients = new HashSet(); /** Holds the conversation helper for the control conversation. */ - private ConversationHelper conversation; + private ConversationFactory conversationFactory; + + /** Holds the connection that the coordinating messages are sent over. */ private Connection connection; + /** + * Holds the name of the class of the test currently being run. Ideally passed into the {@link #createTestResult} + * method, but as the signature is already fixed for this, the current value gets pushed here as a member variable. + */ + private String currentTestClassName; + + /** Holds the path of the directory to output test results too, if one is defined. */ + private static String reportDir; + /** * Creates an interop test coordinator on the specified broker and virtual host. * @@ -114,19 +126,26 @@ public class Coordinator extends TestRunnerImprovedErrorHandling new String[][] { { "b", "The broker URL.", "broker", "false" }, - { "h", "The virtual host to use.", "virtual host", "false" } + { "h", "The virtual host to use.", "virtual host", "false" }, + { "o", "The name of the directory to output test timings to.", "dir", "false" } })); // Extract the command line options. String brokerUrl = options.getProperty("b"); String virtualHost = options.getProperty("h"); + reportDir = options.getProperty("o"); // Scan for available test cases using a classpath scanner. Collection> testCaseClasses = new ArrayList>(); // ClasspathScanner.getMatches(CoordinatingTestCase.class, "^Test.*", true); // Hard code the test classes till the classpath scanner is fixed. - Collections.addAll(testCaseClasses, new Class[] { CoordinatingTestCase2BasicP2P.class }); + Collections.addAll(testCaseClasses, + new Class[] + { + CoordinatingTestCase1DummyRun.class, CoordinatingTestCase2BasicP2P.class, + CoordinatingTestCase3BasicPubSub.class + }); // Check that some test classes were actually found. if ((testCaseClasses == null) || testCaseClasses.isEmpty()) @@ -145,9 +164,12 @@ public class Coordinator extends TestRunnerImprovedErrorHandling // Create a coordinator and begin its test procedure. Coordinator coordinator = new Coordinator(brokerUrl, virtualHost); + + boolean failure = false; + TestResult testResult = coordinator.start(testClassNames); - if (!testResult.wasSuccessful()) + if (failure) { System.exit(FAILURE_EXIT); } @@ -176,7 +198,7 @@ public class Coordinator extends TestRunnerImprovedErrorHandling public TestResult start(String[] testClassNames) throws Exception { log.debug("public TestResult start(String[] testClassNames = " + PrettyPrintingUtils.printArray(testClassNames) - + "): called"); + + ": called"); // Connect to the broker. connection = TestClient.createConnection(DEFAULT_CONNECTION_PROPS_RESOURCE, brokerUrl, virtualHost); @@ -185,28 +207,39 @@ public class Coordinator extends TestRunnerImprovedErrorHandling Destination controlTopic = session.createTopic("iop.control"); Destination responseQueue = session.createQueue("coordinator"); - conversation = new ConversationHelper(connection, controlTopic, responseQueue, LinkedBlockingQueue.class); + conversationFactory = new ConversationFactory(connection, responseQueue, LinkedBlockingQueue.class); + ConversationFactory.Conversation conversation = conversationFactory.startConversation(); + + connection.start(); // Broadcast the compulsory invitation to find out what clients are available to test. Message invite = session.createMessage(); invite.setStringProperty("CONTROL_TYPE", "INVITE"); invite.setJMSReplyTo(responseQueue); - conversation.send(invite); + conversation.send(controlTopic, invite); // Wait for a short time, to give test clients an opportunity to reply to the invitation. - Collection enlists = conversation.receiveAll(0, 10000); + Collection enlists = conversation.receiveAll(0, 3000); enlistedClients = extractEnlists(enlists); - // Run all of the tests in the suite using JUnit. - TestResult result = super.start(testClassNames); + // Run the test in the suite using JUnit. + TestResult result = null; + + for (String testClassName : testClassNames) + { + // Record the current test class, so that the test results can be output to a file incorporating this name. + this.currentTestClassName = testClassName; + + result = super.start(new String[] { testClassName }); + } // At this point in time, all tests have completed. Broadcast the shutdown message. Message terminate = session.createMessage(); terminate.setStringProperty("CONTROL_TYPE", "TERMINATE"); - conversation.send(terminate); + conversation.send(controlTopic, terminate); return result; } @@ -283,8 +316,69 @@ public class Coordinator extends TestRunnerImprovedErrorHandling } // Wrap the tests in an inviting test decorator, to perform the invite/test cycle. - targetTest = new InvitingTestDecorator(targetTest, enlistedClients, conversation); + targetTest = new InvitingTestDecorator(targetTest, enlistedClients, conversationFactory, connection); + + TestSuite suite = new TestSuite(); + suite.addTest(targetTest); + + // Wrap the tests in a scaled test decorator to them them as a 'batch' in one thread. + // targetTest = new ScaledTestDecorator(targetTest, new int[] { 1 }); - return super.doRun(targetTest, wait); + return super.doRun(suite, wait); + } + + /** + * Creates the TestResult object to be used for test runs. + * + * @return An instance of the test result object. + */ + protected TestResult createTestResult() + { + log.debug("protected TestResult createTestResult(): called"); + + TKTestResult result = new TKTestResult(fPrinter.getWriter(), delay, verbose, testCaseName); + + // Check if a directory to output reports to has been specified and attach test listeners if so. + if (reportDir != null) + { + // Create the report directory if it does not already exist. + File reportDirFile = new File(reportDir); + + if (!reportDirFile.exists()) + { + reportDirFile.mkdir(); + } + + // Create the timings file (make the name of this configurable as a command line parameter). + Writer timingsWriter = null; + + try + { + File timingsFile = new File(reportDirFile, "TEST." + currentTestClassName + ".xml"); + timingsWriter = new BufferedWriter(new FileWriter(timingsFile), 20000); + } + catch (IOException e) + { + throw new RuntimeException("Unable to create the log file to write test results to: " + e, e); + } + + // Set up a CSV results listener to output the timings to the results file. + XMLTestListener listener = new XMLTestListener(timingsWriter, currentTestClassName); + result.addListener(listener); + result.addTKTestListener(listener); + + // Register the results listeners shutdown hook to flush its data if the test framework is shutdown + // prematurely. + // registerShutdownHook(listener); + + // Record the start time of the batch. + // result.notifyStartBatch(); + + // At this point in time the test class has been instantiated, giving it an opportunity to read its parameters. + // Inform any test listers of the test properties. + result.notifyTestProperties(TestContextProperties.getAccessedProps()); + } + + return result; } } diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java index 2975082631..858ed1a589 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java @@ -23,6 +23,8 @@ package org.apache.qpid.interop.coordinator; import java.util.*; import java.util.concurrent.LinkedBlockingQueue; +import javax.jms.Connection; +import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.Message; @@ -32,14 +34,14 @@ import junit.framework.TestSuite; import org.apache.log4j.Logger; -import org.apache.qpid.util.ConversationHelper; +import org.apache.qpid.util.ConversationFactory; import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; /** *

*
CRC Card
Responsibilities Collaborations - *
Broadcast test invitations and collect enlists. {@link ConversationHelper}. + *
Broadcast test invitations and collect enlists. {@link ConversationFactory}. *
Output test failures for clients unwilling to run the test case. {@link Coordinator} *
Execute coordinated test cases. {@link CoordinatingTestCase} *
@@ -52,7 +54,10 @@ public class InvitingTestDecorator extends WrappedSuiteTestDecorator Set allClients; /** Holds the conversation helper for the control level conversation for coordinating the test through. */ - ConversationHelper conversation; + ConversationFactory conversationFactory; + + /** Holds the connection that the control conversation is held over. */ + Connection connection; /** Holds the underlying {@link CoordinatingTestCase}s that this decorator wraps. */ WrappedSuiteTestDecorator testSuite; @@ -61,11 +66,12 @@ public class InvitingTestDecorator extends WrappedSuiteTestDecorator * Creates a wrapped suite test decorator from another one. * * @param suite The test suite. - * @param availableClients The list of all clients that responded to the compulsory invite. + * @param availableClients The list of all clients that responded to the compulsory invite. * @param controlConversation The conversation helper for the control level, test coordination conversation. + * @param controlConnection The connection that the coordination messages are sent over. */ public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set availableClients, - ConversationHelper controlConversation) + ConversationFactory controlConversation, Connection controlConnection) { super(suite); @@ -74,7 +80,8 @@ public class InvitingTestDecorator extends WrappedSuiteTestDecorator testSuite = suite; allClients = availableClients; - conversation = controlConversation; + conversationFactory = controlConversation; + connection = controlConnection; } /** @@ -103,14 +110,17 @@ public class InvitingTestDecorator extends WrappedSuiteTestDecorator Set enlists = null; try { - Message invite = conversation.getSession().createMessage(); + Message invite = conversationFactory.getSession().createMessage(); + Destination controlTopic = conversationFactory.getSession().createTopic("iop.control"); + ConversationFactory.Conversation conversation = conversationFactory.startConversation(); + invite.setStringProperty("CONTROL_TYPE", "INVITE"); - invite.setStringProperty("TEST_NAME", coordTest.getName()); + invite.setStringProperty("TEST_NAME", coordTest.getTestCaseNameForTestMethod(coordTest.getName())); - conversation.send(invite); + conversation.send(controlTopic, invite); // Wait for a short time, to give test clients an opportunity to reply to the invitation. - Collection replies = conversation.receiveAll(allClients.size(), 10000); + Collection replies = conversation.receiveAll(allClients.size(), 3000); enlists = Coordinator.extractEnlists(replies); } catch (JMSException e) @@ -143,12 +153,25 @@ public class InvitingTestDecorator extends WrappedSuiteTestDecorator coordTest.setSender(enlistedPair.get(0)); coordTest.setReceiver(enlistedPair.get(1)); + // Pass down the connection to hold the coordination conversation over. + coordTest.setConversationFactory(conversationFactory); + // Execute the test case. coordTest.run(testResult); } } } + /** + * Prints a string summarizing this test decorator, mainly for debugging purposes. + * + * @return String representation for debugging purposes. + */ + public String toString() + { + return "InvitingTestDecorator: [ testSuite = " + testSuite + " ]"; + } + /** * Produces all pairs of combinations of elements from two sets. The ordering of the elements in the pair is * important, that is the pair is distinct from ; both pairs are generated. For any element, i, in diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java index 27e2c42a9a..42a382a898 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java @@ -49,4 +49,17 @@ public class OptOutTestCase extends CoordinatingTestCase { Assert.fail("One of " + getSender() + " and " + getReceiver() + " opted out of the test."); } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "OptOutTest"; + } } diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java index 80abd4d4c9..c4a9d39cd8 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java @@ -82,6 +82,6 @@ public class TestClientDetails */ public String toString() { - return "clientName = " + clientName + ", privateControlKey = " + privateControlKey; + return "TestClientDetails: [ clientName = " + clientName + ", privateControlKey = " + privateControlKey + " ]"; } } diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/XMLTestListener.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/XMLTestListener.java new file mode 100644 index 0000000000..6c7a2a5d52 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/XMLTestListener.java @@ -0,0 +1,381 @@ +package org.apache.qpid.interop.coordinator; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; +import java.util.*; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestCase; + +import org.apache.log4j.Logger; + +import uk.co.thebadgerset.junit.extensions.listeners.TKTestListener; + +/** + * Listens for test results for a named test and outputs these in the standard JUnit XML format to the specified + * writer. + * + *

The API for this listener accepts notifications about different aspects of a tests results through different + * methods, so some assumption needs to be made as to which test result a notification refers to. For example + * {@link #startTest} will be called, then possibly {@link #timing} will be called, even though the test instance is + * passed in both cases, it is not enough to distinguish a particular run of the test, as the test case instance may + * be being shared between multiple threads, or being run a repeated number of times, and can therfore be re-used + * between calls. The listeners make the assumption that, for every test, a unique thread will call {@link #startTest} + * and {@link #endTest} to delimit each test. All calls to set test parameters, timings, state and so on, will occur + * between the start and end and will be given with the same thread id as the start and end, so the thread id provides + * a unqiue value to identify a particular test run against. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
+ * + * @todo Merge this class with CSV test listener, making the collection of results common to both, and only factoring + * out the results printing code into sub-classes. Provide a simple XML results formatter with the same format as + * the ant XML formatter, and a more structured one for outputing results with timings and summaries from + * performance tests. + */ +public class XMLTestListener implements TKTestListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(XMLTestListener.class); + + /** The results file writer. */ + protected Writer writer; + + /** Holds the results for individual tests. */ + // protected Map results = new LinkedHashMap(); + // protected List results = new ArrayList(); + + /** + * Map for holding results on a per thread basis as they come in. A ThreadLocal is not used as sometimes an + * explicit thread id must be used, where notifications come from different threads than the ones that called + * the test method. + */ + Map threadLocalResults = Collections.synchronizedMap(new LinkedHashMap()); + + /** + * Holds results for tests that have ended. Transferring these results here from the per-thread results map, means + * that the thread id is freed for the thread to generate more results. + */ + List results = new ArrayList(); + + /** Holds the overall error count. */ + protected int errors = 0; + + /** Holds the overall failure count. */ + protected int failures = 0; + + /** Holds the overall tests run count. */ + protected int runs = 0; + + /** Holds the name of the class that tests are being run for. */ + String testClassName; + + /** + * Creates a new XML results output listener that writes to the specified location. + * + * @param writer The location to write results to. + */ + public XMLTestListener(Writer writer, String testClassName) + { + log.debug("public XMLTestListener(Writer writer, String testClassName = " + testClassName + "): called"); + + this.writer = writer; + this.testClassName = testClassName; + } + + /** + * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed. + * + * @param test The test to resest any results for. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void reset(Test test, Long threadId) + { + log.debug("public void reset(Test test = " + test + ", Long threadId = " + threadId + "): called"); + + XMLTestListener.Result r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + r.error = null; + r.failure = null; + + } + + /** + * A test started. + */ + public void startTest(Test test) + { + log.debug("public void startTest(Test test = " + test + "): called"); + + Result newResult = new Result(test.getClass().getName(), ((TestCase) test).getName()); + + // Initialize the thread local test results. + threadLocalResults.put(Thread.currentThread().getId(), newResult); + runs++; + } + + /** + * Should be called every time a test completes with the run time of that test. + * + * @param test The name of the test. + * @param nanos The run time of the test in nanoseconds. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void timing(Test test, long nanos, Long threadId) + { } + + /** + * Should be called every time a test completed with the amount of memory used before and after the test was run. + * + * @param test The test which memory was measured for. + * @param memStart The total JVM memory used before the test was run. + * @param memEnd The total JVM memory used after the test was run. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void memoryUsed(Test test, long memStart, long memEnd, Long threadId) + { } + + /** + * Should be called every time a parameterized test completed with the int value of its test parameter. + * + * @param test The test which memory was measured for. + * @param parameter The int parameter value. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void parameterValue(Test test, int parameter, Long threadId) + { } + + /** + * Should be called every time a test completes with the current number of test threads running. + * + * @param test The test for which the measurement is being generated. + * @param threads The number of tests being run concurrently. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void concurrencyLevel(Test test, int threads, Long threadId) + { } + + /** + * Notifies listeners of the tests read/set properties. + * + * @param properties The tests read/set properties. + */ + public void properties(Properties properties) + { } + + /** + * A test ended. + */ + public void endTest(Test test) + { + log.debug("public void endTest(Test test = " + test + "): called"); + + // Move complete test results into the completed tests list. + Result r = threadLocalResults.get(Thread.currentThread().getId()); + results.add(r); + + // Clear all the test results for the thread. + threadLocalResults.remove(Thread.currentThread().getId()); + } + + /** + * Called when a test completes. Success, failure and errors. This method should be used when registering an + * end test from a different thread than the one that started the test. + * + * @param test The test which completed. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void endTest(Test test, Long threadId) + { + log.debug("public void endTest(Test test = " + test + ", Long threadId = " + threadId + "): called"); + + // Move complete test results into the completed tests list. + Result r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + results.add(r); + + // Clear all the test results for the thread. + threadLocalResults.remove(Thread.currentThread().getId()); + } + + /** + * An error occurred. + */ + public void addError(Test test, Throwable t) + { + log.debug("public void addError(Test test = " + test + ", Throwable t = " + t + "): called"); + + Result r = threadLocalResults.get(Thread.currentThread().getId()); + r.error = t; + errors++; + } + + /** + * A failure occurred. + */ + public void addFailure(Test test, AssertionFailedError t) + { + log.debug("public void addFailure(Test test = " + test + ", AssertionFailedError t = " + t + "): called"); + + Result r = threadLocalResults.get(Thread.currentThread().getId()); + r.failure = t; + failures++; + } + + /** + * Called when a test completes to mark it as a test fail. This method should be used when registering a + * failure from a different thread than the one that started the test. + * + * @param test The test which failed. + * @param e The assertion that failed the test. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void addFailure(Test test, AssertionFailedError e, Long threadId) + { + log.debug("public void addFailure(Test test, AssertionFailedError e, Long threadId): called"); + + Result r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + r.failure = e; + failures++; + } + + /** + * Notifies listeners of the start of a complete run of tests. + */ + public void startBatch() + { + log.debug("public void startBatch(): called"); + + // Reset all results counts. + threadLocalResults = Collections.synchronizedMap(new HashMap()); + errors = 0; + failures = 0; + runs = 0; + + // Write out the file header. + try + { + writer.write("\n"); + } + catch (IOException e) + { + throw new RuntimeException("Unable to write the test results.", e); + } + } + + /** + * Notifies listeners of the end of a complete run of tests. + * + * @param parameters The optional test parameters to log out with the batch results. + */ + public void endBatch(Properties parameters) + { + log.debug("public void endBatch(Properties parameters = " + parameters + "): called"); + + // Write out the results. + try + { + // writer.write("\n"); + writer.write("\n"); + + for (Result result : results) + { + writer.write(" \n"); + + if (result.error != null) + { + writer.write(" "); + result.error.printStackTrace(new PrintWriter(writer)); + writer.write(" "); + } + else if (result.failure != null) + { + writer.write(" "); + result.failure.printStackTrace(new PrintWriter(writer)); + writer.write(" "); + } + + writer.write(" \n"); + } + + writer.write("\n"); + writer.flush(); + } + catch (IOException e) + { + throw new RuntimeException("Unable to write the test results.", e); + } + } + + /** + * Used to capture the results of a particular test run. + */ + protected static class Result + { + public Result(String testClass, String testName) + { + this.testClass = testClass; + this.testName = testName; + } + + public String testClass; + public String testName; + + /** Holds the exception that caused error in this test. */ + public Throwable error; + + /** Holds the assertion exception that caused failure in this test. */ + public AssertionFailedError failure; + + /** Holds the error count for this test. */ + // public int errors = 0; + + /** Holds the failure count for this tests. */ + // public int failures = 0; + + /** Holds the overall tests run count for this test. */ + // public int runs = 0; + + /*public boolean equals(Object o) + { + if (this == o) + { + return true; + } + + if (!(o instanceof Result)) + { + return false; + } + + final Result result = (Result) o; + + if ((testClass != null) ? (!testClass.equals(result.testClass)) : (result.testClass != null)) + { + return false; + } + + if ((testName != null) ? (!testName.equals(result.testName)) : (result.testName != null)) + { + return false; + } + + return true; + } + + public int hashCode() + { + int result; + result = ((testClass != null) ? testClass.hashCode() : 0); + result = (29 * result) + ((testName != null) ? testName.hashCode() : 0); + + return result; + }*/ + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase1DummyRun.java new file mode 100644 index 0000000000..e20f702dd1 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase1DummyRun.java @@ -0,0 +1,64 @@ +package org.apache.qpid.interop.coordinator.testcases; + +import java.util.HashMap; +import java.util.Map; + +import javax.jms.Message; + +import junit.framework.Assert; + +import org.apache.log4j.Logger; + +import org.apache.qpid.interop.coordinator.CoordinatingTestCase; + +/** + *

+ *
CRC Card
Responsibilities Collaborations + *
Exercises the interop testing framework without actually sending any test messages. + * {@link org.apache.qpid.interop.coordinator.CoordinatingTestCase} + *
+ */ +public class CoordinatingTestCase1DummyRun extends CoordinatingTestCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(CoordinatingTestCase1DummyRun.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public CoordinatingTestCase1DummyRun(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + */ + public void testDummyRun() throws Exception + { + log.debug("public void testDummyRun(): called"); + + Map testConfig = new HashMap(); + testConfig.put("TEST_NAME", "TC1_DummyRun"); + + Message[] reports = sequenceTest(testConfig); + + // Compare sender and receiver reports. + Assert.assertEquals("Expected to get 2 dummy reports.", 2, reports.length); + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC1_DummyRun"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java index 4fb9729c4a..d66d42dd51 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java @@ -1,11 +1,14 @@ package org.apache.qpid.interop.coordinator.testcases; -import java.util.Properties; +import java.util.HashMap; +import java.util.Map; import javax.jms.Message; import junit.framework.Assert; +import org.apache.log4j.Logger; + import org.apache.qpid.interop.coordinator.CoordinatingTestCase; /** @@ -16,6 +19,9 @@ import org.apache.qpid.interop.coordinator.CoordinatingTestCase; */ public class CoordinatingTestCase2BasicP2P extends CoordinatingTestCase { + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(CoordinatingTestCase2BasicP2P.class); + /** * Creates a new coordinating test case with the specified name. * @@ -31,10 +37,12 @@ public class CoordinatingTestCase2BasicP2P extends CoordinatingTestCase */ public void testBasicP2P() throws Exception { - Properties testConfig = new Properties(); - testConfig.setProperty("TEST_CASE", "TC2_BasicP2P"); - testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); - testConfig.setProperty("P2P_NUM_MESSAGES", "50"); + log.debug("public void testBasicP2P(): called"); + + Map testConfig = new HashMap(); + testConfig.put("TEST_NAME", "TC2_BasicP2P"); + testConfig.put("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); + testConfig.put("P2P_NUM_MESSAGES", 50); Message[] reports = sequenceTest(testConfig); @@ -45,4 +53,17 @@ public class CoordinatingTestCase2BasicP2P extends CoordinatingTestCase Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); Assert.assertEquals("Sender and receiver messages sent did not match up.", messagesSent, messagesReceived); } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC2_BasicP2P"; + } } diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase3BasicPubSub.java new file mode 100644 index 0000000000..ce46bb87ba --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase3BasicPubSub.java @@ -0,0 +1,71 @@ +package org.apache.qpid.interop.coordinator.testcases; + +import java.util.HashMap; +import java.util.Map; + +import javax.jms.Message; + +import junit.framework.Assert; + +import org.apache.log4j.Logger; + +import org.apache.qpid.interop.coordinator.CoordinatingTestCase; + +/** + *

+ *
CRC Card
Responsibilities Collaborations + *
Setup pub/sub test parameters and compare with test output. {@link CoordinatingTestCase} + *
+ */ +public class CoordinatingTestCase3BasicPubSub extends CoordinatingTestCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(CoordinatingTestCase3BasicPubSub.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public CoordinatingTestCase3BasicPubSub(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + */ + public void testBasicPubSub() throws Exception + { + log.debug("public void testBasicPubSub(): called"); + + Map testConfig = new HashMap(); + testConfig.put("TEST_NAME", "TC3_BasicPubSub"); + testConfig.put("PUBSUB_KEY", "tc3route"); + testConfig.put("PUBSUB_NUM_MESSAGES", 10); + testConfig.put("PUBSUB_NUM_RECEIVERS", 5); + + Message[] reports = sequenceTest(testConfig); + + // Compare sender and receiver reports. + int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 10, messagesSent); + Assert.assertEquals("Received messages did not match up to num sent * num receivers.", messagesSent * 5, + messagesReceived); + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC3_BasicPubSub"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java index 57726285f9..9f769822de 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java @@ -79,8 +79,10 @@ public interface InteropClientTestCase extends MessageListener /** * Performs the test case actions. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. */ - public void start(); + public void start() throws JMSException; /** * Gets a report on the actions performed by the test case in its assigned role. diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java index 6d7ab9c978..0b9c72e1b6 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java @@ -21,10 +21,7 @@ package org.apache.qpid.interop.testclient; import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; +import java.util.*; import javax.jms.*; import javax.naming.Context; @@ -33,6 +30,9 @@ import javax.naming.NamingException; import org.apache.log4j.Logger; +import org.apache.qpid.interop.testclient.testcases.TestCase1DummyRun; +import org.apache.qpid.interop.testclient.testcases.TestCase2BasicP2P; +import org.apache.qpid.interop.testclient.testcases.TestCase3BasicPubSub; import org.apache.qpid.util.ClasspathScanner; import org.apache.qpid.util.CommandLineParser; import org.apache.qpid.util.PropertiesUtils; @@ -61,41 +61,53 @@ public class TestClient implements MessageListener { private static Logger log = Logger.getLogger(TestClient.class); + public static final String CONNECTION_PROPERTY = "connectionfactory.broker"; + public static final String CONNECTION_NAME = "broker"; + public static final String CLIENT_NAME = "java"; + public static final String DEFAULT_CONNECTION_PROPS_RESOURCE = "org/apache/qpid/interop/connection.properties"; + /** Holds the URL of the broker to run the tests on. */ - String brokerUrl; + public static String brokerUrl; /** Holds the virtual host to run the tests on. If null, then the default virtual host is used. */ - String virtualHost; + public static String virtualHost; /** Holds all the test cases loaded from the classpath. */ Map testCases = new HashMap(); InteropClientTestCase currentTestCase; - public static final String CONNECTION_PROPERTY = "connectionfactory.broker"; - public static final String CONNECTION_NAME = "broker"; - public static final String CLIENT_NAME = "java"; - public static final String DEFAULT_CONNECTION_PROPS_RESOURCE = "org/apache/qpid/interop/connection.properties"; - private MessageProducer producer; private Session session; - public TestClient(String brokerUrl, String virtualHost) + private String clientName = CLIENT_NAME; + + /** + * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified + * client identifying name. + * + * @param brokerUrl The url of the broker to connect to. + * @param virtualHost The virtual host to conect to. + * @param clientName The client name to use. + */ + public TestClient(String brokerUrl, String virtualHost, String clientName) { log.debug("public TestClient(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost - + "): called"); + + ", String clientName = " + clientName + "): called"); // Retain the connection parameters. this.brokerUrl = brokerUrl; this.virtualHost = virtualHost; + this.clientName = clientName; } /** * The entry point for the interop test coordinator. This client accepts the following command line arguments: * *

- *
-b The broker URL. Mandatory. - *
-h The virtual host. Optional. + *
-b The broker URL. Optional. + *
-h The virtual host. Optional. + *
-n The test client name. Optional. *
name=value Trailing argument define name/value pairs. Added to system properties. Optional. *
* @@ -105,11 +117,13 @@ public class TestClient implements MessageListener { // Use the command line parser to evaluate the command line. CommandLineParser commandLine = - new CommandLineParser(new String[][] - { - { "b", "The broker URL.", "broker", "false" }, - { "h", "The virtual host to use.", "virtual host", "false" } - }); + new CommandLineParser( + new String[][] + { + { "b", "The broker URL.", "broker", "false" }, + { "h", "The virtual host to use.", "virtual host", "false" }, + { "n", "The test client name.", "name", "false" } + }); // Capture the command line arguments or display errors and correct usage and then exit. Properties options = null; @@ -128,13 +142,14 @@ public class TestClient implements MessageListener // Extract the command line options. String brokerUrl = options.getProperty("b"); String virtualHost = options.getProperty("h"); + String clientName = options.getProperty("n"); // 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); + TestClient client = new TestClient(brokerUrl, virtualHost, (clientName == null) ? CLIENT_NAME : clientName); try { @@ -147,13 +162,22 @@ public class TestClient implements MessageListener } } + /** + * Starts the interop test client running. This causes it to start listening for incoming test invites. + * + * @throws JMSException Any underlying JMSExceptions are allowed to fall through. + */ private void start() throws JMSException { log.debug("private void start(): called"); // Use a class path scanner to find all the interop test case implementations. Collection> testCaseClasses = - ClasspathScanner.getMatches(InteropClientTestCase.class, "^TestCase.*", true); + new ArrayList>(); + // ClasspathScanner.getMatches(InteropClientTestCase.class, "^TestCase.*", true); + // Hard code the test classes till the classpath scanner is fixed. + Collections.addAll(testCaseClasses, + new Class[] { TestCase1DummyRun.class, TestCase2BasicP2P.class, TestCase3BasicPubSub.class }); // Create all the test case implementations and index them by the test names. for (Class nextClass : testCaseClasses) @@ -181,9 +205,12 @@ public class TestClient implements MessageListener session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); // Set this up to listen for control messages. - MessageConsumer consumer = session.createConsumer(session.createTopic("iop.control." + CLIENT_NAME)); + MessageConsumer consumer = session.createConsumer(session.createTopic("iop.control." + clientName)); consumer.setMessageListener(this); + MessageConsumer consumer2 = session.createConsumer(session.createTopic("iop.control")); + consumer2.setMessageListener(this); + // Create a producer to send replies with. producer = session.createProducer(null); @@ -209,13 +236,13 @@ public class TestClient implements MessageListener public static Connection createConnection(String connectionPropsResource, String brokerUrl, String virtualHost) { log.debug("public static Connection createConnection(String connectionPropsResource = " + connectionPropsResource - + ", String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost + "): called"); + + ", String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost + "): called"); try { Properties connectionProps = PropertiesUtils.getProperties(TestClient.class.getClassLoader().getResourceAsStream( - connectionPropsResource)); + connectionPropsResource)); if (brokerUrl != null) { @@ -270,6 +297,8 @@ public class TestClient implements MessageListener if (testCaseName != null) { + log.debug("Got an invite to test: " + testCaseName); + // Check if the requested test case is available. InteropClientTestCase testCase = testCases.get(testCaseName); @@ -282,6 +311,8 @@ public class TestClient implements MessageListener } else { + log.debug("Got a compulsory invite."); + enlist = true; } @@ -290,8 +321,8 @@ public class TestClient implements MessageListener // Reply with the client name in an Enlist message. Message enlistMessage = session.createMessage(); enlistMessage.setStringProperty("CONTROL_TYPE", "ENLIST"); - enlistMessage.setStringProperty("CLIENT_NAME", CLIENT_NAME); - enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + CLIENT_NAME); + enlistMessage.setStringProperty("CLIENT_NAME", clientName); + enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); producer.send(message.getJMSReplyTo(), enlistMessage); @@ -300,7 +331,10 @@ public class TestClient implements MessageListener else if ("ASSIGN_ROLE".equals(controlType)) { // Assign the role to the current test case. - String roleName = message.getStringProperty(""); + String roleName = message.getStringProperty("ROLE"); + + log.debug("Got a role assignment to role: " + roleName); + InteropClientTestCase.Roles role = Enum.valueOf(InteropClientTestCase.Roles.class, roleName); currentTestCase.assignRole(role, message); @@ -316,9 +350,15 @@ public class TestClient implements MessageListener { if ("START".equals(controlType)) { + log.debug("Got a start notification."); + // Start the current test case. currentTestCase.start(); } + else + { + log.debug("Got a status request."); + } // Generate the report from the test case and reply with it as a Report message. Message reportMessage = currentTestCase.getReport(session); @@ -327,10 +367,17 @@ public class TestClient implements MessageListener producer.send(message.getJMSReplyTo(), reportMessage); } + else if ("TERMINATE".equals(controlType)) + { + System.out.println("Received termination instruction from coordinator."); + + // Is a cleaner shutdown needed? + System.exit(0); + } else { // Log a warning about this but otherwise ignore it. - log.warn("Got an unknown control message: " + message); + log.warn("Got an unknown control message, controlType = " + controlType + ", message = " + message); } } catch (JMSException e) diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java index aa31a59b06..874f86daa9 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java @@ -84,6 +84,8 @@ public class TestCase1DummyRun implements InteropClientTestCase public void onMessage(Message message) { + log.debug("public void onMessage(Message message = " + message + "): called"); + // Ignore any messages. } } diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java index c3c05d8fd9..ea62b46451 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java @@ -20,11 +20,12 @@ */ package org.apache.qpid.interop.testclient.testcases; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.Session; +import javax.jms.*; + +import org.apache.log4j.Logger; import org.apache.qpid.interop.testclient.InteropClientTestCase; +import org.apache.qpid.interop.testclient.TestClient; /** * Implements test case 2, basic P2P. Sends/received a specified number of messages to a specified route on the @@ -41,6 +42,30 @@ import org.apache.qpid.interop.testclient.InteropClientTestCase; */ public class TestCase2BasicP2P implements InteropClientTestCase { + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase2BasicP2P.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The routing key to send them to on the default direct exchange. */ + private Destination sendDestination; + + /** The connection to send the test messages on. */ + private Connection connection; + + /** The session to send the test messages on. */ + private Session session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + /** * Should provide the name of the test case that this class implements. The exact names are defined in the * interop testing spec. @@ -49,6 +74,8 @@ public class TestCase2BasicP2P implements InteropClientTestCase */ public String getName() { + log.debug("public String getName(): called"); + return "TC2_BasicP2P"; } @@ -63,6 +90,8 @@ public class TestCase2BasicP2P implements InteropClientTestCase */ public boolean acceptInvite(Message inviteMessage) throws JMSException { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + // All invites are acceptable. return true; } @@ -79,27 +108,65 @@ public class TestCase2BasicP2P implements InteropClientTestCase */ public void assignRole(Roles role, Message assignRoleMessage) throws JMSException { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + // Take note of the role to be played. + this.role = role; + + // Create a new connection to pass the test messages on. + connection = + TestClient.createConnection(TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, TestClient.brokerUrl, + TestClient.virtualHost); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); + sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); - // Create a new connection to pass the test messages on. + log.debug("numMessages = " + numMessages); + log.debug("sendDestination = " + sendDestination); + log.debug("role = " + role); - // Check if the sender role is being assigned, and set up a message producer if so. + switch (role) { - } + // Check if the sender role is being assigned, and set up a message producer if so. + case SENDER: + producer = session.createProducer(sendDestination); + break; + // Otherwise the receiver role is being assigned, so set this up to listen for messages. - { + case RECEIVER: + MessageConsumer consumer = session.createConsumer(sendDestination); + consumer.setMessageListener(this); + break; } + + connection.start(); } /** * Performs the test case actions. */ - public void start() + public void start() throws JMSException { + log.debug("public void start(): called"); + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) { + Message testMessage = session.createTextMessage("test"); + + for (int i = 0; i < numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } } } @@ -114,11 +181,17 @@ public class TestCase2BasicP2P implements InteropClientTestCase */ public Message getReport(Session session) throws JMSException { + log.debug("public Message getReport(Session session): called"); + // Close the test connection. + connection.close(); // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); - return null; + return report; } /** @@ -128,6 +201,9 @@ public class TestCase2BasicP2P implements InteropClientTestCase */ public void onMessage(Message message) { + log.debug("public void onMessage(Message message = " + message + "): called"); + // Increment the message count. + messageCount++; } } diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java new file mode 100644 index 0000000000..2773cad3f3 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java @@ -0,0 +1,224 @@ +package org.apache.qpid.interop.testclient.testcases; + +import javax.jms.*; + +import org.apache.log4j.Logger; + +import org.apache.qpid.interop.testclient.InteropClientTestCase; +import org.apache.qpid.interop.testclient.TestClient; + +/** + * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the + * default topic exchange, using the specified number of receiver connections. Produces reports on the actual number of + * messages sent/received. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages using pub/sub. + *
Generate test reports. + *
+ */ +public class TestCase3BasicPubSub implements InteropClientTestCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase3BasicPubSub.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The number of receiver connection to use. */ + private int numReceivers; + + /** The routing key to send them to on the default direct exchange. */ + private Destination sendDestination; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC3_BasicPubSub"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // 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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); + numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); + String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); + + log.debug("numMessages = " + numMessages); + log.debug("numReceivers = " + numReceivers); + log.debug("sendKey = " + sendKey); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = + TestClient.createConnection(TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, TestClient.brokerUrl, + TestClient.virtualHost); + session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + sendDestination = session[0].createTopic(sendKey); + + producer = session[0].createProducer(sendDestination); + break; + + // Otherwise the receiver role is being assigned, so set this up to listen for messages on the required number + // of receiver connections. + case RECEIVER: + // Create the required number of receiver connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = + TestClient.createConnection(TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, TestClient.brokerUrl, + TestClient.virtualHost); + session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); + + sendDestination = session[i].createTopic(sendKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + consumer.setMessageListener(this); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (int i = 0; i < connection.length; i++) + { + connection[i].start(); + } + } + + /** + * Performs the test case actions. + */ + public void start() throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = session[0].createTextMessage("test"); + + for (int i = 0; i < numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * 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 + { + log.debug("public Message getReport(Session session): called"); + + // Close the test connections. + for (int i = 0; i < connection.length; i++) + { + connection[i].close(); + } + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationFactory.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationFactory.java new file mode 100644 index 0000000000..4ca2fe8ff5 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationFactory.java @@ -0,0 +1,479 @@ +/* + * + * 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.*; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import javax.jms.*; + +import org.apache.log4j.Logger; + +/** + * 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. + * + *

One use of this is 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 conversation + * 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): + * + *

+ * class Initiator
+ * {
+ * ConversationHelper conversation = new ConversationHelper(connection, null,
+ *                                                          java.util.concurrent.LinkedBlockingQueue.class);
+ *
+ * initiateConversation()
+ * {
+ *  try {
+ *   // Exchange greetings.
+ *   conversation.send(sendDestination, conversation.getSession().createTextMessage("Hello."));
+ *   Message greeting = conversation.receive();
+ *
+ *   // Exchange goodbyes.
+ *   conversation.send(conversation.getSession().createTextMessage("Goodbye."));
+ *   Message goodbye = conversation.receive();
+ *  } finally {
+ *   conversation.end();
+ *  }
+ * }
+ * }
+ *
+ * class Responder
+ * {
+ * ConversationHelper conversation = new ConversationHelper(connection, receiveDestination,
+ *                                                          java.util.concurrent.LinkedBlockingQueue.class);
+ *
+ * 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();
+ *  }
+ * }
+ * }
+ * 
+ * + *

Conversation correlation id's are generated on a per thread basis. + * + *

The same session is shared amongst all conversations. Calls to send are therefore synchronized because JMS + * sessions are not multi-threaded. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Associate messages to an ongoing conversation using correlation ids. + *
Auto manage sessions for conversations. + *
Store messages not in a conversation in dead letter box. + *
+ */ +public class ConversationFactory +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(ConversationFactory.class); + + /** Holds a map from correlation id's to queues. */ + private Map> idsToQueues = new HashMap>(); + + /** Holds the connection over which the conversation is conducted. */ + private Connection connection; + + /** Holds the session over which the conversation is conduxted. */ + private Session session; + + /** The message consumer for incoming messages. */ + MessageConsumer consumer; + + /** The message producer for outgoing messages. */ + MessageProducer producer; + + /** The well-known or temporary destination to receive replies on. */ + Destination receiveDestination; + + /** Holds the queue implementation class for the reply queue. */ + Class queueClass; + + /** Used to hold any replies that are received outside of the context of a conversation. */ + BlockingQueue deadLetterBox = new LinkedBlockingQueue(); + + /* Used to hold conversation state on a per thread basis. */ + /* + ThreadLocal threadLocals = + new ThreadLocal() + { + protected Conversation initialValue() + { + Conversation settings = new Conversation(); + settings.conversationId = conversationIdGenerator.getAndIncrement(); + + return settings; + } + }; + */ + + /** Generates new coversation id's as needed. */ + AtomicLong conversationIdGenerator = new AtomicLong(); + + /** + * 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 receiveDestination The destination to listen to for incoming messages. This may be null to use a temporary + * queue. + * @param queueClass The queue implementation class. + * + * @throws JMSException All undelying JMSExceptions are allowed to fall through. + */ + public ConversationFactory(Connection connection, Destination receiveDestination, + Class queueClass) throws JMSException + { + log.debug("public ConversationFactory(Connection connection, Destination receiveDestination = " + receiveDestination + + ", Class queueClass = " + queueClass + "): called"); + + this.connection = connection; + this.queueClass = queueClass; + + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Check if a well-known receive destination has been provided, or use a temporary queue if not. + this.receiveDestination = (receiveDestination != null) ? receiveDestination : session.createTemporaryQueue(); + + consumer = session.createConsumer(receiveDestination); + producer = session.createProducer(null); + + consumer.setMessageListener(new Receiver()); + } + + /** + * Creates a new conversation context. + * + * @return A new conversation context. + */ + public Conversation startConversation() + { + log.debug("public Conversation startConversation(): called"); + + Conversation conversation = new Conversation(); + conversation.conversationId = conversationIdGenerator.getAndIncrement(); + + return conversation; + } + + /** + * Ensures that the reply queue for a conversation exists. + * + * @param conversationId The conversation correlation id. + */ + private void initQueueForId(long conversationId) + { + if (!idsToQueues.containsKey(conversationId)) + { + idsToQueues.put(conversationId, ReflectionUtils.newInstance(queueClass)); + } + } + + /** + * Clears the dead letter box, returning all messages that were in it. + * + * @return All messages in the dead letter box. + */ + public Collection emptyDeadLetterBox() + { + log.debug("public Collection emptyDeadLetterBox(): called"); + + Collection result = new ArrayList(); + deadLetterBox.drainTo(result); + + return result; + } + + /** + * Gets the session over which the conversation is conducted. + * + * @return The session over which the conversation is conducted. + */ + public Session getSession() + { + // Conversation settings = threadLocals.get(); + + return session; + } + + /** + * Used to hold a conversation context. This consists of a correlating id for the conversation, and a reply + * destination automatically updated to the last received reply-to destination. + */ + public class Conversation + { + /** Holds the correlation id for the context. */ + long conversationId; + + /** + * Holds the send destination for the context. This will automatically be updated to the most recently received + * reply-to destination. + */ + Destination sendDestination; + + /** + * 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 sendDestination The destination to send to. This may be null to use the last received reply-to + * destination. + * @param message The message to send. + * + * @throws JMSException All undelying JMSExceptions are allowed to fall through. This will also be thrown if no + * send destination is specified and there is no most recent reply-to destination available + * to use. + */ + public void send(Destination sendDestination, Message message) throws JMSException + { + log.debug("public void send(Destination sendDestination = " + sendDestination + ", Message message = " + message + + "): called"); + + // Conversation settings = threadLocals.get(); + // long conversationId = conversationId; + message.setJMSCorrelationID(Long.toString(conversationId)); + message.setJMSReplyTo(receiveDestination); + + // Ensure that the reply queue for this conversation exists. + initQueueForId(conversationId); + + // Check if an overriding send to destination has been set or use the last reply-to if not. + Destination sendTo = null; + + if (sendDestination != null) + { + sendTo = sendDestination; + } + else if (sendDestination != null) + { + sendTo = sendDestination; + } + else + { + throw new JMSException("The send destination was specified, and no most recent reply-to available to use."); + } + + // Send the message. + synchronized (this) + { + producer.send(sendTo, 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. + * + * @throws JMSException All undelying JMSExceptions are allowed to fall through. Thrown if the received message + * did not have its reply-to destination set up. + */ + public Message receive() throws JMSException + { + log.debug("public Message receive(): called"); + + // Conversation settings = threadLocals.get(); + // long conversationId = settings.conversationId; + + // Ensure that the reply queue for this conversation exists. + initQueueForId(conversationId); + + BlockingQueue queue = idsToQueues.get(conversationId); + + try + { + Message result = queue.take(); + + // Keep the reply-to destination to send replies to. + sendDestination = result.getJMSReplyTo(); + + return result; + } + catch (InterruptedException e) + { + return null; + } + } + + /** + * Gets many messages in an ongoing conversation. If a limit is specified, then once that many messages are + * received they will be returned. If a timeout is specified, then all messages up to the limit, received within + * that timespan will be returned. At least one of the message count or timeout should be set to a value of + * 1 or greater. + * + * @param num The number of messages to receive, or all if this is less than 1. + * @param timeout The timeout in milliseconds to receive the messages in, or forever if this is less than 1. + * + * @return All messages received within the count limit and the timeout. + * + * @throws JMSException All undelying JMSExceptions are allowed to fall through. + */ + public Collection receiveAll(int num, long timeout) throws JMSException + { + log.debug("public Collection receiveAll(int num = " + num + ", long timeout = " + timeout + + "): called"); + + // Check that a timeout or message count was set. + if ((num < 1) && (timeout < 1)) + { + throw new IllegalArgumentException("At least one of message count (num) or timeout must be set."); + } + + // Ensure that the reply queue for this conversation exists. + initQueueForId(conversationId); + BlockingQueue queue = idsToQueues.get(conversationId); + + // Used to collect the received messages in. + Collection result = new ArrayList(); + + // Used to indicate when the timeout or message count has expired. + boolean receiveMore = true; + + int messageCount = 0; + + // Receive messages until the timeout or message count expires. + do + { + try + { + Message next = null; + + // Try to receive the message with a timeout if one has been set. + if (timeout > 0) + { + next = queue.poll(timeout, TimeUnit.MILLISECONDS); + + // Check if the timeout expired, and stop receiving if so. + if (next == null) + { + receiveMore = false; + } + } + // Receive the message without a timeout. + else + { + next = queue.take(); + } + + // Increment the message count if a message was received. + messageCount += (next != null) ? 1 : 0; + + // Check if all the requested messages were received, and stop receiving if so. + if ((num > 0) && (messageCount >= num)) + { + receiveMore = false; + } + + // Keep the reply-to destination to send replies to. + sendDestination = (next != null) ? next.getJMSReplyTo() : sendDestination; + + if (next != null) + { + result.add(next); + } + } + catch (InterruptedException e) + { + // Restore the threads interrupted status. + Thread.currentThread().interrupt(); + + // Stop receiving but return the messages received so far. + receiveMore = false; + } + } + while (receiveMore); + + return result; + } + + /** + * Completes the conversation. 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() + { + log.debug("public void end(): called"); + + // Ensure that the thread local for the current thread is cleaned up. + // Conversation settings = threadLocals.get(); + // long conversationId = settings.conversationId; + // threadLocals.remove(); + + // Ensure that its queue is removed from the queue map. + BlockingQueue queue = idsToQueues.remove(conversationId); + + // Move any outstanding messages on the threads conversation id into the dead letter box. + queue.drainTo(deadLetterBox); + } + } + + /** + * 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) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + try + { + Long conversationId = Long.parseLong(message.getJMSCorrelationID()); + + // Find the converstaion queue to place the message on. If there is no conversation for the message id, + // the the dead letter box queue is used. + BlockingQueue queue = idsToQueues.get(conversationId); + queue = (queue == null) ? deadLetterBox : queue; + + queue.put(message); + } + catch (JMSException e) + { + throw new RuntimeException(e); + } + catch (InterruptedException e) + { + throw new RuntimeException(e); + } + } + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java deleted file mode 100644 index 1fd1fee377..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * - * 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.*; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicLong; - -import javax.jms.*; - -/** - * 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. - * - *

One use of this is 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 conversation - * 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): - * - *

- * ConversationHelper conversation = new ConversationHelper(connection, sendDesitination, replyDestination,
- *                                                          java.util.concurrent.LinkedBlockingQueue.class);
- *
- * 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();
- *  }
- * }
- *
- * 
- * - *

- *
CRC Card
Responsibilities Collaborations - *
Associate messages to an ongoing conversation using correlation ids. - *
Auto manage sessions for conversations. - *
Store messages not in a conversation in dead letter box. - *
- * - * @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. Or use - * a temporary response queue, with only that session listening to it. - * - * @todo Want something convenient that hides 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 -{ - /** Holds a map from correlation id's to queues. */ - private Map> idsToQueues = new HashMap>(); - - private Session session; - private MessageProducer producer; - private MessageConsumer consumer; - - Class queueClass; - - BlockingQueue deadLetterBox = new LinkedBlockingQueue(); - - ThreadLocal threadLocals = - new ThreadLocal() - { - protected PerThreadSettings initialValue() - { - PerThreadSettings settings = new PerThreadSettings(); - settings.conversationId = conversationIdGenerator.getAndIncrement(); - - return settings; - } - }; - - /** Generates new coversation id's as needed. */ - AtomicLong conversationIdGenerator = new AtomicLong(); - - /** - * 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. - * - * @throws JMSException All undelying JMSExceptions are allowed to fall through. - */ - public ConversationHelper(Connection connection, Destination sendDestination, Destination receiveDestination, - Class queueClass) throws JMSException - { - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - producer = session.createProducer(sendDestination); - consumer = session.createConsumer(receiveDestination); - - consumer.setMessageListener(new Receiver()); - - this.queueClass = 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. - * - * @throws JMSException All undelying JMSExceptions are allowed to fall through. - */ - public void send(Message message) throws JMSException - { - PerThreadSettings settings = threadLocals.get(); - long conversationId = settings.conversationId; - message.setJMSCorrelationID(Long.toString(conversationId)); - - // Ensure that the reply queue for this conversation exists. - initQueueForId(conversationId); - - producer.send(message); - } - - /** - * Ensures that the reply queue for a conversation exists. - * - * @param conversationId The conversation correlation id. - */ - private void initQueueForId(long conversationId) - { - if (!idsToQueues.containsKey(conversationId)) - { - idsToQueues.put(conversationId, ReflectionUtils.newInstance(queueClass)); - } - } - - /** - * 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() - { - PerThreadSettings settings = threadLocals.get(); - long conversationId = settings.conversationId; - - // Ensure that the reply queue for this conversation exists. - initQueueForId(conversationId); - - BlockingQueue queue = idsToQueues.get(conversationId); - - try - { - return queue.take(); - } - catch (InterruptedException e) - { - return null; - } - } - - /** - * Gets many messages in an ongoing conversation. If a limit is specified, then once that many messages are - * received they will be returned. If a timeout is specified, then all messages up to the limit, received within - * that timespan will be returned. - * - * @param num The number of messages to receive, or all if this is less than 1. - * @param timeout The timeout in milliseconds to receive the messages in, or forever if this is less than 1. - * - * @return All messages received within the count limit and the timeout. - */ - public Collection receiveAll(int num, long timeout) - { - Collection result = new ArrayList(); - - for (int i = 0; i < num; i++) - { - result.add(receive()); - } - - return result; - } - - /** - * 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() - { - // Ensure that the thread local for the current thread is cleaned up. - PerThreadSettings settings = threadLocals.get(); - long conversationId = settings.conversationId; - threadLocals.remove(); - - // Ensure that its queue is removed from the queue map. - BlockingQueue queue = idsToQueues.remove(conversationId); - - // Move any outstanding messages on the threads conversation id into the dead letter box. - queue.drainTo(deadLetterBox); - } - - /** - * Clears the dead letter box, returning all messages that were in it. - * - * @return All messages in the dead letter box. - */ - public Collection emptyDeadLetterBox() - { - Collection result = new ArrayList(); - deadLetterBox.drainTo(result); - - return result; - } - - /** - * 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) - { - try - { - Long conversationId = Long.parseLong(message.getJMSCorrelationID()); - - // Find the converstaion queue to place the message on. If there is no conversation for the message id, - // the the dead letter box queue is used. - BlockingQueue queue = idsToQueues.get(conversationId); - queue = (queue == null) ? deadLetterBox : queue; - - queue.put(message); - } - catch (JMSException e) - { - throw new RuntimeException(e); - } - catch (InterruptedException e) - { - throw new RuntimeException(e); - } - } - } - - protected class PerThreadSettings - { - /** Holds the correlation id for the current threads conversation. */ - long conversationId; - } - - public Session getSession() - { - return session; - } -} -- cgit v1.2.1 From c40d919a420b377608fce2fc35de7069f912cf84 Mon Sep 17 00:00:00 2001 From: Rupert Smith Date: Mon, 14 May 2007 11:47:13 +0000 Subject: Merged revisions 536571-536583,536586-536823,536825-537014,537016-537018,537020-537025,537027-537028,537030-537160,537162-537170,537172-537672,537674-537782 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/M2 ........ r536571 | ritchiem | 2007-05-09 16:46:57 +0100 (Wed, 09 May 2007) | 1 line QPID-484 Provided default GC and easy customisation.. via setting QPID_JAVA_QC. ........ r536573 | rgreig | 2007-05-09 16:48:11 +0100 (Wed, 09 May 2007) | 1 line Corrected mistaked in test parameters. ........ r537782 | rupertlssmith | 2007-05-14 12:37:41 +0100 (Mon, 14 May 2007) | 1 line Added to the Javadoc. ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@537789 13f79535-47bb-0310-9956-ffa450edef68 --- .../java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java | 2 +- .../src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java index ef69d14be8..3003c00ca5 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java @@ -35,7 +35,7 @@ import org.apache.log4j.Logger; import org.apache.qpid.util.ConversationFactory; /** - * An CoordinatingTestCase is a JUnit test case extension that knows how to coordinate test clients that take part in a + * A 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). * diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java index de5faeac0f..2f9937cae9 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java @@ -134,6 +134,7 @@ public class Coordinator extends TKTestRunner String brokerUrl = options.getProperty("b"); String virtualHost = options.getProperty("h"); reportDir = options.getProperty("o"); + reportDir = (reportDir == null) ? "." : reportDir; // Scan for available test cases using a classpath scanner. Collection> testCaseClasses = -- cgit v1.2.1 From f9e83f70ff57ca31d0849926ecc6271673404b0b Mon Sep 17 00:00:00 2001 From: Rupert Smith Date: Tue, 29 May 2007 10:19:25 +0000 Subject: Added to the Javadoc git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@542476 13f79535-47bb-0310-9956-ffa450edef68 --- .../main/java/org/apache/qpid/interop/coordinator/Coordinator.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java index 2f9937cae9..7c5079327e 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java @@ -23,15 +23,11 @@ package org.apache.qpid.interop.coordinator; import java.io.*; import java.util.*; import java.util.concurrent.LinkedBlockingQueue; - import javax.jms.*; - import junit.framework.Test; import junit.framework.TestResult; import junit.framework.TestSuite; - import org.apache.log4j.Logger; - import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase1DummyRun; import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase2BasicP2P; import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase3BasicPubSub; @@ -39,7 +35,6 @@ import org.apache.qpid.interop.testclient.TestClient; import org.apache.qpid.util.CommandLineParser; import org.apache.qpid.util.ConversationFactory; import org.apache.qpid.util.PrettyPrintingUtils; - import uk.co.thebadgerset.junit.extensions.TKTestResult; import uk.co.thebadgerset.junit.extensions.TKTestRunner; import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; @@ -119,7 +114,7 @@ public class Coordinator extends TKTestRunner try { // Use the command line parser to evaluate the command line with standard handling behaviour (print errors - // and usage then exist if there are errors). + // and usage then exit if there are errors). Properties options = CommandLineParser.processCommandLine(args, new CommandLineParser( -- cgit v1.2.1 From 1d23b9ec5e7296aecefe94a532a9f22dfb5fa5bb Mon Sep 17 00:00:00 2001 From: Martin Ritchie Date: Fri, 15 Jun 2007 16:28:46 +0000 Subject: Merged revisions 439476-447993,447995-448007,448009-448141,448143-448157,448161-448194,448196-448210,448212-448218,448220-448223,448225-448233,448235,448237-448241,448243-448596,448598-448623,448625-448850,448852-448880,448882-448982,448984-449635,449637-449639,449641-449642,449644-449645,449647-449674,449676-449719,449721-449749,449751-449762,449764-449933,449935-449941,449943-450383,450385,450387-450400,450402-450433,450435-450503,450505-450555,450557-450860,450862-451024,451026-451149,451151-451316,451318-451931,451933-452139,452141-452162,452164-452320,452322,452324-452325,452327-452333,452335-452429,452431-452528,452530-452545,452547-453192,453194-453195,453197-453536,453538,453540-453656,453658-454676,454678-454735,454737,454739-454781,454783-462728,462730-462819,462821-462833,462835-462839,462841-463071,463073-463178,463180-463308,463310-463362,463364-463375,463377-463396,463398-463402,463404-463409,463411-463661,463663-463670,463672-463673,463675-464493,464495-464502,464504-464576,464578-464613,464615-464628,464630,464632-464866,464868-464899,464901-464942,464944-464949,464951-465004,465006-465016,465018-465053,465055-465165,465167-465321,465323-465406,465408-465427,465429-465431,465433-465548,465550-466044,466047-466075,466077,466079-466081,466083-466099,466101-466112,466114-466126,466128-466240,466242-466971,466973-466978,466980-467309,467311-467312,467316-467328,467330-467485,467487-467588,467590-467604,467606-467699,467701-467706,467708-467749,467751-468069,468071-468537,468539-469241,469244-469246,469248-469318,469320-469421,469423,469425-469429,469431-469435,469437-469462,469464-469469,469472-469477,469479-469490,469492-469503,469505-469529,469531-469598,469600-469624,469626-469737,469739-469752,469754-469806,469808-469928,469930-469953,469955-470011,470013-470109,470111-470335,470338-470339,470341-470379,470381,470383-470399,470401-470446,470448-470741,470743-470758,470760-470809,470811-470817,470819-470993,470995-471001,471003-471788,471790-471792,471794-472028,472030-472032,472034-472036,472038,472040,472043,472045-472059,472061,472063,472065-472066,472068,472070-472072,472074-472080,472082,472084-472092,472094-472107,472109-472123,472125-472158,472160-472165,472167-472172,472174-472457,472459-472460,472462-472464,472466-472470,472472-472483,472486-472491,472493-472494,472496-472497,472499,472501-472503,472505-472512,472514-472544,472546-472556,472558-472560,472562-472572,472574-472587,472589-472591,472593-472605,472607,472609-472731,472733-472786,472788-472843,472845-472849,472851-472859,472861-472878,472880-472903,472905,472907-472988,472990-472991,472993-473071,473073-473086,473088-473090,473093,473095-473096,473098-473106,473108-473110,473112-473185,473187-473260,473262,473268-473270,473275-473279,473281,473284-473287,473289-473295,473297-473306,473308-473330,473332-473335,473337,473339-473344,473346-473351,473353-473355,473357-473358,473361-473471,473473-473497,473499-473535,473537-473567,473569-473888,473890-474451,474454-474492,474494-474563,474565-474843,474845-474865,474867-474932,474934-475035,475037-475144,475146-475180,475182-475265,475267-475285,475287,475289-475293,475295-475296,475298-475302,475304-475631,475633-475649,475651-475748,475750-475752,475754-476107,476109-476302,476304-476413,476415-476430,476432-476700,476702-476868,476870-477147,477149-477213,477215-477263,477265-477340,477342-477635,477637-477789,477791-477825,477827-477841,477843,477846-477852,477854,477856,477858-477865,477867-477894,477896-478022,478024-478182,478184-478211,478213-478233,478235-478236,478238-478241,478243-478252,478254-478259,478261-478263,478265,478267-478269,478271-478286,478288-478342,478344-478379,478381-478412,478414-478443,478445-478636,478639-478658,478660-478821,478823-478853,478855-478922,478924-478962,478965-478974,478976-479029,479031-479049,479051-479210,479212-479214,479216-479407,479409-479415,479417-479425,479427-479559,479561-479639,479641-479676,479678-479685,479687-480030,480033-480086,480091-480093,480095-480118,480120-480139,480141,480143-480148,480150-480156,480158-480163,480165-480177,480179-480189,480191-480193,480195-480198,480200-480220,480222-480282,480284-480292,480294-480308,480310-480317,480320-480422,480424,480426-480581,480583-480656,480658-480692,480695-480702,480704,480706-480710,480712-480910,480913-480933,480935-480945,480947-480972,480974-480993,480995-481034,481036-481158,481161-481174,481176-481220,481222-481234,481236-481260,481263-481264,481266-481296,481298-481304,481306-481311,481313-481332,481334,481336-481380,481382-481441,481443-482144,482146-482180,482182-482193,482195-482232,482234-482236,482239,482241-482242,482244-482247,482250-482251,482253,482256-482261,482264-482288,482290-482364,482366,482368,482370-482554,482556,482558-482569,482572-482636,482638,482640-482696,482698-482722,482724-482732,482734-482771,482774-482957,482959-483045,483047-483105,483108,483110-483115,483117,483119-483127,483130-483134,483136-483148,483150-483158,483160-483164,483166-483178,483180-483391,483393-483400,483402-483403,483405-483418,483420-483421,483425-483436,483438-483470,483472-483502,483504-483558,483560-483599,483601-483637,483639-483644,483646-483659,483661-483670,483672-483878,483880-483910,483912-483915,483917-483940,483942,483944-483968,483970-483972,483974-483976,483978,483980-484612,484614-484657,484659-484693,484695-484718,484720-484842,484844-484847,484849-484986,484988-485019,485021-485489,485491-485544,485546-485591,485593,485595-485697,485699-485729,485731-485734,485736-485779,485781-485787,485789-485851,485853,485855-486007,486009,486011-486020,486022-486083,486085-486097,486099-486117,486120-486131,486133-486148,486150-486161,486163-486164,486166-486197,486199-486205,486208-486247,486249-486253,486256-486427,486429-486431,486433-486554,486556-486573,486575-486593,486595,486597-486609,486611-486619,486622,486625,486627-486641,486643-486645,486649-486687,486689-486721,486723-486730,486732-486746,486748-486759,486761,486763-486777,486779-486782,486784-486788,486790,486792,486794-486796,486798-487175,487178,487180-487213,487215,487217-487267,487269-487284,487286-487298,487300-487358,487360-487367,487369-487382,487384-487434,487436-487480,487482-487547,487549-487561,487563-487565,487567-487578,487580-487615,487617-487622,487624,487626,487628,487630-487635,487637-487703,487705-487777,487780-487781,487783-487800,487802-487803,487805-487820,487822-487848,487850-487902,487904-488103,488105-488133,488135-488158,488160-488163,488165-488187,488189-488216,488218-488248,488250-488278,488280,488282-488303,488305-488313,488315-488342,488344-488351,488353-488376,488378-488449,488451-488593,488595,488597-488623,488625-488700,488702-488704,488706-488710,488714,488716-488725,488727-488744,488746-488770,488772-488798,488800,488802-488807,488809,488811-488829,488831-488843,488845-488851,488853-489069,489071-489077,489079-489081,489084-489102,489104-489105,489107-489109,489111-489112,489114-489139,489141-489178,489181-489203,489205-489211,489213,489216-489329,489332-489402,489404-489417,489419-489421,489423-489643,489645-489690,489692-489703,489705-489714,489716-489747,489749-489753,489755-489803,489805-489904,489906-490372,490374-490504,490506-490604,490606-490707,490710-490733,490735-490871,490873-490984,490986-491028,491030,491032-491071,491073-491119,491121-491576,491578-491672,491674-491800,491802-491838,491840-491878,491880-492183,492185-492279,492281-492317,492319-492513,492515-492584,492586-492587,492589-492601,492603-492635,492637-492640,492642-492717,492719-492723,492725-492729,492731-492755,492757-492901,492903-492955,492957-492962,492964-492997,492999-493002,493004-493041,493043-493059,493062-493063,493065-493086,493088-493125,493127-493139,493141-493150,493152-493871,493873-494017,494019-494030,494032-494041,494043-494091,494093-494120,494122-494354,494356-494436,494438-494539,494541-494552,494554-494586,494588-494649,494651,494653-494654,494656-494657,494659-494764,494766-494768,494770-494796,494798-494799,494802,494804-494860,494862-494903,494905-494906,494908-495019,495021-495160,495162-495168,495171-495188,495190-495229,495231-495254,495256-495303,495305-495313,495315-495336,495338-495372,495374-495379,495381-495454,495457-495459,495462-495516,495518-495524,495526-495531,495533-495548,495551-495553,495555,495557-495558,495560,495562-495573,495575-495583,495585-495594,495596-495628,495630-495638,495640-495651,495653-495660,495662-495753,495755-496259,496261-496262,496264-496269,496271-496275,496277-496301,496303-496316,496318-496383,496385-496413,496415-496495,496497-496625,496627-496636,496638-496640,496642-496647,496650-496657,496659-496660,496663-496664,496666-496677,496679-496681,496683-496730,496732-496750,496752,496754-496784,496786-496832,496834-496840,496842-496990,496992-496995,496997-497340,497343-497351,497353-497403,497405-497424,497426-497438,497440-497481,497483-497497,497499-497765,497767-497769,497771-497775,497777-497778,497780,497782-497783,497785,497787-497812,497814-497871,497873-497877,497879-498573,498575-498588,498590,498592,498594-498636,498638-498669,498671-498686,498688-498689,498691-498719,498721-498964,498966-498969,498971-498973,498975-498982,498985-499035,499037-499040,499042,499044-499048,499050-499082,499084-499086,499088-499164,499167-499169,499171-499355,499357-499370,499372-499373,499375-499391,499393,499395-499425,499428,499430-499445,499447-499455,499457-499460,499462-499465,499467,499469-499489,499491-499492,499494-499531,499533-499562,499566-499627,499629-499715,499717-499732,499734-499755,499758-499763,499765-499780,499782-499795,499797-499802,499804-499844,499846,499848-499850,499852-499863,499865-499873,499875-499974,499976-499978,499980-500263,500265-500283,500285-500309,500311-501000,501002,501012-501057,501059-501095,501097-501390,501392-501410,501413-501447,501449-501454,501456,501458-501464,501466-501471,501473-501803,501805-501913,501915-501916,501918-501919,501921-501944,501946-502171,502173-502177,502181,502183-502247,502250-502252,502254-502260,502262-502267,502270,502272,502274-502575,502577-502609,502611-502619,502621-502626,502628-502654,502656-503592,503594-503603,503605-503608,503610-503636,503638-503645,503647-503705,503707-503789,503791-504024,504026-504111,504113-504506,504508-504735,504737-504863,504865-504867,504869-504914,504916-505241,505243-505254,505257-505267,505269-505354,505356-505891,505893-505971,505973-506400,506402-506404,506407-506438,506440-506516,506518-506541,506543-506966,506968-506971,506973-507095,507097-507108,507111-507454,507456,507459-507471,507473-507556,507558,507560-507581,507585-507594,507597,507599-507608,507610-507728,507730-507893,507895-507937,507940-508234,508236-508350,508352-508365,508367-508380,508383,508386-508415,508417-508648,508650-508941,508943-509146,509148-509171,509173-509175,509179-509201,509203-509207,509209-509215,509217-509222,509224-509477,509480-509627,509629-509634,509636-509641,509643-509736,509738-509931,509933-510059,510061-510075,510077-510158,510161-510896,510898-510938,510940-511388,511390-511922,511924-512287,512289-512698,512702-512813,512815-512817,512819-513359,513361-513370,513372-514702,514704-514886,514888-514902,514904-515126,515129-515141,515143-515516,515518-515534,515536-515538,515540-515648,515650-515651,515653-516070,516072-516411,516413-516448,516450,516452-517637,517639-517647,517649-517659,517661-517663,517665-517677,517679-517682,517684-517744,517746-518085,518087-518175,518177-518558,518560-518568,518571-518666,518668,518670-518699,518701-518987,518990-518992,518994-519908,519910-519932,519934-520414,520416-520842,520844-520937,520939-521362,521364-521681,521683-521704,521706-521709,521711-521714,521716-521781,521783-521792,521794-522462,522464-522527,522529-522534,522536-522566,522568-522958,522960,522962-522966,522968-522976,522978-522980,522982-522988,522992-522993,522995-523244,523246-523746,523748-524049,524051-524738,524741-524742,524744-524762,524764,524766,524768-525486,525488-525530,525532,525534,525537-525552,525554-525765,525767-525776,525778-525784,525789-525803,525805-525816,525818-525828,525830-525861,525863-525866,525868-526090,526092-526112,526114-526116,526119-526121,526123-526149,526151-526153,526155-526156,526160-526165,526167-526186,526188-526193,526196-526197,526200-526665,526667-526682,526686-526690,526693,526695-526708,526710-526713,526715-526775,526777-526802,526804-526806,526808-527048,527051-527052,527054-527181,527183-527486,527488-527492,527494-527498,527500-527508,527510-527517,527519-527536,527538-527555,527559-527802,527804-527842,527844-527847,527849-527875,527877-527940,527942-527958,527960-527971,527973-528002,528004,528006-528423,528425-529232,529234-529245,529247-529296,529298-529634,529636-529658,529660-529665,529667-529668,529670-530033,530035-530036,530038-530040,530045-530046,530050-530051,530053-530431,530433-530436,530439-530440,530443,530445-530446,530448,530450-530682,530684,530687-530696,530698-530733,530735-530776,530778-530795,530799,530801-530811,530813-530818,530820-530837,530839-531436,531438-531455,531457,531459-531511,531514,531516,531519-531523,531525,531528-531858,531860-531864,531866-531907,531909-531916,531918-531936,531938-531988,531990-532001,532003-532371,532373-532465,532467-532727,532729-532765,532767-532785,532788-532790,532792-532793,532795-533064,533066-533074,533076,533080-533130,533132-533139,533142-533703,533705-533720,533722-533763,533766-533818,533820-533839,533841-533859,533862-534035,534037-534112,534114-534116,534118-534472,534474-534477,534479-534762,534764-534896,534898-534902,534904-535253,535255-535308,535310-535808,535810-535873,535875-536007,536009-536140,536142-536162,536165-536242,536244-536252,536254-536278,536280-536338,536340-536448,536450-536479,536481-536482,536484-536485,536487-536495,536497,536500-536505,536507-536561,536563-536570,536572,536574-536583,536586-536823,536825-537014,537016-537018,537020-537025,537027-537028,537030-537160,537162-537170,537172-537672,537674-537781,537783-537833,537836-537840,537842-537844,537846-537953,537955-538034,538036-538078,538080-538083,538085-538097,538099-538108,538110-538239,538241-538881,538883-538906,538908-538911,538913-538921,538923-539177,539179-539190,539192-539475,539477-539500,539502-539593,539595-539782,539784-539787,539789-540106,540108-540168,540170-540510,540512-541919,541921-544507,544509-544865,544867-545145,545147-547177,547179-547627 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/M2 ........ r539470 | ritchiem | 2007-05-18 14:50:59 +0100 (Fri, 18 May 2007) | 1 line QPID-401 Integrated python tests with maven tested on windows CMD.exe and linux FC5 ........ r539481 | ritchiem | 2007-05-18 15:30:06 +0100 (Fri, 18 May 2007) | 1 line QPID-401 Update to allow -Dskip-python-tests to disable python checking ........ r539484 | ritchiem | 2007-05-18 15:35:13 +0100 (Fri, 18 May 2007) | 1 line QPID-401 Update to allow -Dskip-python-tests to disable python checking ........ r541247 | rgodfrey | 2007-05-24 10:57:00 +0100 (Thu, 24 May 2007) | 1 line QPID-482 : Small performance tweaks ........ r542484 | rupertlssmith | 2007-05-29 11:52:29 +0100 (Tue, 29 May 2007) | 1 line Can now pass property to skip python tests, set in settings.xml ........ r542789 | ritchiem | 2007-05-30 11:09:28 +0100 (Wed, 30 May 2007) | 1 line Update to ensure fastinstall profile skips the broker python tests. ........ r543496 | rupertlssmith | 2007-06-01 15:33:07 +0100 (Fri, 01 Jun 2007) | 1 line QPID-402: FailoverException falling through to client. All blocking operations now wrapped in failover support wrappers. ........ r544109 | ritchiem | 2007-06-04 10:47:53 +0100 (Mon, 04 Jun 2007) | 7 lines Addition of a sustained test client. This is currently setup for running a pub/sub test. The test allows for multiple clients to connect and participate in testing the broker throughput. A single producer continually sends messages to a topic which the clients then send batched results back about. The producer uses the timings in the reports to update the rate at which it sends messages. Ideally reaching a steady state where all messages produced are received by everyone within a specified time frame. ........ r544422 | ritchiem | 2007-06-05 09:50:54 +0100 (Tue, 05 Jun 2007) | 1 line Added documentation on how to run the sustained tests. ........ r546096 | rupertlssmith | 2007-06-11 12:23:08 +0100 (Mon, 11 Jun 2007) | 1 line Set up top dir location and path to parent pom. Needed in preparation for deploy and release plugins. ........ r546190 | rupertlssmith | 2007-06-11 17:43:57 +0100 (Mon, 11 Jun 2007) | 1 line Removed log4j dependency from client. Using slf4j instead, end-user to supply logging implementation as desired. Log4j used for tests. ........ r546441 | rupertlssmith | 2007-06-12 10:52:29 +0100 (Tue, 12 Jun 2007) | 1 line QPID-465, now throws UnsupportedOperationException when sending to a null queue in QueueSender. ........ r546458 | ritchiem | 2007-06-12 12:41:17 +0100 (Tue, 12 Jun 2007) | 1 line Added repository info for running mvn rat:check ........ r547627 | ritchiem | 2007-06-15 12:21:40 +0100 (Fri, 15 Jun 2007) | 1 line QPID-511 adjusted to use the ReadWriteThreadModel rather than setting editing the filterChain directly which could cause problems when using an InVM transport due to the way the InVM transport alters the filter chain during a connect call. ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@547730 13f79535-47bb-0310-9956-ffa450edef68 --- .../docs/RunningSustainedTests.txt | 15 + .../interop/coordinator/CoordinatingTestCase.java | 9 +- .../qpid/interop/coordinator/Coordinator.java | 74 +-- .../interop/coordinator/InvitingTestDecorator.java | 4 +- .../coordinator/ListeningCoordinatorTest.java | 28 + .../coordinator/ListeningTestDecorator.java | 200 ++++++++ .../apache/qpid/interop/testclient/TestClient.java | 87 ++-- .../testclient/testcases/TestCase1DummyRun.java | 8 +- .../testclient/testcases/TestCase3BasicPubSub.java | 9 +- .../apache/qpid/sustained/SustainedTestClient.java | 567 +++++++++++++++++++++ .../qpid/sustained/SustainedTestCoordinator.java | 219 ++++++++ .../java/org/apache/qpid/sustained/TestClient.java | 157 ++++++ .../org/apache/qpid/sustained/TestCoordinator.java | 117 +++++ 13 files changed, 1405 insertions(+), 89 deletions(-) create mode 100644 qpid/java/integrationtests/docs/RunningSustainedTests.txt create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningCoordinatorTest.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningTestDecorator.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestClient.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCoordinator.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestClient.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestCoordinator.java (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/docs/RunningSustainedTests.txt b/qpid/java/integrationtests/docs/RunningSustainedTests.txt new file mode 100644 index 0000000000..2b37f4c5a7 --- /dev/null +++ b/qpid/java/integrationtests/docs/RunningSustainedTests.txt @@ -0,0 +1,15 @@ +In addition to the integration tests the framework provided by this package also allows for +sustained tests to be run. Currently avaible tests: +- org.apache.qpid.sustained.SustainedTestClient : Pub Sub test to determine steady state throughput. + + +Running Tests. + +Run the tests as per the integration tests. + - Start a broker + - Start at least one Client [java org.apache.qpid.sustained.TestClient], ensuring unique naming + - Start Test Controller [java org.apache.qpid.sustained.TestCoordinator] + - Additional Test clients can be started: + [java org.apache.qpid.sustained.TestClient -j org.apache.qpid.sustained.SustainedTestClient] + + diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java index 3003c00ca5..31de84e630 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java @@ -21,10 +21,7 @@ */ package org.apache.qpid.interop.coordinator; -import java.util.Collection; import java.util.Map; -import java.util.Properties; -import java.util.concurrent.LinkedBlockingQueue; import javax.jms.*; @@ -74,13 +71,13 @@ public abstract class CoordinatingTestCase extends TestCase private static final Logger log = Logger.getLogger(CoordinatingTestCase.class); /** Holds the contact details for the sending test client. */ - TestClientDetails sender; + protected TestClientDetails sender; /** Holds the contact details for the receving test client. */ - TestClientDetails receiver; + protected TestClientDetails receiver; /** Holds the conversation factory over which to coordinate the test. */ - ConversationFactory conversationFactory; + protected ConversationFactory conversationFactory; /** * Creates a new coordinating test case with the specified name. diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java index 7c5079327e..6eec20769a 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java @@ -60,28 +60,28 @@ public class Coordinator extends TKTestRunner public static final String DEFAULT_CONNECTION_PROPS_RESOURCE = "org/apache/qpid/interop/connection.properties"; /** Holds the URL of the broker to coordinate the tests on. */ - String brokerUrl; + protected String brokerUrl; /** Holds the virtual host to coordinate the tests on. If null, then the default virtual host is used. */ - String virtualHost; + protected String virtualHost; /** Holds the list of all clients that enlisted, when the compulsory invite was issued. */ - Set enlistedClients = new HashSet(); + protected Set enlistedClients = new HashSet(); /** Holds the conversation helper for the control conversation. */ - private ConversationFactory conversationFactory; + protected ConversationFactory conversationFactory; /** Holds the connection that the coordinating messages are sent over. */ - private Connection connection; + protected Connection connection; /** * Holds the name of the class of the test currently being run. Ideally passed into the {@link #createTestResult} * method, but as the signature is already fixed for this, the current value gets pushed here as a member variable. */ - private String currentTestClassName; + protected String currentTestClassName; /** Holds the path of the directory to output test results too, if one is defined. */ - private static String reportDir; + protected static String _reportDir; /** * Creates an interop test coordinator on the specified broker and virtual host. @@ -89,7 +89,7 @@ public class Coordinator extends TKTestRunner * @param brokerUrl The URL of the broker to connect to. * @param virtualHost The virtual host to run all tests on. Optional, may be null. */ - Coordinator(String brokerUrl, String virtualHost) + public Coordinator(String brokerUrl, String virtualHost) { log.debug("Coordinator(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost + "): called"); @@ -116,38 +116,36 @@ public class Coordinator extends TKTestRunner // Use the command line parser to evaluate the command line with standard handling behaviour (print errors // and usage then exit if there are errors). Properties options = - CommandLineParser.processCommandLine(args, - new CommandLineParser( - new String[][] - { - { "b", "The broker URL.", "broker", "false" }, - { "h", "The virtual host to use.", "virtual host", "false" }, - { "o", "The name of the directory to output test timings to.", "dir", "false" } - })); + CommandLineParser.processCommandLine(args, + new CommandLineParser( + new String[][] + { + {"b", "The broker URL.", "broker", "false"}, + {"h", "The virtual host to use.", "virtual host", "false"}, + {"o", "The name of the directory to output test timings to.", "dir", "false"} + })); // Extract the command line options. String brokerUrl = options.getProperty("b"); String virtualHost = options.getProperty("h"); - reportDir = options.getProperty("o"); - reportDir = (reportDir == null) ? "." : reportDir; + _reportDir = options.getProperty("o"); + _reportDir = (_reportDir == null) ? "." : _reportDir; // Scan for available test cases using a classpath scanner. Collection> testCaseClasses = - new ArrayList>(); + new ArrayList>(); // ClasspathScanner.getMatches(CoordinatingTestCase.class, "^Test.*", true); // Hard code the test classes till the classpath scanner is fixed. Collections.addAll(testCaseClasses, - new Class[] - { - CoordinatingTestCase1DummyRun.class, CoordinatingTestCase2BasicP2P.class, - CoordinatingTestCase3BasicPubSub.class - }); + CoordinatingTestCase1DummyRun.class, + CoordinatingTestCase2BasicP2P.class, + CoordinatingTestCase3BasicPubSub.class); // Check that some test classes were actually found. - if ((testCaseClasses == null) || testCaseClasses.isEmpty()) + if (testCaseClasses.isEmpty()) { throw new RuntimeException( - "No test classes implementing CoordinatingTestCase were found on the class path."); + "No test classes implementing CoordinatingTestCase were found on the class path."); } int i = 0; @@ -194,7 +192,7 @@ public class Coordinator extends TKTestRunner public TestResult start(String[] testClassNames) throws Exception { log.debug("public TestResult start(String[] testClassNames = " + PrettyPrintingUtils.printArray(testClassNames) - + ": called"); + + ": called"); // Connect to the broker. connection = TestClient.createConnection(DEFAULT_CONNECTION_PROPS_RESOURCE, brokerUrl, virtualHost); @@ -228,7 +226,7 @@ public class Coordinator extends TKTestRunner // Record the current test class, so that the test results can be output to a file incorporating this name. this.currentTestClassName = testClassName; - result = super.start(new String[] { testClassName }); + result = super.start(new String[]{testClassName}); } // At this point in time, all tests have completed. Broadcast the shutdown message. @@ -252,7 +250,7 @@ public class Coordinator extends TKTestRunner public static Set extractEnlists(Collection enlists) throws JMSException { log.debug("public static Set extractEnlists(Collection enlists = " + enlists - + "): called"); + + "): called"); Set enlistedClients = new HashSet(); @@ -310,9 +308,9 @@ public class Coordinator extends TKTestRunner targetTest = new WrappedSuiteTestDecorator(suite); log.debug("Wrapped with a WrappedSuiteTestDecorator."); } - // Wrap the tests in an inviting test decorator, to perform the invite/test cycle. - targetTest = new InvitingTestDecorator(targetTest, enlistedClients, conversationFactory, connection); + + targetTest = newTestDecorator(targetTest, enlistedClients, conversationFactory, connection); TestSuite suite = new TestSuite(); suite.addTest(targetTest); @@ -323,6 +321,11 @@ public class Coordinator extends TKTestRunner return super.doRun(suite, wait); } + protected WrappedSuiteTestDecorator newTestDecorator(WrappedSuiteTestDecorator targetTest, Set enlistedClients, ConversationFactory conversationFactory, Connection connection) + { + return new InvitingTestDecorator(targetTest, enlistedClients, conversationFactory, connection); + } + /** * Creates the TestResult object to be used for test runs. * @@ -335,10 +338,10 @@ public class Coordinator extends TKTestRunner TKTestResult result = new TKTestResult(fPrinter.getWriter(), delay, verbose, testCaseName); // Check if a directory to output reports to has been specified and attach test listeners if so. - if (reportDir != null) + if (_reportDir != null) { // Create the report directory if it does not already exist. - File reportDirFile = new File(reportDir); + File reportDirFile = new File(_reportDir); if (!reportDirFile.exists()) { @@ -377,4 +380,9 @@ public class Coordinator extends TKTestRunner return result; } + + public void setReportDir(String reportDir) + { + _reportDir = reportDir; + } } diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java index 858ed1a589..8695f7f66f 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java @@ -21,7 +21,6 @@ package org.apache.qpid.interop.coordinator; import java.util.*; -import java.util.concurrent.LinkedBlockingQueue; import javax.jms.Connection; import javax.jms.Destination; @@ -30,7 +29,6 @@ import javax.jms.Message; import junit.framework.Test; import junit.framework.TestResult; -import junit.framework.TestSuite; import org.apache.log4j.Logger; @@ -107,7 +105,7 @@ public class InvitingTestDecorator extends WrappedSuiteTestDecorator CoordinatingTestCase coordTest = (CoordinatingTestCase) test; // Broadcast the invitation to find out what clients are available to test. - Set enlists = null; + Set enlists; try { Message invite = conversationFactory.getSession().createMessage(); diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningCoordinatorTest.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningCoordinatorTest.java new file mode 100644 index 0000000000..1b4461f8c2 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningCoordinatorTest.java @@ -0,0 +1,28 @@ +/* + * 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 javax.jms.Message; + +public interface ListeningCoordinatorTest +{ + public void latejoin(Message message); +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningTestDecorator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningTestDecorator.java new file mode 100644 index 0000000000..4312dfbcc6 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningTestDecorator.java @@ -0,0 +1,200 @@ +/* + * + * 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 junit.framework.Test; +import junit.framework.TestResult; +import org.apache.log4j.Logger; +import org.apache.qpid.util.ConversationFactory; +import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +/** + *

CRC Card
Responsibilities Collaborations
Broadcast test + * invitations and collect enlists. {@link ConversationFactory}.
Output test failures for clients + * unwilling to run the test case. {@link Coordinator}
Execute coordinated test cases. {@link + * CoordinatingTestCase}
+ */ +public class ListeningTestDecorator extends WrappedSuiteTestDecorator implements MessageListener +{ + private static final Logger log = Logger.getLogger(ListeningTestDecorator.class); + + /** Holds the contact information for all test clients that are available and that may take part in the test. */ + Set allClients; + + /** Holds the conversation helper for the control level conversation for coordinating the test through. */ + ConversationFactory conversationFactory; + + /** Holds the connection that the control conversation is held over. */ + Connection connection; + + /** Holds the underlying {@link CoordinatingTestCase}s that this decorator wraps. */ + WrappedSuiteTestDecorator testSuite; + + /** Hold the current running test case. */ + CoordinatingTestCase _currentTest = null; + + /** + * Creates a wrapped suite test decorator from another one. + * + * @param suite The test suite. + * @param availableClients The list of all clients that responded to the compulsory invite. + * @param controlConversation The conversation helper for the control level, test coordination conversation. + * @param controlConnection The connection that the coordination messages are sent over. + */ + public ListeningTestDecorator(WrappedSuiteTestDecorator suite, Set availableClients, + ConversationFactory controlConversation, Connection controlConnection) + { + super(suite); + + log.debug("public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set allClients = " + + availableClients + ", ConversationHelper controlConversation = " + controlConversation + "): called"); + + testSuite = suite; + allClients = availableClients; + conversationFactory = controlConversation; + connection = controlConnection; + } + + /** + * Broadcasts a test invitation and accetps enlisting from participating clients. The wrapped test case is then + * repeated for every combination of test clients (provided the wrapped test case extends {@link + * CoordinatingTestCase}. + * + *

Any JMSExceptions during the invite/enlist conversation will be allowed to fall through as runtime + * exceptions, resulting in the non-completion of the test run. + * + * @param testResult The the results object to monitor the test results with. + * + * @todo Better error recovery for failure of the invite/enlist conversation could be added. + */ + public void run(TestResult testResult) + { + log.debug("public void run(TestResult testResult): called"); + + Collection tests = testSuite.getAllUnderlyingTests(); + + for (Test test : tests) + { + CoordinatingTestCase coordTest = (CoordinatingTestCase) test; + + Set enlists = signupClients(coordTest); + + if (enlists.size() == 0) + { + throw new RuntimeException("No clients to test with"); + } + + Iterator clients = enlists.iterator(); + coordTest.setSender(clients.next()); + + while (clients.hasNext()) + { + // Set the sending and receiving client details on the test case. + coordTest.setReceiver(clients.next()); + } + + // Pass down the connection to hold the coordination conversation over. + coordTest.setConversationFactory(conversationFactory); + + + if (coordTest instanceof ListeningCoordinatorTest) + { + _currentTest = coordTest; + } + // Execute the test case. + coordTest.run(testResult); + + _currentTest = null; + } + } + + private Set signupClients(CoordinatingTestCase coordTest) + { + // Broadcast the invitation to find out what clients are available to test. + Set enlists; + try + { + Message invite = conversationFactory.getSession().createMessage(); + Destination controlTopic = conversationFactory.getSession().createTopic("iop.control"); + ConversationFactory.Conversation conversation = conversationFactory.startConversation(); + + invite.setStringProperty("CONTROL_TYPE", "INVITE"); + invite.setStringProperty("TEST_NAME", coordTest.getTestCaseNameForTestMethod(coordTest.getName())); + + conversation.send(controlTopic, invite); + + // Wait for a short time, to give test clients an opportunity to reply to the invitation. + Collection replies = conversation.receiveAll(allClients.size(), 5000); + + log.debug("Received " + replies.size() + " enlist replies"); + + enlists = Coordinator.extractEnlists(replies); + + //Create topic to listen on for latejoiners + Destination listenTopic = conversationFactory.getSession().createTopic("iop.control.test." + coordTest.getTestCaseNameForTestMethod(coordTest.getName())); + + //Listen for joiners + conversationFactory.getSession().createConsumer(listenTopic).setMessageListener(this); + log.debug("Created consumer on :" + listenTopic); + } + catch (JMSException e) + { + throw new RuntimeException("There was a JMSException during the invite/enlist conversation.", e); + } + + return enlists; + } + + /** + * Prints a string summarizing this test decorator, mainly for debugging purposes. + * + * @return String representation for debugging purposes. + */ + public String toString() + { + return "ListeningTestDecorator: [ testSuite = " + testSuite + " ]"; + } + + + public void onMessage(Message message) + { + try + { + if (message.getStringProperty("CONTROL_TYPE").equals("LATEJOIN")) + { + ((ListeningCoordinatorTest) _currentTest).latejoin(message); + } + } + catch (JMSException e) + { + log.debug("Unable to process message:" + message); + } + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java index 0b9c72e1b6..6cca23446f 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java @@ -75,16 +75,17 @@ public class TestClient implements MessageListener /** Holds all the test cases loaded from the classpath. */ Map testCases = new HashMap(); - InteropClientTestCase currentTestCase; + protected InteropClientTestCase currentTestCase; - private MessageProducer producer; - private Session session; + protected Connection _connection; + protected MessageProducer producer; + protected Session session; - private String clientName = CLIENT_NAME; + protected String clientName = CLIENT_NAME; /** - * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified - * client identifying name. + * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client + * identifying name. * * @param brokerUrl The url of the broker to connect to. * @param virtualHost The virtual host to conect to. @@ -93,7 +94,7 @@ public class TestClient implements MessageListener public TestClient(String brokerUrl, String virtualHost, String clientName) { log.debug("public TestClient(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost - + ", String clientName = " + clientName + "): called"); + + ", String clientName = " + clientName + "): called"); // Retain the connection parameters. this.brokerUrl = brokerUrl; @@ -117,13 +118,13 @@ public class TestClient implements MessageListener { // Use the command line parser to evaluate the command line. CommandLineParser commandLine = - new CommandLineParser( - new String[][] - { - { "b", "The broker URL.", "broker", "false" }, - { "h", "The virtual host to use.", "virtual host", "false" }, - { "n", "The test client name.", "name", "false" } - }); + new CommandLineParser( + new String[][] + { + {"b", "The broker URL.", "broker", "false"}, + {"h", "The virtual host to use.", "virtual host", "false"}, + {"n", "The test client name.", "name", "false"} + }); // Capture the command line arguments or display errors and correct usage and then exit. Properties options = null; @@ -151,9 +152,17 @@ public class TestClient implements MessageListener // Create a test client and start it running. TestClient client = new TestClient(brokerUrl, virtualHost, (clientName == null) ? CLIENT_NAME : clientName); + // Use a class path scanner to find all the interop test case implementations. + Collection> testCaseClasses = + new ArrayList>(); + // ClasspathScanner.getMatches(InteropClientTestCase.class, "^TestCase.*", true); + // Hard code the test classes till the classpath scanner is fixed. + Collections.addAll(testCaseClasses, + new Class[]{TestCase1DummyRun.class, TestCase2BasicP2P.class, TestClient.class}); + try { - client.start(); + client.start(testCaseClasses); } catch (Exception e) { @@ -165,20 +174,12 @@ public class TestClient implements MessageListener /** * Starts the interop test client running. This causes it to start listening for incoming test invites. * - * @throws JMSException Any underlying JMSExceptions are allowed to fall through. + * @throws JMSException Any underlying JMSExceptions are allowed to fall through. @param testCaseClasses */ - private void start() throws JMSException + protected void start(Collection> testCaseClasses) throws JMSException { log.debug("private void start(): called"); - // Use a class path scanner to find all the interop test case implementations. - Collection> testCaseClasses = - new ArrayList>(); - // ClasspathScanner.getMatches(InteropClientTestCase.class, "^TestCase.*", true); - // Hard code the test classes till the classpath scanner is fixed. - Collections.addAll(testCaseClasses, - new Class[] { TestCase1DummyRun.class, TestCase2BasicP2P.class, TestCase3BasicPubSub.class }); - // Create all the test case implementations and index them by the test names. for (Class nextClass : testCaseClasses) { @@ -200,9 +201,9 @@ public class TestClient implements MessageListener } // Open a connection to communicate with the coordinator on. - Connection connection = createConnection(DEFAULT_CONNECTION_PROPS_RESOURCE, brokerUrl, virtualHost); + _connection = createConnection(DEFAULT_CONNECTION_PROPS_RESOURCE, brokerUrl, virtualHost); - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); // Set this up to listen for control messages. MessageConsumer consumer = session.createConsumer(session.createTopic("iop.control." + clientName)); @@ -215,7 +216,7 @@ public class TestClient implements MessageListener producer = session.createProducer(null); // Start listening for incoming control messages. - connection.start(); + _connection.start(); } /** @@ -232,22 +233,25 @@ public class TestClient implements MessageListener * @param virtualHost The virtual host to connectio to, null to use the default. * * @return A JMS conneciton. + * + * @todo Make username/password configurable. Allow multiple urls for fail over. Once it feels right, move it to a + * Utils library class. */ public static Connection createConnection(String connectionPropsResource, String brokerUrl, String virtualHost) { log.debug("public static Connection createConnection(String connectionPropsResource = " + connectionPropsResource - + ", String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost + "): called"); + + ", String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost + "): called"); try { Properties connectionProps = - PropertiesUtils.getProperties(TestClient.class.getClassLoader().getResourceAsStream( - connectionPropsResource)); + PropertiesUtils.getProperties(TestClient.class.getClassLoader().getResourceAsStream( + connectionPropsResource)); if (brokerUrl != null) { String connectionString = - "amqp://guest:guest/" + ((virtualHost != null) ? virtualHost : "") + "?brokerlist='" + brokerUrl + "'"; + "amqp://guest:guest/" + ((virtualHost != null) ? virtualHost : "") + "?brokerlist='" + brokerUrl + "'"; connectionProps.setProperty(CONNECTION_PROPERTY, connectionString); } @@ -286,21 +290,21 @@ public class TestClient implements MessageListener String controlType = message.getStringProperty("CONTROL_TYPE"); String testName = message.getStringProperty("TEST_NAME"); + log.info("onMessage(Message message = " + message + "): for '" + controlType + "' to '" + testName + "'"); + // Check if the message is a test invite. if ("INVITE".equals(controlType)) { - String testCaseName = message.getStringProperty("TEST_NAME"); - // Flag used to indicate that an enlist should be sent. Only enlist to compulsory invites or invites // for which test cases exist. boolean enlist = false; - if (testCaseName != null) + if (testName != null) { - log.debug("Got an invite to test: " + testCaseName); + log.debug("Got an invite to test: " + testName); // Check if the requested test case is available. - InteropClientTestCase testCase = testCases.get(testCaseName); + InteropClientTestCase testCase = testCases.get(testName); if (testCase != null) { @@ -308,6 +312,10 @@ public class TestClient implements MessageListener currentTestCase = testCase; enlist = true; } + else + { + log.warn("'" + testName + "' not part of this clients tests."); + } } else { @@ -325,6 +333,8 @@ public class TestClient implements MessageListener enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + log.info("Sending Message '" + enlistMessage + "'. to " + message.getJMSReplyTo()); + producer.send(message.getJMSReplyTo(), enlistMessage); } } @@ -369,9 +379,10 @@ public class TestClient implements MessageListener } else if ("TERMINATE".equals(controlType)) { - System.out.println("Received termination instruction from coordinator."); + log.info("Received termination instruction from coordinator."); // Is a cleaner shutdown needed? + _connection.close(); System.exit(0); } else diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java index 874f86daa9..85b89172bb 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java @@ -20,14 +20,14 @@ */ package org.apache.qpid.interop.testclient.testcases; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.Session; - import org.apache.log4j.Logger; import org.apache.qpid.interop.testclient.InteropClientTestCase; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; + /** * 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. diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java index 2773cad3f3..ec8c72afa9 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java @@ -5,7 +5,6 @@ import javax.jms.*; import org.apache.log4j.Logger; import org.apache.qpid.interop.testclient.InteropClientTestCase; -import org.apache.qpid.interop.testclient.TestClient; /** * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the @@ -120,8 +119,8 @@ public class TestCase3BasicPubSub implements InteropClientTestCase session = new Session[1]; connection[0] = - TestClient.createConnection(TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, TestClient.brokerUrl, - TestClient.virtualHost); + org.apache.qpid.interop.testclient.TestClient.createConnection(org.apache.qpid.interop.testclient.TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, org.apache.qpid.interop.testclient.TestClient.brokerUrl, + org.apache.qpid.interop.testclient.TestClient.virtualHost); session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); // Extract and retain the test parameters. @@ -140,8 +139,8 @@ public class TestCase3BasicPubSub implements InteropClientTestCase for (int i = 0; i < numReceivers; i++) { connection[i] = - TestClient.createConnection(TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, TestClient.brokerUrl, - TestClient.virtualHost); + org.apache.qpid.interop.testclient.TestClient.createConnection(org.apache.qpid.interop.testclient.TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, org.apache.qpid.interop.testclient.TestClient.brokerUrl, + org.apache.qpid.interop.testclient.TestClient.virtualHost); session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); sendDestination = session[i].createTopic(sendKey); diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestClient.java new file mode 100644 index 0000000000..3a0d587d44 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestClient.java @@ -0,0 +1,567 @@ +package org.apache.qpid.sustained; + +import org.apache.log4j.Logger; +import org.apache.qpid.client.AMQNoConsumersException; +import org.apache.qpid.client.AMQNoRouteException; +import org.apache.qpid.interop.testclient.testcases.TestCase3BasicPubSub; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import java.util.HashMap; +import java.util.Map; + +/** + * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the + * default topic exchange, using the specified number of receiver connections. Produces reports on the actual number of + * messages sent/received. + * + *

CRC Card
Responsibilities Collaborations
Supply the name + * of the test case that this implements.
Accept/Reject invites based on test parameters.
Adapt to + * assigned roles.
Send required number of test messages using pub/sub.
Generate test reports. + *
+ */ +public class SustainedTestClient extends TestCase3BasicPubSub implements ExceptionListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(SustainedTestClient.class); + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ +// private int numMessages; + + /** The number of receiver connection to use. */ + private int numReceivers; + + /** The routing key to send them to on the default direct exchange. */ + private Destination sendDestination; + + /** The routing key to send updates to on the default direct exchange. */ + private Destination sendUpdateDestination; + + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** Adapter that adjusts the send rate based on the updates from clients. */ + SustainedRateAdapter _rateAdapter; + + /** */ + int updateInterval; + + private boolean _running = true; + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "Perf_SustainedPubSub"; + } + + /** + * 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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numReceivers = assignRoleMessage.getIntProperty("SUSTAINED_NUM_RECEIVERS"); + updateInterval = assignRoleMessage.getIntProperty("SUSTAINED_UPDATE_INTERVAL"); + String sendKey = assignRoleMessage.getStringProperty("SUSTAINED_KEY"); + String sendUpdateKey = assignRoleMessage.getStringProperty("SUSTAINED_UPDATE_KEY"); + int ackMode = assignRoleMessage.getIntProperty("ACKNOWLEDGE_MODE"); + + log.debug("numReceivers = " + numReceivers); + log.debug("updateInterval = " + updateInterval); + log.debug("ackMode = " + ackMode); + log.debug("sendKey = " + sendKey); + log.debug("sendUpdateKey = " + sendUpdateKey); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + log.info("*********** Creating SENDER"); + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = + org.apache.qpid.interop.testclient.TestClient.createConnection(org.apache.qpid.interop.testclient.TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, org.apache.qpid.interop.testclient.TestClient.brokerUrl, + org.apache.qpid.interop.testclient.TestClient.virtualHost); + session[0] = connection[0].createSession(false, ackMode); + + // Extract and retain the test parameters. + sendDestination = session[0].createTopic(sendKey); + + connection[0].setExceptionListener(this); + + producer = session[0].createProducer(sendDestination); + + sendUpdateDestination = session[0].createTopic(sendUpdateKey); + MessageConsumer updateConsumer = session[0].createConsumer(sendUpdateDestination); + + _rateAdapter = new SustainedRateAdapter(this); + updateConsumer.setMessageListener(_rateAdapter); + + + break; + + // Otherwise the receiver role is being assigned, so set this up to listen for messages on the required number + // of receiver connections. + case RECEIVER: + log.info("*********** Creating RECEIVER"); + // Create the required number of receiver connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = + org.apache.qpid.interop.testclient.TestClient.createConnection(org.apache.qpid.interop.testclient.TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, + org.apache.qpid.interop.testclient.TestClient.brokerUrl, + org.apache.qpid.interop.testclient.TestClient.virtualHost); + session[i] = connection[i].createSession(false, ackMode); + + sendDestination = session[i].createTopic(sendKey); + + sendUpdateDestination = session[i].createTopic(sendUpdateKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + + consumer.setMessageListener(new SustainedListener(TestClient.CLIENT_NAME + "-" + i, updateInterval, session[i], sendUpdateDestination)); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (int i = 0; i < connection.length; i++) + { + connection[i].start(); + } + } + + /** Performs the test case actions. */ + public void start() throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + Message testMessage = session[0].createTextMessage("test"); + +// for (int i = 0; i < numMessages; i++) + while (_running) + { + producer.send(testMessage); + + _rateAdapter.sentMessage(); + } + break; + case RECEIVER: + + } + } + + /** + * 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 + { + log.debug("public Message getReport(Session session): called"); + + // Close the test connections. + for (int i = 0; i < connection.length; i++) + { + connection[i].close(); + } + + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + + return report; + } + + public void onException(JMSException jmsException) + { + Exception linked = jmsException.getLinkedException(); + + if (linked != null) + { + if (linked instanceof AMQNoRouteException) + { + log.warn("No route ."); + } + else if (linked instanceof AMQNoConsumersException) + { + log.warn("No clients currently available for message:" + ((AMQNoConsumersException) linked).getUndeliveredMessage()); + } + else + { + + log.warn("LinkedException:" + linked); + } + + _rateAdapter.NO_CLIENTS = true; + } + else + { + log.warn("Exception:" + linked); + } + } + + class SustainedListener implements MessageListener + { + private int _received = 0; + private int _updateInterval = 0; + private Long _time; + MessageProducer _updater; + Session _session; + String _client; + + + public SustainedListener(String clientname, int updateInterval, Session session, Destination sendDestination) throws JMSException + { + _updateInterval = updateInterval; + _client = clientname; + _session = session; + _updater = session.createProducer(sendDestination); + } + + public void setReportInterval(int reportInterval) + { + _updateInterval = reportInterval; + _received = 0; + } + + public void onMessage(Message message) + { + if (log.isDebugEnabled()) + { + log.debug("Message " + _received + "received in listener"); + } + + if (message instanceof TextMessage) + { + + try + { + if (((TextMessage) message).getText().equals("test")) + { + if (_received == 0) + { + _time = System.nanoTime(); + sendStatus(0, _received); + } + + _received++; + + if (_received % _updateInterval == 0) + { + Long currentTime = System.nanoTime(); + + try + { + sendStatus(currentTime - _time, _received); + _time = currentTime; + } + catch (JMSException e) + { + log.error("Unable to send update."); + } + } + + } + } + catch (JMSException e) + { + //ignore error + } + } + } + + private void sendStatus(long time, int received) throws JMSException + { + Message updateMessage = _session.createTextMessage("update"); + updateMessage.setStringProperty("CLIENT_ID", _client); + updateMessage.setStringProperty("CONTROL_TYPE", "UPDATE"); + updateMessage.setLongProperty("RECEIVED", received); + updateMessage.setLongProperty("DURATION", time); + + log.info("**** SENDING **** CLIENT_ID:" + _client + " RECEIVED:" + received + " DURATION:" + time); + + _updater.send(updateMessage); + } + + } + + class SustainedRateAdapter implements MessageListener + { + private SustainedTestClient _client; + private long _variance = 250; //no. messages to allow drifting + private volatile long _delay; //in nanos + private long _sent; + private Map _slowClients = new HashMap(); + private static final long PAUSE_SLEEP = 10; // 10 ms + private static final long NO_CLIENT_SLEEP = 1000; // 1s + private static final long MAX_MESSAGE_DRIFT = 1000; // no messages drifted from producer + private volatile boolean NO_CLIENTS = true; + private int _delayShifting; + private static final int REPORTS_WITHOUT_CHANGE = 10; + private static final double MAXIMUM_DELAY_SHIFT = .02; //2% + + SustainedRateAdapter(SustainedTestClient client) + { + _client = client; + } + + public void onMessage(Message message) + { + if (log.isDebugEnabled()) + { + log.debug("SustainedRateAdapter onMessage(Message message = " + message + "): called"); + } + + try + { + String controlType = message.getStringProperty("CONTROL_TYPE"); + + // Check if the message is a test invite. + if ("UPDATE".equals(controlType)) + { + NO_CLIENTS = false; + long duration = message.getLongProperty("DURATION"); + long received = message.getLongProperty("RECEIVED"); + String client = message.getStringProperty("CLIENT_ID"); + + log.info("**** SENDING **** CLIENT_ID:" + client + " RECEIVED:" + received + " DURATION:" + duration); + + + recordSlow(client, received); + + adjustDelay(client, received, duration); + } + } + catch (JMSException e) + { + // + } + } + + class Pair + { + X item1; + Y item2; + + Pair(X i1, Y i2) + { + item1 = i1; + item2 = i2; + } + + X getItem1() + { + return item1; + } + + Y getItem2() + { + return item2; + } + } + + Map> delays = new HashMap>(); + Long totalReceived = 0L; + Long totalDuration = 0L; + + private void adjustDelay(String client, long received, long duration) + { + Pair current = delays.get(client); + + if (current == null) + { + delays.put(client, new Pair(received, duration)); + } + else + { + //reduce totals + totalReceived -= current.getItem1(); + totalDuration -= current.getItem2(); + } + + totalReceived += received; + totalDuration += duration; + + long averageDuration = totalDuration / delays.size(); + + long diff = Math.abs(_delay - averageDuration); + + //if the averageDuration differs from the current by more than the specified variane then adjust delay. + if (diff > _variance) + { + if (averageDuration > _delay) + { + // we can go faster + _delay -= diff; + if (_delay < 0) + { + _delay = 0; + } + } + else + { + // we need to slow down + _delay += diff; + } + delayChanged(); + } + else + { + delayStable(); + } + + } + + private void delayChanged() + { + _delayShifting = REPORTS_WITHOUT_CHANGE; + } + + private void delayStable() + { + _delayShifting--; + + if (_delayShifting < 0) + { + _delayShifting = 0; + log.info("Delay stabilised:" + _delay); + } + } + + // Record Slow clients + private void recordSlow(String client, long received) + { + if (received < (_sent - _variance)) + { + _slowClients.put(client, received); + } + else + { + _slowClients.remove(client); + } + } + + public void sentMessage() + { + if (_sent % updateInterval == 0) + { + + // Cause test to pause when we have slow + if (!_slowClients.isEmpty() || NO_CLIENTS) + { + log.info("Pausing for slow clients"); + + //_delay <<= 1; + + while (!_slowClients.isEmpty()) + { + sleep(PAUSE_SLEEP); + } + + if (NO_CLIENTS) + { + sleep(NO_CLIENT_SLEEP); + } + + log.debug("Continuing"); + return; + } + else + { + log.info("Delay:" + _delay); + } + } + + _sent++; + + if (_delay > 0) + { + // less than 10ms sleep doesn't work. + // _delay is in nano seconds + if (_delay < 1000000) + { + sleep(0, (int) _delay); + } + else + { + if (_delay < 30000000000L) + { + sleep(_delay / 1000000, (int) (_delay % 1000000)); + } + } + } + } + + private void sleep(long sleep) + { + sleep(sleep, 0); + } + + private void sleep(long milli, int nano) + { + try + { + log.debug("Sleep:" + milli + ":" + nano); + Thread.sleep(milli, nano); + } + catch (InterruptedException e) + { + // + } + } + } + +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCoordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCoordinator.java new file mode 100644 index 0000000000..4081d87192 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCoordinator.java @@ -0,0 +1,219 @@ +/* + * 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.sustained; + +import org.apache.log4j.Logger; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.interop.coordinator.ListeningCoordinatorTest; +import org.apache.qpid.interop.coordinator.TestClientDetails; +import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase3BasicPubSub; +import org.apache.qpid.util.ConversationFactory; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class SustainedTestCoordinator extends CoordinatingTestCase3BasicPubSub implements ListeningCoordinatorTest +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(SustainedTestCoordinator.class); + private List _receivers; + private static final String SUSTAINED_KEY = "Perf_SustainedPubSub"; + Map _testProperties; + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public SustainedTestCoordinator(String name) + { + super(name); + _receivers = new LinkedList(); + } + + /** + * Adds a receiver to this test. + * + * @param receiver The contact details of the sending client in the test. + */ + public void setReceiver(TestClientDetails receiver) + { + _receivers.add(receiver); + } + + + /** + * Performs the a single test run + * + * @throws Exception if there was a problem running the test. + */ + public void testBasicPubSub() throws Exception + { + log.debug("public void testSinglePubSubCycle(): called"); + + Map testConfig = new HashMap(); + testConfig.put("TEST_NAME", "Perf_SustainedPubSub"); + testConfig.put("SUSTAINED_KEY", SUSTAINED_KEY); + //testConfig.put("SUSTAINED_MSG_RATE", 10); + testConfig.put("SUSTAINED_NUM_RECEIVERS", 2); + testConfig.put("SUSTAINED_UPDATE_INTERVAL", 25); + testConfig.put("SUSTAINED_UPDATE_KEY", SUSTAINED_KEY + ".UPDATE"); + testConfig.put("ACKNOWLEDGE_MODE", AMQSession.NO_ACKNOWLEDGE); + + sequenceTest(testConfig); + } + + /** + * 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 assigning the test roles, begining the test and gathering the test reports from the + * participants. + * + * @param testProperties The test case definition. + * + * @return The test results from the senders and receivers. + * + * @throws javax.jms.JMSException All underlying JMSExceptions are allowed to fall through. + */ + protected Message[] sequenceTest(Map testProperties) throws JMSException + { + log.debug("protected Message[] sequenceTest(Object... testProperties = " + testProperties + "): called"); + + Session session = conversationFactory.getSession(); + Destination senderControlTopic = session.createTopic(sender.privateControlKey); + + ConversationFactory.Conversation senderConversation = conversationFactory.startConversation(); + + // Assign the sender role to the sending test client. + Message assignSender = conversationFactory.getSession().createMessage(); + setPropertiesOnMessage(assignSender, testProperties); + assignSender.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); + assignSender.setStringProperty("ROLE", "SENDER"); + + senderConversation.send(senderControlTopic, assignSender); + + //Assign and wait for the receiver ckuebts to be ready. + _testProperties = testProperties; + + // Wait for the senders to confirm their roles. + senderConversation.receive(); + + assignReceivers(); + + // Start the test. + Message start = session.createMessage(); + start.setStringProperty("CONTROL_TYPE", "START"); + + senderConversation.send(senderControlTopic, start); + + // Wait for the test sender to return its report. + Message senderReport = senderConversation.receive(); + + try + { + Thread.sleep(500); + } + catch (InterruptedException e) + { + } + + // Ask the receiver for its report. + Message statusRequest = session.createMessage(); + statusRequest.setStringProperty("CONTROL_TYPE", "STATUS_REQUEST"); + + + return new Message[]{senderReport}; + } + + private void assignReceivers() + { + for (TestClientDetails receiver : _receivers) + { + registerReceiver(receiver); + } + } + + private void registerReceiver(TestClientDetails receiver) + { + log.info("registerReceiver called for receiver:" + receiver); + try + { + Session session = conversationFactory.getSession(); + Destination receiverControlTopic = session.createTopic(receiver.privateControlKey); + ConversationFactory.Conversation receiverConversation = conversationFactory.startConversation(); + // Assign the receiver role the receiving client. + Message assignReceiver = session.createMessage(); + setPropertiesOnMessage(assignReceiver, _testProperties); + assignReceiver.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); + assignReceiver.setStringProperty("ROLE", "RECEIVER"); + + receiverConversation.send(receiverControlTopic, assignReceiver); + + //Don't wait for receiver to be ready.... we can't this is being done in + // the dispatcher thread, and most likely the acceptance message we + // want is sitting in the Dispatcher._queue waiting its turn for being + // dispatched so if we block here we won't can't get the message. + // So assume consumer is ready for action. + //receiverConversation.receive(); + } + catch (JMSException e) + { + log.warn("Unable to assign receiver:" + receiver + ". Due to:" + e.getMessage()); + } + } + + public void latejoin(Message message) + { + try + { + + TestClientDetails clientDetails = new TestClientDetails(); + clientDetails.clientName = message.getStringProperty("CLIENT_NAME"); + clientDetails.privateControlKey = message.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); + + + registerReceiver(clientDetails); + } + catch (JMSException e) + { + //swallow + } + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the interop + * testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "Perf_SustainedPubSub"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestClient.java new file mode 100644 index 0000000000..44fc090410 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestClient.java @@ -0,0 +1,157 @@ +/* + * 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.sustained; + +import org.apache.log4j.Logger; +import org.apache.qpid.interop.testclient.InteropClientTestCase; +import org.apache.qpid.util.CommandLineParser; + +import javax.jms.JMSException; +import javax.jms.Message; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Properties; + +public class TestClient extends org.apache.qpid.interop.testclient.TestClient +{ + private static Logger log = Logger.getLogger(TestClient.class); + + /** + * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client + * identifying name. + * + * @param brokerUrl The url of the broker to connect to. + * @param virtualHost The virtual host to conect to. + * @param clientName The client name to use. + */ + public TestClient(String brokerUrl, String virtualHost, String clientName) + { + super(brokerUrl, virtualHost, clientName); + } + + /** + * The entry point for the interop test coordinator. This client accepts the following command line arguments: + * + *

-b The broker URL. Optional.
-h The virtual + * host. Optional.
-n The test client name. Optional.
name=value + * Trailing argument define name/value pairs. Added to system properties. Optional.
+ * + * @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", "false"}, + {"h", "The virtual host to use.", "virtual host", "false"}, + {"n", "The test client name.", "name", "false"}, + {"j", "Join this test client to running test.", "join", ""} + }); + + // 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"); + String clientName = options.getProperty("n"); + String join = options.getProperty("j"); + + // 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, (clientName == null) ? CLIENT_NAME : clientName); + + // Use a class path scanner to find all the interop test case implementations. + Collection> testCaseClasses = + new ArrayList>(); + // ClasspathScanner.getMatches(InteropClientTestCase.class, "^TestCase.*", true); + // Hard code the test classes till the classpath scanner is fixed. + Collections.addAll(testCaseClasses, + SustainedTestClient.class); + + + try + { + client.start(testCaseClasses, join); + } + catch (Exception e) + { + log.error("The test client was unable to start.", e); + System.exit(1); + } + } + + protected void start(Collection> testCaseClasses, String join) throws JMSException, ClassNotFoundException + { + super.start(testCaseClasses); + log.debug("private void start(): called"); + + if (join != null && !join.equals("")) + { + Message latejoin = session.createMessage(); + + try + { + Object test = Class.forName(join).newInstance(); + if (test instanceof InteropClientTestCase) + { + currentTestCase = (InteropClientTestCase) test; + } + else + { + throw new RuntimeException("Requested to join class '" + join + "' but this is not a InteropClientTestCase."); + } + + latejoin.setStringProperty("CONTROL_TYPE", "LATEJOIN"); + latejoin.setStringProperty("CLIENT_NAME", clientName); + latejoin.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); + producer.send(session.createTopic("iop.control.test." + currentTestCase.getName()), latejoin); + } + catch (InstantiationException e) + { + log.warn("Unable to request latejoining of test:" + currentTestCase); + } + catch (IllegalAccessException e) + { + log.warn("Unable to request latejoining of test:" + currentTestCase); + } + } + } + +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestCoordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestCoordinator.java new file mode 100644 index 0000000000..7e12fe39fb --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestCoordinator.java @@ -0,0 +1,117 @@ +/* + * 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.sustained; + +import org.apache.qpid.interop.coordinator.Coordinator; +import org.apache.qpid.interop.coordinator.ListeningTestDecorator; +import org.apache.qpid.interop.coordinator.TestClientDetails; +import org.apache.qpid.util.CommandLineParser; +import org.apache.qpid.util.ConversationFactory; +import org.apache.log4j.Logger; + +import java.util.Properties; +import java.util.Set; + +import junit.framework.TestResult; +import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; + +import javax.jms.Connection; + +public class TestCoordinator extends Coordinator +{ + + private static final Logger log = Logger.getLogger(TestCoordinator.class); + + /** + * 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 null. + */ + TestCoordinator(String brokerUrl, String virtualHost) + { + super(brokerUrl, virtualHost); + } + + protected WrappedSuiteTestDecorator newTestDecorator(WrappedSuiteTestDecorator targetTest, Set enlistedClients, ConversationFactory conversationFactory, Connection connection) + { + return new ListeningTestDecorator(targetTest, enlistedClients, conversationFactory, connection); + } + + + /** + * The entry point for the interop test coordinator. This client accepts the following command line arguments: + * + *

-b The broker URL. Mandatory.
-h The virtual host. + * Optional.
name=value Trailing argument define name/value pairs. Added to system properties. + * Optional.
+ * + * @param args The command line arguments. + */ + public static void main(String[] args) + { + try + { + // Use the command line parser to evaluate the command line with standard handling behaviour (print errors + // and usage then exist if there are errors). + Properties options = + CommandLineParser.processCommandLine(args, + new CommandLineParser( + new String[][] + { + {"b", "The broker URL.", "broker", "false"}, + {"h", "The virtual host to use.", "virtual host", "false"}, + {"o", "The name of the directory to output test timings to.", "dir", "false"} + })); + + // Extract the command line options. + String brokerUrl = options.getProperty("b"); + String virtualHost = options.getProperty("h"); + String reportDir = options.getProperty("o"); + reportDir = (reportDir == null) ? "." : reportDir; + + + String[] testClassNames = {SustainedTestCoordinator.class.getName()}; + + // Create a coordinator and begin its test procedure. + Coordinator coordinator = new TestCoordinator(brokerUrl, virtualHost); + + coordinator.setReportDir(reportDir); + + TestResult testResult = coordinator.start(testClassNames); + + if (testResult.failureCount() > 0) + { + System.exit(FAILURE_EXIT); + } + else + { + System.exit(SUCCESS_EXIT); + } + } + catch (Exception e) + { + System.err.println(e.getMessage()); + log.error("Top level handler caught execption.", e); + System.exit(EXCEPTION_EXIT); + } + } +} -- cgit v1.2.1 From 8c8ee073330c02ba451a7a1a57e7da7110e4d8c0 Mon Sep 17 00:00:00 2001 From: Martin Ritchie Date: Mon, 18 Jun 2007 16:29:49 +0000 Subject: Merged revisions 539476-539480,539482-539483,539485-539500,539502-539593,539595-539782,539784-539787,539789-540106,540108-540168,540170-540510,540512-541246,541248-541919,541921-542483,542485-542788,542790-543495,543497-544108,544110-544421,544423-544507,544509-546095,546097-546189,546191-546440,546442-546457,546459-547177,547179-547626,547628-548381 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/M2 ........ r539476 | ritchiem | 2007-05-18 15:12:28 +0100 (Fri, 18 May 2007) | 1 line QPID-401 Update to pom to correctly set the broker.dir ........ r544866 | rupertlssmith | 2007-06-06 16:25:02 +0100 (Wed, 06 Jun 2007) | 1 line Updated examples to build source jar. Also put the java14 retrotranslated module back in now that the strange repeating build problem is solved. ........ r545146 | ritchiem | 2007-06-07 12:30:01 +0100 (Thu, 07 Jun 2007) | 1 line POM update renumbering tests now we have trimmed down the tests being run. ........ r548276 | ritchiem | 2007-06-18 10:59:32 +0100 (Mon, 18 Jun 2007) | 1 line Various License header updates. ........ r548279 | ritchiem | 2007-06-18 11:17:20 +0100 (Mon, 18 Jun 2007) | 1 line Various License header updates. ........ r548302 | ritchiem | 2007-06-18 11:49:50 +0100 (Mon, 18 Jun 2007) | 2 lines Various License header updates. Update to PrincipalDatabase's to ensure they work correctly with # comments. ........ r548308 | ritchiem | 2007-06-18 11:58:38 +0100 (Mon, 18 Jun 2007) | 1 line Various License header updates, missed this file. Though the first update of it via the JMX console will remove the license. ........ r548312 | rupertlssmith | 2007-06-18 12:03:09 +0100 (Mon, 18 Jun 2007) | 1 line Added SLF4J to Log4J binding. ........ r548315 | ritchiem | 2007-06-18 12:07:45 +0100 (Mon, 18 Jun 2007) | 1 line Old lib dir from M1 ........ r548317 | ritchiem | 2007-06-18 12:13:57 +0100 (Mon, 18 Jun 2007) | 1 line Old lib dirs from M1 ........ r548319 | ritchiem | 2007-06-18 12:16:25 +0100 (Mon, 18 Jun 2007) | 1 line Mistakenly checked in Intelij file ........ r548381 | ritchiem | 2007-06-18 16:37:41 +0100 (Mon, 18 Jun 2007) | 1 line QPID-525 Memory leak in DestWildExchange. Used routing key in remove rather than empty queue ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@548400 13f79535-47bb-0310-9956-ffa450edef68 --- .../java/integrationtests/jar-with-dependencies.xml | 18 ++++++++++++++++++ .../qpid/interop/coordinator/XMLTestListener.java | 21 +++++++++++++++++++++ .../testcases/CoordinatingTestCase1DummyRun.java | 21 +++++++++++++++++++++ .../testcases/CoordinatingTestCase2BasicP2P.java | 21 +++++++++++++++++++++ .../testcases/CoordinatingTestCase3BasicPubSub.java | 21 +++++++++++++++++++++ .../testclient/testcases/TestCase3BasicPubSub.java | 21 +++++++++++++++++++++ .../apache/qpid/sustained/SustainedTestClient.java | 21 +++++++++++++++++++++ .../org/apache/qpid/interop/connection.properties | 18 ++++++++++++++++++ 8 files changed, 162 insertions(+) (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/jar-with-dependencies.xml b/qpid/java/integrationtests/jar-with-dependencies.xml index 62978ee864..3e95e7ab22 100644 --- a/qpid/java/integrationtests/jar-with-dependencies.xml +++ b/qpid/java/integrationtests/jar-with-dependencies.xml @@ -1,3 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.1 From 4fbd28b6078d6b3fbfe528a99d6e27963c68f99b Mon Sep 17 00:00:00 2001 From: Martin Ritchie Date: Tue, 31 Jul 2007 15:53:37 +0000 Subject: Merged revisions 1-447993,447995-448007,448009-448141,448143-448157,448161-448194,448196-448210,448212-448218,448220-448223,448225-448233,448235,448237-448241,448243-448596,448598-448623,448625-448850,448852-448880,448882-448982,448984-449635,449637-449639,449641-449642,449644-449645,449647-449674,449676-449719,449721-449749,449751-449762,449764-449933,449935-449941,449943-450383,450385,450387-450400,450402-450433,450435-450503,450505-450555,450557-450860,450862-451024,451026-451149,451151-451316,451318-451931,451933-452139,452141-452162,452164-452320,452322,452324-452325,452327-452333,452335-452429,452431-452528,452530-452545,452547-453192,453194-453195,453197-453536,453538,453540-453656,453658-454676,454678-454735,454737,454739-454781,454783-462728,462730-462819,462821-462833,462835-462839,462841-463071,463073-463178,463180-463308,463310-463362,463364-463375,463377-463396,463398-463402,463404-463409,463411-463661,463663-463670,463672-463673,463675-464493,464495-464502,464504-464576,464578-464613,464615-464628,464630,464632-464866,464868-464899,464901-464942,464944-464949,464951-465004,465006-465016,465018-465053,465055-465165,465167-465321,465323-465406,465408-465427,465429-465431,465433-465548,465550-466044,466047-466075,466077,466079-466081,466083-466099,466101-466112,466114-466126,466128-466240,466242-466971,466973-466978,466980-467309,467311-467312,467316-467328,467330-467485,467487-467588,467590-467604,467606-467699,467701-467706,467708-467749,467751-468069,468071-468537,468539-469241,469244-469246,469248-469318,469320-469421,469423,469425-469429,469431-469435,469437-469462,469464-469469,469472-469477,469479-469490,469492-469503,469505-469529,469531-469598,469600-469624,469626-469737,469739-469752,469754-469806,469808-469928,469930-469953,469955-470011,470013-470109,470111-470335,470338-470339,470341-470379,470381,470383-470399,470401-470446,470448-470741,470743-470758,470760-470809,470811-470817,470819-470993,470995-471001,471003-471788,471790-471792,471794-472028,472030-472032,472034-472036,472038,472040,472043,472045-472059,472061,472063,472065-472066,472068,472070-472072,472074-472080,472082,472084-472092,472094-472107,472109-472123,472125-472158,472160-472165,472167-472172,472174-472457,472459-472460,472462-472464,472466-472470,472472-472483,472486-472491,472493-472494,472496-472497,472499,472501-472503,472505-472512,472514-472544,472546-472556,472558-472560,472562-472572,472574-472587,472589-472591,472593-472605,472607,472609-472731,472733-472786,472788-472843,472845-472849,472851-472859,472861-472878,472880-472903,472905,472907-472988,472990-472991,472993-473071,473073-473086,473088-473090,473093,473095-473096,473098-473106,473108-473110,473112-473185,473187-473260,473262,473268-473270,473275-473279,473281,473284-473287,473289-473295,473297-473306,473308-473330,473332-473335,473337,473339-473344,473346-473351,473353-473355,473357-473358,473361-473471,473473-473497,473499-473535,473537-473567,473569-473888,473890-474451,474454-474492,474494-474563,474565-474843,474845-474865,474867-474932,474934-475035,475037-475144,475146-475180,475182-475265,475267-475285,475287,475289-475293,475295-475296,475298-475302,475304-475631,475633-475649,475651-475748,475750-475752,475754-476107,476109-476302,476304-476413,476415-476430,476432-476700,476702-476868,476870-477147,477149-477213,477215-477263,477265-477340,477342-477635,477637-477789,477791-477825,477827-477841,477843,477846-477852,477854,477856,477858-477865,477867-477894,477896-478022,478024-478182,478184-478211,478213-478233,478235-478236,478238-478241,478243-478252,478254-478259,478261-478263,478265,478267-478269,478271-478286,478288-478342,478344-478379,478381-478412,478414-478443,478445-478636,478639-478658,478660-478821,478823-478853,478855-478922,478924-478962,478965-478974,478976-479029,479031-479049,479051-479210,479212-479214,479216-479407,479409-479415,479417-479425,479427-479559,479561-479639,479641-479676,479678-479685,479687-480030,480033-480086,480091-480093,480095-480118,480120-480139,480141,480143-480148,480150-480156,480158-480163,480165-480177,480179-480189,480191-480193,480195-480198,480200-480220,480222-480282,480284-480292,480294-480308,480310-480317,480320-480422,480424,480426-480581,480583-480656,480658-480692,480695-480702,480704,480706-480710,480712-480910,480913-480933,480935-480945,480947-480972,480974-480993,480995-481034,481036-481158,481161-481174,481176-481220,481222-481234,481236-481260,481263-481264,481266-481296,481298-481304,481306-481311,481313-481332,481334,481336-481380,481382-481441,481443-482144,482146-482180,482182-482193,482195-482232,482234-482236,482239,482241-482242,482244-482247,482250-482251,482253,482256-482261,482264-482288,482290-482364,482366,482368,482370-482554,482556,482558-482569,482572-482636,482638,482640-482696,482698-482722,482724-482732,482734-482771,482774-482957,482959-483045,483047-483105,483108,483110-483115,483117,483119-483127,483130-483134,483136-483148,483150-483158,483160-483164,483166-483178,483180-483391,483393-483400,483402-483403,483405-483418,483420-483421,483425-483436,483438-483470,483472-483502,483504-483558,483560-483599,483601-483637,483639-483644,483646-483659,483661-483670,483672-483878,483880-483910,483912-483915,483917-483940,483942,483944-483968,483970-483972,483974-483976,483978,483980-484612,484614-484657,484659-484693,484695-484718,484720-484842,484844-484847,484849-484986,484988-485019,485021-485489,485491-485544,485546-485591,485593,485595-485697,485699-485729,485731-485734,485736-485779,485781-485787,485789-485851,485853,485855-486007,486009,486011-486020,486022-486083,486085-486097,486099-486117,486120-486131,486133-486148,486150-486161,486163-486164,486166-486197,486199-486205,486208-486247,486249-486253,486256-486427,486429-486431,486433-486554,486556-486573,486575-486593,486595,486597-486609,486611-486619,486622,486625,486627-486641,486643-486645,486649-486687,486689-486721,486723-486730,486732-486746,486748-486759,486761,486763-486777,486779-486782,486784-486788,486790,486792,486794-486796,486798-487175,487178,487180-487213,487215,487217-487267,487269-487284,487286-487298,487300-487358,487360-487367,487369-487382,487384-487434,487436-487480,487482-487547,487549-487561,487563-487565,487567-487578,487580-487615,487617-487622,487624,487626,487628,487630-487635,487637-487703,487705-487777,487780-487781,487783-487800,487802-487803,487805-487820,487822-487848,487850-487902,487904-488103,488105-488133,488135-488158,488160-488163,488165-488187,488189-488216,488218-488248,488250-488278,488280,488282-488303,488305-488313,488315-488342,488344-488351,488353-488376,488378-488449,488451-488593,488595,488597-488623,488625-488700,488702-488704,488706-488710,488714,488716-488725,488727-488744,488746-488770,488772-488798,488800,488802-488807,488809,488811-488829,488831-488843,488845-488851,488853-489069,489071-489077,489079-489081,489084-489102,489104-489105,489107-489109,489111-489112,489114-489139,489141-489178,489181-489203,489205-489211,489213,489216-489329,489332-489402,489404-489417,489419-489421,489423-489643,489645-489690,489692-489703,489705-489714,489716-489747,489749-489753,489755-489803,489805-489904,489906-490372,490374-490504,490506-490604,490606-490707,490710-490733,490735-490871,490873-490984,490986-491028,491030,491032-491071,491073-491119,491121-491576,491578-491672,491674-491800,491802-491838,491840-491878,491880-492183,492185-492279,492281-492317,492319-492513,492515-492584,492586-492587,492589-492601,492603-492635,492637-492640,492642-492717,492719-492723,492725-492729,492731-492755,492757-492901,492903-492955,492957-492962,492964-492997,492999-493002,493004-493041,493043-493059,493062-493063,493065-493086,493088-493125,493127-493139,493141-493150,493152-493871,493873-494017,494019-494030,494032-494041,494043-494091,494093-494120,494122-494354,494356-494436,494438-494539,494541-494552,494554-494586,494588-494649,494651,494653-494654,494656-494657,494659-494764,494766-494768,494770-494796,494798-494799,494802,494804-494860,494862-494903,494905-494906,494908-495019,495021-495160,495162-495168,495171-495188,495190-495229,495231-495254,495256-495303,495305-495313,495315-495336,495338-495372,495374-495379,495381-495454,495457-495459,495462-495516,495518-495524,495526-495531,495533-495548,495551-495553,495555,495557-495558,495560,495562-495573,495575-495583,495585-495594,495596-495628,495630-495638,495640-495651,495653-495660,495662-495753,495755-496259,496261-496262,496264-496269,496271-496275,496277-496301,496303-496316,496318-496383,496385-496413,496415-496495,496497-496625,496627-496636,496638-496640,496642-496647,496650-496657,496659-496660,496663-496664,496666-496677,496679-496681,496683-496730,496732-496750,496752,496754-496784,496786-496832,496834-496840,496842-496990,496992-496995,496997-497340,497343-497351,497353-497403,497405-497424,497426-497438,497440-497481,497483-497497,497499-497765,497767-497769,497771-497775,497777-497778,497780,497782-497783,497785,497787-497812,497814-497871,497873-497877,497879-498573,498575-498588,498590,498592,498594-498636,498638-498669,498671-498686,498688-498689,498691-498719,498721-498964,498966-498969,498971-498973,498975-498982,498985-499035,499037-499040,499042,499044-499048,499050-499082,499084-499086,499088-499164,499167-499169,499171-499355,499357-499370,499372-499373,499375-499391,499393,499395-499425,499428,499430-499445,499447-499455,499457-499460,499462-499465,499467,499469-499489,499491-499492,499494-499531,499533-499562,499566-499627,499629-499715,499717-499732,499734-499755,499758-499763,499765-499780,499782-499795,499797-499802,499804-499844,499846,499848-499850,499852-499863,499865-499873,499875-499974,499976-499978,499980-500263,500265-500283,500285-500309,500311-501000,501002,501012-501057,501059-501095,501097-501390,501392-501410,501413-501447,501449-501454,501456,501458-501464,501466-501471,501473-501803,501805-501913,501915-501916,501918-501919,501921-501944,501946-502171,502173-502177,502181,502183-502247,502250-502252,502254-502260,502262-502267,502270,502272,502274-502575,502577-502609,502611-502619,502621-502626,502628-502654,502656-503592,503594-503603,503605-503608,503610-503636,503638-503645,503647-503705,503707-503789,503791-504024,504026-504111,504113-504506,504508-504735,504737-504863,504865-504867,504869-504914,504916-505241,505243-505254,505257-505267,505269-505354,505356-505891,505893-505971,505973-506400,506402-506404,506407-506438,506440-506516,506518-506541,506543-506966,506968-506971,506973-507095,507097-507108,507111-507454,507456,507459-507471,507473-507556,507558,507560-507581,507585-507594,507597,507599-507608,507610-507728,507730-507893,507895-507937,507940-508234,508236-508350,508352-508365,508367-508380,508383,508386-508415,508417-508648,508650-508941,508943-509146,509148-509171,509173-509175,509179-509201,509203-509207,509209-509215,509217-509222,509224-509477,509480-509627,509629-509634,509636-509641,509643-509736,509738-509931,509933-510059,510061-510075,510077-510158,510161-510896,510898-510938,510940-511388,511390-511922,511924-512287,512289-512698,512702-512813,512815-512817,512819-513359,513361-513370,513372-514702,514704-514886,514888-514902,514904-515126,515129-515141,515143-515516,515518-515534,515536-515538,515540-515648,515650-515651,515653-516070,516072-516411,516413-516448,516450,516452-517637,517639-517647,517649-517659,517661-517663,517665-517677,517679-517682,517684-517744,517746-518085,518087-518175,518177-518558,518560-518568,518571-518666,518668,518670-518699,518701-518987,518990-518992,518994-519908,519910-519932,519934-520414,520416-520842,520844-520937,520939-521362,521364-521681,521683-521704,521706-521709,521711-521714,521716-521781,521783-521792,521794-522462,522464-522527,522529-522534,522536-522566,522568-522958,522960,522962-522966,522968-522976,522978-522980,522982-522988,522992-522993,522995-523244,523246-523746,523748-524049,524051-524738,524741-524742,524744-524762,524764,524766,524768-525486,525488-525530,525532,525534,525537-525552,525554-525765,525767-525776,525778-525784,525789-525803,525805-525816,525818-525828,525830-525861,525863-525866,525868-526090,526092-526112,526114-526116,526119-526121,526123-526149,526151-526153,526155-526156,526160-526165,526167-526186,526188-526193,526196-526197,526200-526665,526667-526682,526686-526690,526693,526695-526708,526710-526713,526715-526775,526777-526802,526804-526806,526808-527048,527051-527052,527054-527181,527183-527486,527488-527492,527494-527498,527500-527508,527510-527517,527519-527536,527538-527555,527559-527802,527804-527842,527844-527847,527849-527875,527877-527940,527942-527958,527960-527971,527973-528002,528004,528006-528423,528425-529232,529234-529245,529247-529296,529298-529634,529636-529658,529660-529665,529667-529668,529670-530033,530035-530036,530038-530040,530045-530046,530050-530051,530053-530431,530433-530436,530439-530440,530443,530445-530446,530448,530450-530682,530684,530687-530696,530698-530733,530735-530776,530778-530795,530799,530801-530811,530813-530818,530820-530837,530839-531436,531438-531455,531457,531459-531511,531514,531516,531519-531523,531525,531528-531858,531860-531864,531866-531907,531909-531916,531918-531936,531938-531988,531990-532001,532003-532371,532373-532465,532467-532727,532729-532765,532767-532785,532788-532790,532792-532793,532795-533064,533066-533074,533076,533080-533130,533132-533139,533142-533703,533705-533720,533722-533763,533766-533818,533820-533839,533841-533859,533862-534035,534037-534112,534114-534116,534118-534472,534474-534477,534479-534762,534764-534896,534898-534902,534904-535253,535255-535308,535310-535808,535810-535873,535875-536007,536009-536140,536142-536162,536165-536242,536244-536252,536254-536278,536280-536338,536340-536448,536450-536479,536481-536482,536484-536485,536487-536495,536497,536500-536505,536507-536561,536563-536570,536572,536574-536583,536586-536823,536825-537014,537016-537018,537020-537025,537027-537028,537030-537160,537162-537170,537172-537672,537674-537781,537783-537833,537836-537840,537842-537844,537846-537953,537955-538034,538036-538078,538080-538083,538085-538097,538099-538108,538110-538239,538241-538881,538883-538906,538908-538911,538913-538921,538923-539177,539179-539190,539192-539469,539471-539475,539477-539480,539482-539483,539485-539500,539502-539593,539595-539782,539784-539787,539789-540106,540108-540168,540170-540510,540512-541246,541248-542483,542485-542788,542790-543495,543497-544108,544110-544421,544423-544507,544509-544865,544867-545145,545147-546095,546097-546189,546191-546440,546442-546457,546459-547177,547179-547626,547628-548275,548277-548278,548280-548301,548303-548307,548309-548311,548313-548314,548316,548318,548320-548380,548382-549010,549012-549529,549531-549848,549850-550508,550510-550747,550749-550772,550774-550848,550850-551116,551122-553446,553448-561282 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/M2 ........ r541920 | tomasr | 2007-05-26 18:35:51 +0100 (Sat, 26 May 2007) | 1 line QPID-136 Initial Prefetch Implementation ........ r549112 | arnaudsimon | 2007-06-20 15:11:03 +0100 (Wed, 20 Jun 2007) | 1 line changed setText to use UTF8 as default encoder ........ r551167 | arnaudsimon | 2007-06-27 15:08:50 +0100 (Wed, 27 Jun 2007) | 1 line added public void declareAndBind(AMQDestination amqd) ........ r551174 | ritchiem | 2007-06-27 15:23:21 +0100 (Wed, 27 Jun 2007) | 3 lines Caused each of these tests to run 10 times to help identify any race conditions that were occuring. Updated the CommitRollbackTest to be more robust in the detection of failure. ........ r551175 | ritchiem | 2007-06-27 15:23:52 +0100 (Wed, 27 Jun 2007) | 1 line Allowed more of the constants to be set via system properties. ........ r551176 | ritchiem | 2007-06-27 15:25:13 +0100 (Wed, 27 Jun 2007) | 1 line renamed the passwd programme qpid-passwd to match the change in bin directory. ........ r552441 | rupertlssmith | 2007-07-02 10:23:54 +0100 (Mon, 02 Jul 2007) | 1 line Added log4j as slfj logger for perftests. ........ r552499 | rupertlssmith | 2007-07-02 15:17:45 +0100 (Mon, 02 Jul 2007) | 1 line Added some documentation. ........ r553172 | rupertlssmith | 2007-07-04 12:11:04 +0100 (Wed, 04 Jul 2007) | 1 line Messages moved by management console now commited on the message store. ........ r553248 | ritchiem | 2007-07-04 17:05:55 +0100 (Wed, 04 Jul 2007) | 6 lines Addition of the MessageStore Tool. Small changes to the Exchanges to allow the extraction of currently listed items. Extracted initial broker configuration mechanism to a reusable class. Have modified broker to use it. Move the Passwd.java to new tools package structure on the broker. ........ r553265 | ritchiem | 2007-07-04 17:42:59 +0100 (Wed, 04 Jul 2007) | 1 line Tidied up some extranious logging. ........ r553432 | rupertlssmith | 2007-07-05 10:28:33 +0100 (Thu, 05 Jul 2007) | 1 line Fixed test state carrying over to mandatory message test from immediate. Also added in-vm clean up to other tests. ........ r553480 | ritchiem | 2007-07-05 13:40:50 +0100 (Thu, 05 Jul 2007) | 2 lines Minor changes and tidy up when running via command line. Added Copy command. ........ r553482 | ritchiem | 2007-07-05 13:44:42 +0100 (Thu, 05 Jul 2007) | 2 lines Forgot to compile before committing. Missed a method change in the Select command. ........ r554964 | rupertlssmith | 2007-07-10 15:40:04 +0100 (Tue, 10 Jul 2007) | 1 line Added message copy method. ........ r555249 | rupertlssmith | 2007-07-11 12:52:39 +0100 (Wed, 11 Jul 2007) | 1 line Update perftests to center better around current performance. ........ r556011 | rupertlssmith | 2007-07-13 15:24:03 +0100 (Fri, 13 Jul 2007) | 1 line Moved test framework into its own package and cleaned it up. ........ r556024 | rupertlssmith | 2007-07-13 16:02:06 +0100 (Fri, 13 Jul 2007) | 1 line Completed javadoc for test framework. ........ r556628 | rgodfrey | 2007-07-16 14:50:57 +0100 (Mon, 16 Jul 2007) | 1 line QPID-537 : Make AMQMessage.incrementReference public ........ r556675 | cctrieloff | 2007-07-16 18:36:21 +0100 (Mon, 16 Jul 2007) | 2 lines added notice entries ........ r556680 | cctrieloff | 2007-07-16 18:56:40 +0100 (Mon, 16 Jul 2007) | 2 lines clean up ........ r556682 | cctrieloff | 2007-07-16 18:58:37 +0100 (Mon, 16 Jul 2007) | 2 lines removed optional cppunit as not in distributed packages ........ r556845 | ritchiem | 2007-07-17 09:26:33 +0100 (Tue, 17 Jul 2007) | 3 lines Additional logging in case of broker failure at startup. Use broker logger at error level as well as System.out ........ r556847 | ritchiem | 2007-07-17 09:35:35 +0100 (Tue, 17 Jul 2007) | 3 lines Update to the MessageStore Tool to provide Move and Purge functionality. Updated to remove the AMQExceptions that will be removed from the Exchange class. ........ r556861 | ritchiem | 2007-07-17 10:26:47 +0100 (Tue, 17 Jul 2007) | 2 lines QPID-538 Check to ensure a duplicate binding is not created. ........ r556868 | ritchiem | 2007-07-17 10:55:56 +0100 (Tue, 17 Jul 2007) | 1 line Addition of simple pub/sub examples. ........ r556869 | ritchiem | 2007-07-17 10:56:17 +0100 (Tue, 17 Jul 2007) | 1 line QPID-540 Prevent NPE when purging message from the main _message queue in the ConcurrentSelectorDeliveryManager that have been delivered via a Subscribers _messageQueue. Ensuring that any expired messages are still correctly handled. i.e. the Queue size/depth is reduced and the message correctly dequeued from the underlying store. ........ r556871 | ritchiem | 2007-07-17 10:57:35 +0100 (Tue, 17 Jul 2007) | 1 line White space & code formatting change ........ r556872 | ritchiem | 2007-07-17 10:58:35 +0100 (Tue, 17 Jul 2007) | 3 lines Added additional information to hard-error logging in exceptionReceived. Fully expanded imports ........ r556888 | ritchiem | 2007-07-17 12:33:08 +0100 (Tue, 17 Jul 2007) | 1 line Change to allow the management port to be specified on the command line, via -m or --mport ........ r556890 | ritchiem | 2007-07-17 12:38:10 +0100 (Tue, 17 Jul 2007) | 4 lines QPID-541 A large portion of memory was being wasted in 32k ByteBuffers being held by the AMQShortStrings. Patch submitted by Robert Godfrey to intern() the AMQSSs to reduce memory usage. Current implementation *will* impact performance due to the usage of a static Map for storage. However, a thread local implementation is in the works. ........ r556898 | rgodfrey | 2007-07-17 13:00:57 +0100 (Tue, 17 Jul 2007) | 1 line QPID-541 : Change to use threadlocal maps for intern for the common case to avoid excessive synchronization. In the uncommon case will require more lookup. ........ r556958 | rupertlssmith | 2007-07-17 17:22:16 +0100 (Tue, 17 Jul 2007) | 1 line Refactored the distributed test clients and coordinator to support different distribution and sequencing engines. ........ r556967 | rupertlssmith | 2007-07-17 17:40:14 +0100 (Tue, 17 Jul 2007) | 1 line Removed unused package. ........ r556968 | rupertlssmith | 2007-07-17 17:42:00 +0100 (Tue, 17 Jul 2007) | 1 line Retired old interop tests. ........ r556969 | rupertlssmith | 2007-07-17 17:43:49 +0100 (Tue, 17 Jul 2007) | 1 line Properties file not needed any more. Test properties all driven from MessagingTestConfigProperties. ........ r557276 | ritchiem | 2007-07-18 15:36:11 +0100 (Wed, 18 Jul 2007) | 1 line Updates to pom files and Licensing/Notice files for M2 release. ........ r557279 | ritchiem | 2007-07-18 15:51:42 +0100 (Wed, 18 Jul 2007) | 1 line This is left over from ANT ........ r557281 | ritchiem | 2007-07-18 15:54:06 +0100 (Wed, 18 Jul 2007) | 1 line updated comment to refelect property values ........ r557286 | ritchiem | 2007-07-18 16:02:22 +0100 (Wed, 18 Jul 2007) | 1 line Set default mvn build to assembly:assembly ........ r557288 | ritchiem | 2007-07-18 16:09:07 +0100 (Wed, 18 Jul 2007) | 1 line Ensure the top level release-docs directory is included in the builds ........ r557306 | ritchiem | 2007-07-18 17:01:58 +0100 (Wed, 18 Jul 2007) | 1 line Update fix incorrect license headers. ........ r557312 | ritchiem | 2007-07-18 17:07:01 +0100 (Wed, 18 Jul 2007) | 1 line added license ........ r557314 | ritchiem | 2007-07-18 17:11:17 +0100 (Wed, 18 Jul 2007) | 1 line added license ........ r557452 | aconway | 2007-07-19 03:03:02 +0100 (Thu, 19 Jul 2007) | 14 lines * lib/broker/Daemon.cpp, .h - Rewrote to remove libdaemon dependency. - PID file stored in /var/run if root, /tmp otherwise. * src/qpidd.cpp: Use new Daemon.cpp. - lock files stored in /var/run (for root) or /tmp. - updated to trunk daemon flag behavior. * lib/broker/Makefile.am (libqpidbroker_la_LIBADD): - Daemon.cpp now needs -lboost_iostreams * NOTICE, README: Removed mention of libdaemon. ........ r558027 | ritchiem | 2007-07-20 17:08:05 +0100 (Fri, 20 Jul 2007) | 1 line Added a logger but only used to control the toString inclusion of password. If in debug mode it will include password otherwise the password is "********". ........ r558072 | astitcher | 2007-07-20 18:49:41 +0100 (Fri, 20 Jul 2007) | 2 lines Fixed the license from the "old" apache copyright notice ........ r558083 | aconway | 2007-07-20 19:29:08 +0100 (Fri, 20 Jul 2007) | 2 lines Remove -ldaemon, we no longer require libdaemon. ........ r558099 | aconway | 2007-07-20 20:20:01 +0100 (Fri, 20 Jul 2007) | 2 lines Ignore QPID_ env variables that don't correspond to known options. ........ r558108 | cctrieloff | 2007-07-20 20:55:40 +0100 (Fri, 20 Jul 2007) | 2 lines typo fix ........ r558114 | rajith | 2007-07-20 21:11:03 +0100 (Fri, 20 Jul 2007) | 1 line added release notes ........ r558115 | rajith | 2007-07-20 21:12:20 +0100 (Fri, 20 Jul 2007) | 1 line Checking in the release notes ........ r558116 | aconway | 2007-07-20 21:16:20 +0100 (Fri, 20 Jul 2007) | 3 lines Removed .txt from RELEASE_NOTES Added RELEASE_NOTES to EXTRA_DIST in Makefile.am ........ r558168 | rajith | 2007-07-20 23:03:42 +0100 (Fri, 20 Jul 2007) | 1 line added release notes ........ r558170 | rajith | 2007-07-20 23:04:11 +0100 (Fri, 20 Jul 2007) | 1 line added release notes ........ r558630 | gsim | 2007-07-23 08:21:49 +0100 (Mon, 23 Jul 2007) | 3 lines Revised release notes: removed bug fixed on this branch, removed outstanding feature lists as it is not terribly accurate or helpful. ........ r559419 | rupertlssmith | 2007-07-25 13:17:59 +0100 (Wed, 25 Jul 2007) | 1 line Refactored interop tests into general distributed test framework. Moved framework under systests from integrationtests. ........ r559427 | ritchiem | 2007-07-25 13:40:24 +0100 (Wed, 25 Jul 2007) | 2 lines AMQMessage - added //todo-s and removed unused parameter StoreContext from expired() method call. ConcurrentSelectorDeliveryManager - Update to reflect expired() call change. Created new _reaperContextStore to be used when performing reaper operations such as message dequeue due to expiration. Removed old commented code. ........ r559455 | rupertlssmith | 2007-07-25 14:40:16 +0100 (Wed, 25 Jul 2007) | 1 line Added to comments. ........ r559456 | rupertlssmith | 2007-07-25 14:41:21 +0100 (Wed, 25 Jul 2007) | 1 line Removed redundant method. ........ r559458 | rupertlssmith | 2007-07-25 14:57:21 +0100 (Wed, 25 Jul 2007) | 1 line Refactored packaging of test framework. ........ r559461 | rupertlssmith | 2007-07-25 15:00:16 +0100 (Wed, 25 Jul 2007) | 1 line Removed redundant packages. ........ r559943 | rhs | 2007-07-26 20:15:17 +0100 (Thu, 26 Jul 2007) | 1 line adding missing ack ........ r559944 | rhs | 2007-07-26 20:15:44 +0100 (Thu, 26 Jul 2007) | 1 line removed old script ........ r560198 | ritchiem | 2007-07-27 12:30:34 +0100 (Fri, 27 Jul 2007) | 1 line Added files to ignore list ........ r560225 | ritchiem | 2007-07-27 14:33:50 +0100 (Fri, 27 Jul 2007) | 1 line Converted namespaces from Qpid.* to Apache.Qpid.* ........ r560471 | tomasr | 2007-07-28 03:35:41 +0100 (Sat, 28 Jul 2007) | 1 line Removed using directives causing compilation failure in .NET 1.1 ........ r561278 | ritchiem | 2007-07-31 10:07:57 +0100 (Tue, 31 Jul 2007) | 8 lines Changes to POMs. Client pom now builds a single jar with all dependancies included in the single bundle. systests/pom.xml adjusted to include only *Test.class items. This will fix the current Error on OptOutTestCase management/eclipse-plugin/pom.xml - editied to include there required MANIFEST.MF to identify plugin to eclipse distribution/src/main/assembly/management-eclipse-plugin.xml editied to include there required MANIFEST.MF to identify the plugin distribution/pom.xml - white space Also updated log4j.xml default to create an alert.log file from the AMQQueue alerting. Added a debug.log4j.xml that gives example of debugging the broker via log4j. ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@561365 13f79535-47bb-0310-9956-ffa450edef68 --- .../docs/RunningSustainedTests.txt | 14 +- qpid/java/integrationtests/pom.xml | 6 +- .../interop/clienttestcases/TestCase1DummyRun.java | 133 +++ .../interop/clienttestcases/TestCase2BasicP2P.java | 207 +++++ .../clienttestcases/TestCase3BasicPubSub.java | 237 ++++++ .../interop/coordinator/CoordinatingTestCase.java | 263 ------ .../qpid/interop/coordinator/Coordinator.java | 388 --------- .../interop/coordinator/InvitingTestDecorator.java | 220 ----- .../coordinator/ListeningCoordinatorTest.java | 28 - .../coordinator/ListeningTestDecorator.java | 200 ----- .../qpid/interop/coordinator/OptOutTestCase.java | 65 -- .../interop/coordinator/TestClientDetails.java | 87 -- .../qpid/interop/coordinator/XMLTestListener.java | 402 --------- .../testcases/CoordinatingTestCase1DummyRun.java | 85 -- .../testcases/CoordinatingTestCase2BasicP2P.java | 90 -- .../CoordinatingTestCase3BasicPubSub.java | 92 --- .../java/org/apache/qpid/interop/old/Listener.java | 291 ------- .../org/apache/qpid/interop/old/Publisher.java | 244 ------ .../qpid/interop/testcases/CircuitTestCase.java | 101 +++ .../testcases/InteropTestCase1DummyRun.java | 84 ++ .../testcases/InteropTestCase2BasicP2P.java | 90 ++ .../testcases/InteropTestCase3BasicPubSub.java | 88 ++ .../interop/testclient/InteropClientTestCase.java | 104 --- .../apache/qpid/interop/testclient/TestClient.java | 422 ---------- .../testclient/testcases/TestCase1DummyRun.java | 96 --- .../testclient/testcases/TestCase2BasicP2P.java | 214 ----- .../testclient/testcases/TestCase3BasicPubSub.java | 249 ------ .../qpid/sustained/SustainedClientTestCase.java | 905 +++++++++++++++++++++ .../apache/qpid/sustained/SustainedTestCase.java | 126 +++ .../apache/qpid/sustained/SustainedTestClient.java | 8 +- .../qpid/sustained/SustainedTestCoordinator.java | 222 ----- .../java/org/apache/qpid/sustained/TestClient.java | 157 ---- .../org/apache/qpid/sustained/TestCoordinator.java | 117 --- .../distributedtesting/InteropClientTestCase.java | 101 +++ .../framework/distributedtesting/TestClient.java | 377 +++++++++ .../org/apache/qpid/util/ClasspathScanner.java | 234 ------ .../org/apache/qpid/util/ConversationFactory.java | 479 ----------- .../org/apache/qpid/interop/connection.properties | 20 - 38 files changed, 2466 insertions(+), 4780 deletions(-) create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningCoordinatorTest.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningTestDecorator.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/XMLTestListener.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase1DummyRun.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase3BasicPubSub.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/CircuitTestCase.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCoordinator.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestClient.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestCoordinator.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropClientTestCase.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationFactory.java delete mode 100644 qpid/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/docs/RunningSustainedTests.txt b/qpid/java/integrationtests/docs/RunningSustainedTests.txt index 2b37f4c5a7..db4405a32d 100644 --- a/qpid/java/integrationtests/docs/RunningSustainedTests.txt +++ b/qpid/java/integrationtests/docs/RunningSustainedTests.txt @@ -1,15 +1,17 @@ In addition to the integration tests the framework provided by this package also allows for sustained tests to be run. Currently avaible tests: -- org.apache.qpid.sustained.SustainedTestClient : Pub Sub test to determine steady state throughput. +- org.apache.qpid.sustained.SustainedClientTestCase : Pub Sub test to determine steady state throughput. Running Tests. Run the tests as per the integration tests. - - Start a broker - - Start at least one Client [java org.apache.qpid.sustained.TestClient], ensuring unique naming - - Start Test Controller [java org.apache.qpid.sustained.TestCoordinator] - - Additional Test clients can be started: - [java org.apache.qpid.sustained.TestClient -j org.apache.qpid.sustained.SustainedTestClient] +- Start a broker +- Start at least one test client [java org.apache.qpid.interop.TestClient], ensuring unique naming. + +- Start the test coordinator with the 'fanout' engine, on the sustained test case [java org.apache.qpid.test.framework.distributedtesting.Coordinator] + +- Additional Test clients can be started and joined into the running test: [java org.apache.qpid.interop.TestClient -j] + diff --git a/qpid/java/integrationtests/pom.xml b/qpid/java/integrationtests/pom.xml index 9ccd153f54..89fd5ede28 100644 --- a/qpid/java/integrationtests/pom.xml +++ b/qpid/java/integrationtests/pom.xml @@ -40,12 +40,16 @@ - org.apache.qpid qpid-client + + org.apache.qpid + qpid-systests + + org.slf4j slf4j-log4j12 diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java new file mode 100644 index 0000000000..b119d13a3d --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.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.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.distributedtesting.InteropClientTestCase; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; + +/** + * 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. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Perform test case actions. + *
Generate test reports. + *
+ */ +public class TestCase1DummyRun implements InteropClientTestCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase1DummyRun.class); + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC1_DummyRun"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage): called"); + + // Test parameters don't matter, accept all invites. + 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 receivers. + * @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 + { + log.debug("public void assignRole(Roles role, Message assignRoleMessage): called"); + + // Do nothing, both roles are the same. + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + */ + public void start() + { + log.debug("public void start(): called"); + + // Do nothing. + } + + /** + * 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 + { + log.debug("public Message getReport(Session session): called"); + + // Generate a dummy report, the coordinator expects a report but doesn't care what it is. + return session.createTextMessage("Dummy Run, Ok."); + } + + /** + * Handles incoming test messages. Does nothing. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Ignore any messages. + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java new file mode 100644 index 0000000000..080bd846ee --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java @@ -0,0 +1,207 @@ +/* + * + * 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.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.distributedtesting.InteropClientTestCase; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.TestUtils; + +import javax.jms.*; + +/** + * 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. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages. + *
Generate test reports. + *
+ */ +public class TestCase2BasicP2P implements InteropClientTestCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase2BasicP2P.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The connection to send the test messages on. */ + private Connection connection; + + /** The session to send the test messages on. */ + private Session session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC2_BasicP2P"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // 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 receivers. + * + * @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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Create a new connection to pass the test messages on. + connection = TestUtils.createConnection(TestClient.testContextProperties); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); + Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); + + log.debug("numMessages = " + numMessages); + log.debug("sendDestination = " + sendDestination); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a message producer if so. + case SENDER: + producer = session.createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages. + case RECEIVER: + MessageConsumer consumer = session.createConsumer(sendDestination); + consumer.setMessageListener(this); + break; + } + + connection.start(); + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start() throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = session.createTextMessage("test"); + + for (int i = 0; i < numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * 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 + { + log.debug("public Message getReport(Session session): called"); + + // Close the test connection. + connection.close(); + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java new file mode 100644 index 0000000000..a11d045e89 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java @@ -0,0 +1,237 @@ +/* + * + * 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.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.distributedtesting.InteropClientTestCase; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.TestUtils; + +import javax.jms.*; + +/** + * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the + * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of + * messages sent/received. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages using pub/sub. + *
Generate test reports. + *
+ */ +public class TestCase3BasicPubSub implements InteropClientTestCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase3BasicPubSub.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC3_BasicPubSub"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // 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 receivers. + * + * @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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); + int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); + String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); + + log.debug("numMessages = " + numMessages); + log.debug("numReceivers = " + numReceivers); + log.debug("sendKey = " + sendKey); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = TestUtils.createConnection(TestClient.testContextProperties); + session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + Destination sendDestination = session[0].createTopic(sendKey); + + producer = session[0].createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number + // of receivers connections. + case RECEIVER: + // Create the required number of receivers connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = TestUtils.createConnection(TestClient.testContextProperties); + session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); + + sendDestination = session[i].createTopic(sendKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + consumer.setMessageListener(this); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (Connection conn : connection) + { + conn.start(); + } + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start() throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = session[0].createTextMessage("test"); + + for (int i = 0; i < numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * 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 + { + log.debug("public Message getReport(Session session): called"); + + // Close the test connections. + for (Connection conn : connection) + { + conn.close(); + } + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java deleted file mode 100644 index d2042be741..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * - * 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 junit.framework.TestCase; - -import org.apache.log4j.Logger; - -import org.apache.qpid.util.ConversationFactory; - -import javax.jms.*; - -import java.util.Map; - -/** - * A 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). - * - *

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: - * - *

- * 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.");
- *   }
- * }
- *
- * 
- * - *

- *
CRC Card
Responsibilities Collaborations - *
Accept notification of test case participants. {@link InvitingTestDecorator} - *
Accpet JMS Connection to carry out the coordination over. - *
Coordinate the test sequence amongst participants. {@link ConversationFactory} - *
Supply test properties - *
- */ -public abstract class CoordinatingTestCase extends TestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(CoordinatingTestCase.class); - - /** Holds the contact details for the sending test client. */ - protected TestClientDetails sender; - - /** Holds the contact details for the receving test client. */ - protected TestClientDetails receiver; - - /** Holds the conversation factory over which to coordinate the test. */ - protected ConversationFactory conversationFactory; - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public CoordinatingTestCase(String name) - { - super(name); - } - - /** - * Sets the sender test client to coordinate the test with. - * - * @param sender The contact details of the sending client in the test. - */ - public void setSender(TestClientDetails sender) - { - log.debug("public void setSender(TestClientDetails sender = " + sender + "): called"); - - this.sender = sender; - } - - /** - * Sets the receiving test client to coordinate the test with. - * - * @param receiver The contact details of the sending client in the test. - */ - public void setReceiver(TestClientDetails receiver) - { - log.debug("public void setReceiver(TestClientDetails receiver = " + receiver + "): called"); - - this.receiver = receiver; - } - - /** - * Supplies the sending test client. - * - * @return The sending test client. - */ - public TestClientDetails getSender() - { - return sender; - } - - /** - * Supplies the receiving test client. - * - * @return The receiving test client. - */ - public TestClientDetails getReceiver() - { - return receiver; - } - - /** - * Returns the name of the current test method of this test class, with the sending and receiving client names - * appended on to it, so that the resulting name unqiuely identifies the test and the clients that participated - * in it. - * - * @return The unique test and client name. - */ - public String getName() - { - if ((sender == null) || (receiver == null)) - { - return super.getName(); - } - else - { - return super.getName() + "_sender_" + sender.clientName + "_receiver_" + receiver.clientName; - } - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * - * @return The name of the corresponding interop test case. - */ - public abstract String getTestCaseNameForTestMethod(String methodName); - - /** - * Accepts the conversation factory over which to hold the test coordinating conversation. - * - * @param conversationFactory The conversation factory to coordinate the test over. - */ - public void setConversationFactory(ConversationFactory conversationFactory) - { - this.conversationFactory = conversationFactory; - } - - /** - * 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 assigning the test roles, begining the test and gathering the test reports - * from the participants. - * - * @param testProperties The test case definition. - * - * @return The test results from the senders and receivers. - * - * @throws JMSException All underlying JMSExceptions are allowed to fall through. - */ - protected Message[] sequenceTest(Map testProperties) throws JMSException - { - log.debug("protected Message[] sequenceTest(Object... testProperties = " + testProperties + "): called"); - - Session session = conversationFactory.getSession(); - Destination senderControlTopic = session.createTopic(sender.privateControlKey); - Destination receiverControlTopic = session.createTopic(receiver.privateControlKey); - - ConversationFactory.Conversation senderConversation = conversationFactory.startConversation(); - ConversationFactory.Conversation receiverConversation = conversationFactory.startConversation(); - - // Assign the sender role to the sending test client. - Message assignSender = conversationFactory.getSession().createMessage(); - setPropertiesOnMessage(assignSender, testProperties); - assignSender.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); - assignSender.setStringProperty("ROLE", "SENDER"); - - senderConversation.send(senderControlTopic, assignSender); - - // Assign the receiver role the receiving client. - Message assignReceiver = session.createMessage(); - setPropertiesOnMessage(assignReceiver, testProperties); - assignReceiver.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); - assignReceiver.setStringProperty("ROLE", "RECEIVER"); - - receiverConversation.send(receiverControlTopic, assignReceiver); - - // Wait for the senders and receivers to confirm their roles. - senderConversation.receive(); - receiverConversation.receive(); - - // Start the test. - Message start = session.createMessage(); - start.setStringProperty("CONTROL_TYPE", "START"); - - senderConversation.send(senderControlTopic, start); - - // Wait for the test sender to return its report. - Message senderReport = senderConversation.receive(); - - try - { - Thread.sleep(500); - } - catch (InterruptedException e) - { } - - // Ask the receiver for its report. - Message statusRequest = session.createMessage(); - statusRequest.setStringProperty("CONTROL_TYPE", "STATUS_REQUEST"); - - receiverConversation.send(receiverControlTopic, statusRequest); - - // Wait for the receiver to send its report. - Message receiverReport = receiverConversation.receive(); - - return new Message[] { senderReport, receiverReport }; - } - - /** - * Sets properties of different types on a JMS Message. - * - * @param message The message to set properties on. - * @param properties The property name/value pairs to set. - * - * @throws JMSException All underlying JMSExceptions are allowed to fall through. - */ - public void setPropertiesOnMessage(Message message, Map properties) throws JMSException - { - for (Map.Entry entry : properties.entrySet()) - { - String name = entry.getKey(); - Object value = entry.getValue(); - - message.setObjectProperty(name, value); - } - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java deleted file mode 100644 index 6eec20769a..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * - * 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.io.*; -import java.util.*; -import java.util.concurrent.LinkedBlockingQueue; -import javax.jms.*; -import junit.framework.Test; -import junit.framework.TestResult; -import junit.framework.TestSuite; -import org.apache.log4j.Logger; -import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase1DummyRun; -import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase2BasicP2P; -import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase3BasicPubSub; -import org.apache.qpid.interop.testclient.TestClient; -import org.apache.qpid.util.CommandLineParser; -import org.apache.qpid.util.ConversationFactory; -import org.apache.qpid.util.PrettyPrintingUtils; -import uk.co.thebadgerset.junit.extensions.TKTestResult; -import uk.co.thebadgerset.junit.extensions.TKTestRunner; -import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; -import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; - -/** - *

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. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Find out what test clients are available. {@link ConversationFactory} - *
Decorate available tests to run all available clients. {@link InvitingTestDecorator} - *
Attach XML test result logger. - *
Terminate the interop testing framework. - *
- */ -public class Coordinator extends TKTestRunner -{ - private static final Logger log = Logger.getLogger(Coordinator.class); - - public static final String DEFAULT_CONNECTION_PROPS_RESOURCE = "org/apache/qpid/interop/connection.properties"; - - /** Holds the URL of the broker to coordinate the tests on. */ - protected String brokerUrl; - - /** Holds the virtual host to coordinate the tests on. If null, then the default virtual host is used. */ - protected String virtualHost; - - /** Holds the list of all clients that enlisted, when the compulsory invite was issued. */ - protected Set enlistedClients = new HashSet(); - - /** Holds the conversation helper for the control conversation. */ - protected ConversationFactory conversationFactory; - - /** Holds the connection that the coordinating messages are sent over. */ - protected Connection connection; - - /** - * Holds the name of the class of the test currently being run. Ideally passed into the {@link #createTestResult} - * method, but as the signature is already fixed for this, the current value gets pushed here as a member variable. - */ - protected String currentTestClassName; - - /** Holds the path of the directory to output test results too, if one is defined. */ - protected static String _reportDir; - - /** - * 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 null. - */ - public Coordinator(String brokerUrl, String virtualHost) - { - log.debug("Coordinator(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost + "): called"); - - // 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: - * - *

- *
-b The broker URL. Mandatory. - *
-h The virtual host. Optional. - *
name=value Trailing argument define name/value pairs. Added to system properties. Optional. - *
- * - * @param args The command line arguments. - */ - public static void main(String[] args) - { - try - { - // Use the command line parser to evaluate the command line with standard handling behaviour (print errors - // and usage then exit if there are errors). - Properties options = - CommandLineParser.processCommandLine(args, - new CommandLineParser( - new String[][] - { - {"b", "The broker URL.", "broker", "false"}, - {"h", "The virtual host to use.", "virtual host", "false"}, - {"o", "The name of the directory to output test timings to.", "dir", "false"} - })); - - // Extract the command line options. - String brokerUrl = options.getProperty("b"); - String virtualHost = options.getProperty("h"); - _reportDir = options.getProperty("o"); - _reportDir = (_reportDir == null) ? "." : _reportDir; - - // Scan for available test cases using a classpath scanner. - Collection> testCaseClasses = - new ArrayList>(); - // ClasspathScanner.getMatches(CoordinatingTestCase.class, "^Test.*", true); - // Hard code the test classes till the classpath scanner is fixed. - Collections.addAll(testCaseClasses, - CoordinatingTestCase1DummyRun.class, - CoordinatingTestCase2BasicP2P.class, - CoordinatingTestCase3BasicPubSub.class); - - // Check that some test classes were actually found. - if (testCaseClasses.isEmpty()) - { - throw new RuntimeException( - "No test classes implementing CoordinatingTestCase were found on the class path."); - } - - int i = 0; - String[] testClassNames = new String[testCaseClasses.size()]; - - for (Class testClass : testCaseClasses) - { - testClassNames[i++] = testClass.getName(); - } - - // Create a coordinator and begin its test procedure. - Coordinator coordinator = new Coordinator(brokerUrl, virtualHost); - - boolean failure = false; - - TestResult testResult = coordinator.start(testClassNames); - - if (failure) - { - System.exit(FAILURE_EXIT); - } - else - { - System.exit(SUCCESS_EXIT); - } - } - catch (Exception e) - { - System.err.println(e.getMessage()); - log.error("Top level handler caught execption.", e); - System.exit(EXCEPTION_EXIT); - } - } - - /** - * Starts all of the test classes to be run by this coordinator running. - * - * @param testClassNames An array of all the coordinating test case implementations. - * - * @return A JUnit TestResult to run the tests with. - * - * @throws Exception Any underlying exceptions are allowed to fall through, and fail the test process. - */ - public TestResult start(String[] testClassNames) throws Exception - { - log.debug("public TestResult start(String[] testClassNames = " + PrettyPrintingUtils.printArray(testClassNames) - + ": called"); - - // Connect to the broker. - connection = TestClient.createConnection(DEFAULT_CONNECTION_PROPS_RESOURCE, brokerUrl, virtualHost); - Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - Destination controlTopic = session.createTopic("iop.control"); - Destination responseQueue = session.createQueue("coordinator"); - - conversationFactory = new ConversationFactory(connection, responseQueue, LinkedBlockingQueue.class); - ConversationFactory.Conversation conversation = conversationFactory.startConversation(); - - connection.start(); - - // Broadcast the compulsory invitation to find out what clients are available to test. - Message invite = session.createMessage(); - invite.setStringProperty("CONTROL_TYPE", "INVITE"); - invite.setJMSReplyTo(responseQueue); - - conversation.send(controlTopic, invite); - - // Wait for a short time, to give test clients an opportunity to reply to the invitation. - Collection enlists = conversation.receiveAll(0, 3000); - - enlistedClients = extractEnlists(enlists); - - // Run the test in the suite using JUnit. - TestResult result = null; - - for (String testClassName : testClassNames) - { - // Record the current test class, so that the test results can be output to a file incorporating this name. - this.currentTestClassName = testClassName; - - result = super.start(new String[]{testClassName}); - } - - // At this point in time, all tests have completed. Broadcast the shutdown message. - Message terminate = session.createMessage(); - terminate.setStringProperty("CONTROL_TYPE", "TERMINATE"); - - conversation.send(controlTopic, terminate); - - return result; - } - - /** - * For a collection of enlist messages, this method pulls out of the client details for the enlisting clients. - * - * @param enlists The enlist messages. - * - * @return A set of enlisting clients, extracted from the enlist messages. - * - * @throws JMSException Any underlying JMSException is allowed to fall through. - */ - public static Set extractEnlists(Collection enlists) throws JMSException - { - log.debug("public static Set extractEnlists(Collection enlists = " + enlists - + "): called"); - - Set enlistedClients = new HashSet(); - - // Retain the list of all available clients. - for (Message enlist : enlists) - { - TestClientDetails clientDetails = new TestClientDetails(); - clientDetails.clientName = enlist.getStringProperty("CLIENT_NAME"); - clientDetails.privateControlKey = enlist.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); - - enlistedClients.add(clientDetails); - } - - return enlistedClients; - } - - /** - * 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 coordinators ability to invite test clients to participate in - * tests. - * - * @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) - { - log.debug("public TestResult doRun(Test \"" + test + "\", boolean " + wait + "): called"); - - // Wrap all tests in the test suite with WrappedSuiteTestDecorators. This is quite ugly and a bit baffling, - // but the reason it is done is because the JUnit implementation of TestDecorator has some bugs in it. - WrappedSuiteTestDecorator targetTest = null; - - if (test instanceof TestSuite) - { - log.debug("targetTest is a TestSuite"); - - TestSuite suite = (TestSuite) test; - - int numTests = suite.countTestCases(); - log.debug("There are " + numTests + " in the suite."); - - for (int i = 0; i < numTests; i++) - { - Test nextTest = suite.testAt(i); - log.debug("suite.testAt(" + i + ") = " + nextTest); - - if (nextTest instanceof CoordinatingTestCase) - { - log.debug("nextTest is a CoordinatingTestCase"); - } - } - - targetTest = new WrappedSuiteTestDecorator(suite); - log.debug("Wrapped with a WrappedSuiteTestDecorator."); - } - // Wrap the tests in an inviting test decorator, to perform the invite/test cycle. - - targetTest = newTestDecorator(targetTest, enlistedClients, conversationFactory, connection); - - TestSuite suite = new TestSuite(); - suite.addTest(targetTest); - - // Wrap the tests in a scaled test decorator to them them as a 'batch' in one thread. - // targetTest = new ScaledTestDecorator(targetTest, new int[] { 1 }); - - return super.doRun(suite, wait); - } - - protected WrappedSuiteTestDecorator newTestDecorator(WrappedSuiteTestDecorator targetTest, Set enlistedClients, ConversationFactory conversationFactory, Connection connection) - { - return new InvitingTestDecorator(targetTest, enlistedClients, conversationFactory, connection); - } - - /** - * Creates the TestResult object to be used for test runs. - * - * @return An instance of the test result object. - */ - protected TestResult createTestResult() - { - log.debug("protected TestResult createTestResult(): called"); - - TKTestResult result = new TKTestResult(fPrinter.getWriter(), delay, verbose, testCaseName); - - // Check if a directory to output reports to has been specified and attach test listeners if so. - if (_reportDir != null) - { - // Create the report directory if it does not already exist. - File reportDirFile = new File(_reportDir); - - if (!reportDirFile.exists()) - { - reportDirFile.mkdir(); - } - - // Create the timings file (make the name of this configurable as a command line parameter). - Writer timingsWriter = null; - - try - { - File timingsFile = new File(reportDirFile, "TEST." + currentTestClassName + ".xml"); - timingsWriter = new BufferedWriter(new FileWriter(timingsFile), 20000); - } - catch (IOException e) - { - throw new RuntimeException("Unable to create the log file to write test results to: " + e, e); - } - - // Set up a CSV results listener to output the timings to the results file. - XMLTestListener listener = new XMLTestListener(timingsWriter, currentTestClassName); - result.addListener(listener); - result.addTKTestListener(listener); - - // Register the results listeners shutdown hook to flush its data if the test framework is shutdown - // prematurely. - // registerShutdownHook(listener); - - // Record the start time of the batch. - // result.notifyStartBatch(); - - // At this point in time the test class has been instantiated, giving it an opportunity to read its parameters. - // Inform any test listers of the test properties. - result.notifyTestProperties(TestContextProperties.getAccessedProps()); - } - - return result; - } - - public void setReportDir(String reportDir) - { - _reportDir = reportDir; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java deleted file mode 100644 index 8695f7f66f..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * - * 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.*; - -import javax.jms.Connection; -import javax.jms.Destination; -import javax.jms.JMSException; -import javax.jms.Message; - -import junit.framework.Test; -import junit.framework.TestResult; - -import org.apache.log4j.Logger; - -import org.apache.qpid.util.ConversationFactory; - -import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; - -/** - *

- *
CRC Card
Responsibilities Collaborations - *
Broadcast test invitations and collect enlists. {@link ConversationFactory}. - *
Output test failures for clients unwilling to run the test case. {@link Coordinator} - *
Execute coordinated test cases. {@link CoordinatingTestCase} - *
- */ -public class InvitingTestDecorator extends WrappedSuiteTestDecorator -{ - private static final Logger log = Logger.getLogger(InvitingTestDecorator.class); - - /** Holds the contact information for all test clients that are available and that may take part in the test. */ - Set allClients; - - /** Holds the conversation helper for the control level conversation for coordinating the test through. */ - ConversationFactory conversationFactory; - - /** Holds the connection that the control conversation is held over. */ - Connection connection; - - /** Holds the underlying {@link CoordinatingTestCase}s that this decorator wraps. */ - WrappedSuiteTestDecorator testSuite; - - /** - * Creates a wrapped suite test decorator from another one. - * - * @param suite The test suite. - * @param availableClients The list of all clients that responded to the compulsory invite. - * @param controlConversation The conversation helper for the control level, test coordination conversation. - * @param controlConnection The connection that the coordination messages are sent over. - */ - public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set availableClients, - ConversationFactory controlConversation, Connection controlConnection) - { - super(suite); - - log.debug("public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set allClients = " - + availableClients + ", ConversationHelper controlConversation = " + controlConversation + "): called"); - - testSuite = suite; - allClients = availableClients; - conversationFactory = controlConversation; - connection = controlConnection; - } - - /** - * Broadcasts a test invitation and accetps enlisting from participating clients. The wrapped test case is - * then repeated for every combination of test clients (provided the wrapped test case extends - * {@link CoordinatingTestCase}. - * - *

Any JMSExceptions during the invite/enlist conversation will be allowed to fall through as runtime exceptions, - * resulting in the non-completion of the test run. - * - * @todo Better error recovery for failure of the invite/enlist conversation could be added. - * - * @param testResult The the results object to monitor the test results with. - */ - public void run(TestResult testResult) - { - log.debug("public void run(TestResult testResult): called"); - - Collection tests = testSuite.getAllUnderlyingTests(); - - for (Test test : tests) - { - CoordinatingTestCase coordTest = (CoordinatingTestCase) test; - - // Broadcast the invitation to find out what clients are available to test. - Set enlists; - try - { - Message invite = conversationFactory.getSession().createMessage(); - Destination controlTopic = conversationFactory.getSession().createTopic("iop.control"); - ConversationFactory.Conversation conversation = conversationFactory.startConversation(); - - invite.setStringProperty("CONTROL_TYPE", "INVITE"); - invite.setStringProperty("TEST_NAME", coordTest.getTestCaseNameForTestMethod(coordTest.getName())); - - conversation.send(controlTopic, invite); - - // Wait for a short time, to give test clients an opportunity to reply to the invitation. - Collection replies = conversation.receiveAll(allClients.size(), 3000); - enlists = Coordinator.extractEnlists(replies); - } - catch (JMSException e) - { - throw new RuntimeException("There was a JMSException during the invite/enlist conversation.", e); - } - - // Compare the list of willing clients to the list of all available. - Set optOuts = new HashSet(allClients); - optOuts.removeAll(enlists); - - // Output test failures for clients that will not particpate in the test. - Set> failPairs = allPairs(optOuts, allClients); - - for (List failPair : failPairs) - { - CoordinatingTestCase failTest = new OptOutTestCase("testOptOut"); - failTest.setSender(failPair.get(0)); - failTest.setReceiver(failPair.get(1)); - - failTest.run(testResult); - } - - // Loop over all combinations of clients, willing to run the test. - Set> enlistedPairs = allPairs(enlists, enlists); - - for (List enlistedPair : enlistedPairs) - { - // Set the sending and receiving client details on the test case. - coordTest.setSender(enlistedPair.get(0)); - coordTest.setReceiver(enlistedPair.get(1)); - - // Pass down the connection to hold the coordination conversation over. - coordTest.setConversationFactory(conversationFactory); - - // Execute the test case. - coordTest.run(testResult); - } - } - } - - /** - * Prints a string summarizing this test decorator, mainly for debugging purposes. - * - * @return String representation for debugging purposes. - */ - public String toString() - { - return "InvitingTestDecorator: [ testSuite = " + testSuite + " ]"; - } - - /** - * Produces all pairs of combinations of elements from two sets. The ordering of the elements in the pair is - * important, that is the pair is distinct from ; both pairs are generated. For any element, i, in - * both the left and right sets, the reflexive pair is not generated. - * - * @param left The left set. - * @param right The right set. - * - * @return All pairs formed from the permutations of all elements of the left and right sets. - */ - private Set> allPairs(Set left, Set right) - { - log.debug("private Set> allPairs(Set left = " + left + ", Set right = " + right + "): called"); - - Set> results = new HashSet>(); - - // Form all pairs from left to right. - // Form all pairs from right to left. - for (E le : left) - { - for (E re : right) - { - if (!le.equals(re)) - { - results.add(new Pair(le, re)); - results.add(new Pair(re, le)); - } - } - } - - log.debug("results = " + results); - - return results; - } - - /** - * A simple implementation of a pair, using a list. - */ - private class Pair extends ArrayList - { - public Pair(T first, T second) - { - super(); - super.add(first); - super.add(second); - } - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningCoordinatorTest.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningCoordinatorTest.java deleted file mode 100644 index 1b4461f8c2..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningCoordinatorTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 javax.jms.Message; - -public interface ListeningCoordinatorTest -{ - public void latejoin(Message message); -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningTestDecorator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningTestDecorator.java deleted file mode 100644 index 4312dfbcc6..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningTestDecorator.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * - * 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 junit.framework.Test; -import junit.framework.TestResult; -import org.apache.log4j.Logger; -import org.apache.qpid.util.ConversationFactory; -import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; - -import javax.jms.Connection; -import javax.jms.Destination; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageListener; -import java.util.Collection; -import java.util.Iterator; -import java.util.Set; - -/** - *

CRC Card
Responsibilities Collaborations
Broadcast test - * invitations and collect enlists. {@link ConversationFactory}.
Output test failures for clients - * unwilling to run the test case. {@link Coordinator}
Execute coordinated test cases. {@link - * CoordinatingTestCase}
- */ -public class ListeningTestDecorator extends WrappedSuiteTestDecorator implements MessageListener -{ - private static final Logger log = Logger.getLogger(ListeningTestDecorator.class); - - /** Holds the contact information for all test clients that are available and that may take part in the test. */ - Set allClients; - - /** Holds the conversation helper for the control level conversation for coordinating the test through. */ - ConversationFactory conversationFactory; - - /** Holds the connection that the control conversation is held over. */ - Connection connection; - - /** Holds the underlying {@link CoordinatingTestCase}s that this decorator wraps. */ - WrappedSuiteTestDecorator testSuite; - - /** Hold the current running test case. */ - CoordinatingTestCase _currentTest = null; - - /** - * Creates a wrapped suite test decorator from another one. - * - * @param suite The test suite. - * @param availableClients The list of all clients that responded to the compulsory invite. - * @param controlConversation The conversation helper for the control level, test coordination conversation. - * @param controlConnection The connection that the coordination messages are sent over. - */ - public ListeningTestDecorator(WrappedSuiteTestDecorator suite, Set availableClients, - ConversationFactory controlConversation, Connection controlConnection) - { - super(suite); - - log.debug("public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set allClients = " - + availableClients + ", ConversationHelper controlConversation = " + controlConversation + "): called"); - - testSuite = suite; - allClients = availableClients; - conversationFactory = controlConversation; - connection = controlConnection; - } - - /** - * Broadcasts a test invitation and accetps enlisting from participating clients. The wrapped test case is then - * repeated for every combination of test clients (provided the wrapped test case extends {@link - * CoordinatingTestCase}. - * - *

Any JMSExceptions during the invite/enlist conversation will be allowed to fall through as runtime - * exceptions, resulting in the non-completion of the test run. - * - * @param testResult The the results object to monitor the test results with. - * - * @todo Better error recovery for failure of the invite/enlist conversation could be added. - */ - public void run(TestResult testResult) - { - log.debug("public void run(TestResult testResult): called"); - - Collection tests = testSuite.getAllUnderlyingTests(); - - for (Test test : tests) - { - CoordinatingTestCase coordTest = (CoordinatingTestCase) test; - - Set enlists = signupClients(coordTest); - - if (enlists.size() == 0) - { - throw new RuntimeException("No clients to test with"); - } - - Iterator clients = enlists.iterator(); - coordTest.setSender(clients.next()); - - while (clients.hasNext()) - { - // Set the sending and receiving client details on the test case. - coordTest.setReceiver(clients.next()); - } - - // Pass down the connection to hold the coordination conversation over. - coordTest.setConversationFactory(conversationFactory); - - - if (coordTest instanceof ListeningCoordinatorTest) - { - _currentTest = coordTest; - } - // Execute the test case. - coordTest.run(testResult); - - _currentTest = null; - } - } - - private Set signupClients(CoordinatingTestCase coordTest) - { - // Broadcast the invitation to find out what clients are available to test. - Set enlists; - try - { - Message invite = conversationFactory.getSession().createMessage(); - Destination controlTopic = conversationFactory.getSession().createTopic("iop.control"); - ConversationFactory.Conversation conversation = conversationFactory.startConversation(); - - invite.setStringProperty("CONTROL_TYPE", "INVITE"); - invite.setStringProperty("TEST_NAME", coordTest.getTestCaseNameForTestMethod(coordTest.getName())); - - conversation.send(controlTopic, invite); - - // Wait for a short time, to give test clients an opportunity to reply to the invitation. - Collection replies = conversation.receiveAll(allClients.size(), 5000); - - log.debug("Received " + replies.size() + " enlist replies"); - - enlists = Coordinator.extractEnlists(replies); - - //Create topic to listen on for latejoiners - Destination listenTopic = conversationFactory.getSession().createTopic("iop.control.test." + coordTest.getTestCaseNameForTestMethod(coordTest.getName())); - - //Listen for joiners - conversationFactory.getSession().createConsumer(listenTopic).setMessageListener(this); - log.debug("Created consumer on :" + listenTopic); - } - catch (JMSException e) - { - throw new RuntimeException("There was a JMSException during the invite/enlist conversation.", e); - } - - return enlists; - } - - /** - * Prints a string summarizing this test decorator, mainly for debugging purposes. - * - * @return String representation for debugging purposes. - */ - public String toString() - { - return "ListeningTestDecorator: [ testSuite = " + testSuite + " ]"; - } - - - public void onMessage(Message message) - { - try - { - if (message.getStringProperty("CONTROL_TYPE").equals("LATEJOIN")) - { - ((ListeningCoordinatorTest) _currentTest).latejoin(message); - } - } - catch (JMSException e) - { - log.debug("Unable to process message:" + message); - } - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java deleted file mode 100644 index 42a382a898..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * 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 junit.framework.Assert; - -/** - * An OptOutTestCase is a test case that automatically fails. It is used when a list of test clients has been generated - * from a compulsory invite, but only some of those clients have responded to a specific test case invite. The clients - * that did not respond, are automatically given a fail for the test. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Fail the test with a suitable reason. - *
- */ -public class OptOutTestCase extends CoordinatingTestCase -{ - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public OptOutTestCase(String name) - { - super(name); - } - - /** Generates an appropriate test failure assertion. */ - public void testOptOut() - { - Assert.fail("One of " + getSender() + " and " + getReceiver() + " opted out of the test."); - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "OptOutTest"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java deleted file mode 100644 index c4a9d39cd8..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * - * 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; - -/** - *

- *
CRC Card
Responsibilities Collaborations - *
- */ -public class TestClientDetails -{ - /** The test clients name. */ - public String clientName; - - /* The test clients unique sequence number. Not currently used. */ - - /** The routing key of the test clients control topic. */ - public String privateControlKey; - - /** - * Two TestClientDetails are considered to be equal, iff they have the same client name. - * - * @param o The object to compare to. - * - * @return If the object to compare to is a TestClientDetails equal to this one, false otherwise. - */ - public boolean equals(Object o) - { - if (this == o) - { - return true; - } - - if (!(o instanceof TestClientDetails)) - { - return false; - } - - final TestClientDetails testClientDetails = (TestClientDetails) o; - - if ((clientName != null) ? (!clientName.equals(testClientDetails.clientName)) - : (testClientDetails.clientName != null)) - { - return false; - } - - return true; - } - - /** - * Computes a hash code compatible with the equals method; based on the client name alone. - * - * @return A hash code for this. - */ - public int hashCode() - { - return ((clientName != null) ? clientName.hashCode() : 0); - } - - /** - * Outputs the client name and address details. Mostly used for debugging purposes. - * - * @return The client name and address. - */ - public String toString() - { - return "TestClientDetails: [ clientName = " + clientName + ", privateControlKey = " + privateControlKey + " ]"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/XMLTestListener.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/XMLTestListener.java deleted file mode 100644 index 747ba0dd0b..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/XMLTestListener.java +++ /dev/null @@ -1,402 +0,0 @@ -/* - * - * 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.io.IOException; -import java.io.PrintWriter; -import java.io.Writer; -import java.util.*; - -import junit.framework.AssertionFailedError; -import junit.framework.Test; -import junit.framework.TestCase; - -import org.apache.log4j.Logger; - -import uk.co.thebadgerset.junit.extensions.listeners.TKTestListener; - -/** - * Listens for test results for a named test and outputs these in the standard JUnit XML format to the specified - * writer. - * - *

The API for this listener accepts notifications about different aspects of a tests results through different - * methods, so some assumption needs to be made as to which test result a notification refers to. For example - * {@link #startTest} will be called, then possibly {@link #timing} will be called, even though the test instance is - * passed in both cases, it is not enough to distinguish a particular run of the test, as the test case instance may - * be being shared between multiple threads, or being run a repeated number of times, and can therfore be re-used - * between calls. The listeners make the assumption that, for every test, a unique thread will call {@link #startTest} - * and {@link #endTest} to delimit each test. All calls to set test parameters, timings, state and so on, will occur - * between the start and end and will be given with the same thread id as the start and end, so the thread id provides - * a unqiue value to identify a particular test run against. - * - *

- *
CRC Card
Responsibilities Collaborations - *
- * - * @todo Merge this class with CSV test listener, making the collection of results common to both, and only factoring - * out the results printing code into sub-classes. Provide a simple XML results formatter with the same format as - * the ant XML formatter, and a more structured one for outputing results with timings and summaries from - * performance tests. - */ -public class XMLTestListener implements TKTestListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(XMLTestListener.class); - - /** The results file writer. */ - protected Writer writer; - - /** Holds the results for individual tests. */ - // protected Map results = new LinkedHashMap(); - // protected List results = new ArrayList(); - - /** - * Map for holding results on a per thread basis as they come in. A ThreadLocal is not used as sometimes an - * explicit thread id must be used, where notifications come from different threads than the ones that called - * the test method. - */ - Map threadLocalResults = Collections.synchronizedMap(new LinkedHashMap()); - - /** - * Holds results for tests that have ended. Transferring these results here from the per-thread results map, means - * that the thread id is freed for the thread to generate more results. - */ - List results = new ArrayList(); - - /** Holds the overall error count. */ - protected int errors = 0; - - /** Holds the overall failure count. */ - protected int failures = 0; - - /** Holds the overall tests run count. */ - protected int runs = 0; - - /** Holds the name of the class that tests are being run for. */ - String testClassName; - - /** - * Creates a new XML results output listener that writes to the specified location. - * - * @param writer The location to write results to. - */ - public XMLTestListener(Writer writer, String testClassName) - { - log.debug("public XMLTestListener(Writer writer, String testClassName = " + testClassName + "): called"); - - this.writer = writer; - this.testClassName = testClassName; - } - - /** - * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed. - * - * @param test The test to resest any results for. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void reset(Test test, Long threadId) - { - log.debug("public void reset(Test test = " + test + ", Long threadId = " + threadId + "): called"); - - XMLTestListener.Result r = - (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); - - r.error = null; - r.failure = null; - - } - - /** - * A test started. - */ - public void startTest(Test test) - { - log.debug("public void startTest(Test test = " + test + "): called"); - - Result newResult = new Result(test.getClass().getName(), ((TestCase) test).getName()); - - // Initialize the thread local test results. - threadLocalResults.put(Thread.currentThread().getId(), newResult); - runs++; - } - - /** - * Should be called every time a test completes with the run time of that test. - * - * @param test The name of the test. - * @param nanos The run time of the test in nanoseconds. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void timing(Test test, long nanos, Long threadId) - { } - - /** - * Should be called every time a test completed with the amount of memory used before and after the test was run. - * - * @param test The test which memory was measured for. - * @param memStart The total JVM memory used before the test was run. - * @param memEnd The total JVM memory used after the test was run. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void memoryUsed(Test test, long memStart, long memEnd, Long threadId) - { } - - /** - * Should be called every time a parameterized test completed with the int value of its test parameter. - * - * @param test The test which memory was measured for. - * @param parameter The int parameter value. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void parameterValue(Test test, int parameter, Long threadId) - { } - - /** - * Should be called every time a test completes with the current number of test threads running. - * - * @param test The test for which the measurement is being generated. - * @param threads The number of tests being run concurrently. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void concurrencyLevel(Test test, int threads, Long threadId) - { } - - /** - * Notifies listeners of the tests read/set properties. - * - * @param properties The tests read/set properties. - */ - public void properties(Properties properties) - { } - - /** - * A test ended. - */ - public void endTest(Test test) - { - log.debug("public void endTest(Test test = " + test + "): called"); - - // Move complete test results into the completed tests list. - Result r = threadLocalResults.get(Thread.currentThread().getId()); - results.add(r); - - // Clear all the test results for the thread. - threadLocalResults.remove(Thread.currentThread().getId()); - } - - /** - * Called when a test completes. Success, failure and errors. This method should be used when registering an - * end test from a different thread than the one that started the test. - * - * @param test The test which completed. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void endTest(Test test, Long threadId) - { - log.debug("public void endTest(Test test = " + test + ", Long threadId = " + threadId + "): called"); - - // Move complete test results into the completed tests list. - Result r = - (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); - results.add(r); - - // Clear all the test results for the thread. - threadLocalResults.remove(Thread.currentThread().getId()); - } - - /** - * An error occurred. - */ - public void addError(Test test, Throwable t) - { - log.debug("public void addError(Test test = " + test + ", Throwable t = " + t + "): called"); - - Result r = threadLocalResults.get(Thread.currentThread().getId()); - r.error = t; - errors++; - } - - /** - * A failure occurred. - */ - public void addFailure(Test test, AssertionFailedError t) - { - log.debug("public void addFailure(Test test = " + test + ", AssertionFailedError t = " + t + "): called"); - - Result r = threadLocalResults.get(Thread.currentThread().getId()); - r.failure = t; - failures++; - } - - /** - * Called when a test completes to mark it as a test fail. This method should be used when registering a - * failure from a different thread than the one that started the test. - * - * @param test The test which failed. - * @param e The assertion that failed the test. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void addFailure(Test test, AssertionFailedError e, Long threadId) - { - log.debug("public void addFailure(Test test, AssertionFailedError e, Long threadId): called"); - - Result r = - (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); - r.failure = e; - failures++; - } - - /** - * Notifies listeners of the start of a complete run of tests. - */ - public void startBatch() - { - log.debug("public void startBatch(): called"); - - // Reset all results counts. - threadLocalResults = Collections.synchronizedMap(new HashMap()); - errors = 0; - failures = 0; - runs = 0; - - // Write out the file header. - try - { - writer.write("\n"); - } - catch (IOException e) - { - throw new RuntimeException("Unable to write the test results.", e); - } - } - - /** - * Notifies listeners of the end of a complete run of tests. - * - * @param parameters The optional test parameters to log out with the batch results. - */ - public void endBatch(Properties parameters) - { - log.debug("public void endBatch(Properties parameters = " + parameters + "): called"); - - // Write out the results. - try - { - // writer.write("\n"); - writer.write("\n"); - - for (Result result : results) - { - writer.write(" \n"); - - if (result.error != null) - { - writer.write(" "); - result.error.printStackTrace(new PrintWriter(writer)); - writer.write(" "); - } - else if (result.failure != null) - { - writer.write(" "); - result.failure.printStackTrace(new PrintWriter(writer)); - writer.write(" "); - } - - writer.write(" \n"); - } - - writer.write("\n"); - writer.flush(); - } - catch (IOException e) - { - throw new RuntimeException("Unable to write the test results.", e); - } - } - - /** - * Used to capture the results of a particular test run. - */ - protected static class Result - { - public Result(String testClass, String testName) - { - this.testClass = testClass; - this.testName = testName; - } - - public String testClass; - public String testName; - - /** Holds the exception that caused error in this test. */ - public Throwable error; - - /** Holds the assertion exception that caused failure in this test. */ - public AssertionFailedError failure; - - /** Holds the error count for this test. */ - // public int errors = 0; - - /** Holds the failure count for this tests. */ - // public int failures = 0; - - /** Holds the overall tests run count for this test. */ - // public int runs = 0; - - /*public boolean equals(Object o) - { - if (this == o) - { - return true; - } - - if (!(o instanceof Result)) - { - return false; - } - - final Result result = (Result) o; - - if ((testClass != null) ? (!testClass.equals(result.testClass)) : (result.testClass != null)) - { - return false; - } - - if ((testName != null) ? (!testName.equals(result.testName)) : (result.testName != null)) - { - return false; - } - - return true; - } - - public int hashCode() - { - int result; - result = ((testClass != null) ? testClass.hashCode() : 0); - result = (29 * result) + ((testName != null) ? testName.hashCode() : 0); - - return result; - }*/ - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase1DummyRun.java deleted file mode 100644 index e642ef792b..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase1DummyRun.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * 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.testcases; - -import java.util.HashMap; -import java.util.Map; - -import javax.jms.Message; - -import junit.framework.Assert; - -import org.apache.log4j.Logger; - -import org.apache.qpid.interop.coordinator.CoordinatingTestCase; - -/** - *

- *
CRC Card
Responsibilities Collaborations - *
Exercises the interop testing framework without actually sending any test messages. - * {@link org.apache.qpid.interop.coordinator.CoordinatingTestCase} - *
- */ -public class CoordinatingTestCase1DummyRun extends CoordinatingTestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(CoordinatingTestCase1DummyRun.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public CoordinatingTestCase1DummyRun(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - */ - public void testDummyRun() throws Exception - { - log.debug("public void testDummyRun(): called"); - - Map testConfig = new HashMap(); - testConfig.put("TEST_NAME", "TC1_DummyRun"); - - Message[] reports = sequenceTest(testConfig); - - // Compare sender and receiver reports. - Assert.assertEquals("Expected to get 2 dummy reports.", 2, reports.length); - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC1_DummyRun"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java deleted file mode 100644 index b1b2d9f847..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * - * 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.testcases; - -import java.util.HashMap; -import java.util.Map; - -import javax.jms.Message; - -import junit.framework.Assert; - -import org.apache.log4j.Logger; - -import org.apache.qpid.interop.coordinator.CoordinatingTestCase; - -/** - *

- *
CRC Card
Responsibilities Collaborations - *
Setup p2p test parameters and compare with test output. {@link CoordinatingTestCase} - *
- */ -public class CoordinatingTestCase2BasicP2P extends CoordinatingTestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(CoordinatingTestCase2BasicP2P.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public CoordinatingTestCase2BasicP2P(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - */ - public void testBasicP2P() throws Exception - { - log.debug("public void testBasicP2P(): called"); - - Map testConfig = new HashMap(); - testConfig.put("TEST_NAME", "TC2_BasicP2P"); - testConfig.put("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); - testConfig.put("P2P_NUM_MESSAGES", 50); - - Message[] reports = sequenceTest(testConfig); - - // Compare sender and receiver reports. - int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); - int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); - - Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); - Assert.assertEquals("Sender and receiver messages sent did not match up.", messagesSent, messagesReceived); - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC2_BasicP2P"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase3BasicPubSub.java deleted file mode 100644 index 702c240e9a..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase3BasicPubSub.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * - * 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.testcases; - -import java.util.HashMap; -import java.util.Map; - -import javax.jms.Message; - -import junit.framework.Assert; - -import org.apache.log4j.Logger; - -import org.apache.qpid.interop.coordinator.CoordinatingTestCase; - -/** - *

- *
CRC Card
Responsibilities Collaborations - *
Setup pub/sub test parameters and compare with test output. {@link CoordinatingTestCase} - *
- */ -public class CoordinatingTestCase3BasicPubSub extends CoordinatingTestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(CoordinatingTestCase3BasicPubSub.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public CoordinatingTestCase3BasicPubSub(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - */ - public void testBasicPubSub() throws Exception - { - log.debug("public void testBasicPubSub(): called"); - - Map testConfig = new HashMap(); - testConfig.put("TEST_NAME", "TC3_BasicPubSub"); - testConfig.put("PUBSUB_KEY", "tc3route"); - testConfig.put("PUBSUB_NUM_MESSAGES", 10); - testConfig.put("PUBSUB_NUM_RECEIVERS", 5); - - Message[] reports = sequenceTest(testConfig); - - // Compare sender and receiver reports. - int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); - int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); - - Assert.assertEquals("The requested number of messages were not sent.", 10, messagesSent); - Assert.assertEquals("Received messages did not match up to num sent * num receivers.", messagesSent * 5, - messagesReceived); - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC3_BasicPubSub"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java deleted file mode 100644 index 5545f8d2dc..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * - * 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}. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Count messages received on a topic. {@link org.apache.qpid.interop.old.Publisher} - *
Send reports on messages received, when requested to. {@link org.apache.qpid.interop.old.Publisher} - *
Shutdown, when requested to. {@link org.apache.qpid.interop.old.Publisher} - *
- * - * @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 true if it is a termination request control message, false 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 true if it is a report request control message, false 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 trueIf the specified field has the specified value, fals 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); - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java deleted file mode 100644 index f3a545f580..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * - * 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}. - * - *

- *
CRC Card
Responsibilities Collaborations - *
- * - * @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(); - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/CircuitTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/CircuitTestCase.java new file mode 100644 index 0000000000..966a545e16 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/CircuitTestCase.java @@ -0,0 +1,101 @@ +/* + * + * 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.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.sequencers.TestCaseSequencer; +import org.apache.qpid.test.framework.Circuit; +import org.apache.qpid.test.framework.FrameworkBaseCase; +import org.apache.qpid.test.framework.MessagingTestConfigProperties; + +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; +import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; + +/** + * CircuitTestCase runs a test over a {@link Circuit} controlled by the test parameters. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
+ *
+ * + * @todo When working with test context properties, add overrides to defaults to the singleton instance, but when taking + * a starting point to add specific test case parameters to, take a copy. Use the copy with test case specifics + * to control the test. + */ +public class CircuitTestCase extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(CircuitTestCase.class); + + /** + * Creates a new test case with the specified name. + * + * @param name The test case name. + */ + public CircuitTestCase(String name) + { + super(name); + } + + /** + * Performs the a basic P2P test case. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicP2P() throws Exception + { + log.debug("public void testBasicP2P(): called"); + + // Get the test parameters, any overrides on the command line will have been applied. + ParsedProperties testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + // Customize the test parameters. + testProps.setProperty("TEST_NAME", "DEFAULT_CIRCUIT_TEST"); + testProps.setProperty(MessagingTestConfigProperties.SEND_DESTINATION_NAME_ROOT_PROPNAME, "testqueue"); + + // Get the test sequencer to create test circuits and run the standard test procedure through. + TestCaseSequencer sequencer = getTestSequencer(); + + // Send the test messages, and check that there were no errors doing so. + Circuit testCircuit = sequencer.createCircuit(testProps); + sequencer.sequenceTest(testCircuit, assertionList(testCircuit.getPublisher().noExceptionsAssertion()), testProps); + + // Check that all of the message were sent. + // Check that the receiving end got the same number of messages as the publishing end. + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as known to the test + * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test + * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case + * name "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "DEFAULT_CIRCUIT_TEST"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java new file mode 100644 index 0000000000..73e08b578e --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java @@ -0,0 +1,84 @@ +/* + * + * 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.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.distributedtesting.DistributedTestCase; + +import java.util.Properties; + +/** + * Coordinates test case 1, from the interop test specification. This test connects up the sender and receivers roles, + * and gets some dummy test reports from them, in order to check that the test framework itself is operational. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Exercises the interop testing framework without actually sending any test messages. + * {@link org.apache.qpid.test.framework.distributedtesting.DistributedTestCase} + *
+ */ +public class InteropTestCase1DummyRun extends DistributedTestCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase1DummyRun.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase1DummyRun(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testDummyRun() throws Exception + { + log.debug("public void testDummyRun(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "TC1_DummyRun"); + + /*Message[] reports =*/ getTestSequencer().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + // Assert.assertEquals("Expected to get 2 dummy reports.", 2, reports.length); + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC1_DummyRun"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java new file mode 100644 index 0000000000..f77bbf032f --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java @@ -0,0 +1,90 @@ +/* + * + * 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.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.distributedtesting.DistributedTestCase; + +import java.util.Properties; + +/** + * Implements test case 2, from the interop test specification. This test sets up the TC2_BasicP2P test for 50 + * messages. It checks that the sender and receivers reports both indicate that all the test messages were transmitted + * successfully. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Setup p2p test parameters and compare with test output. {@link DistributedTestCase} + *
+ */ +public class InteropTestCase2BasicP2P extends DistributedTestCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase2BasicP2P.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase2BasicP2P(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicP2P() throws Exception + { + log.debug("public void testBasicP2P(): called"); + + Properties testConfig = new Properties(); + testConfig.setProperty("TEST_NAME", "TC2_BasicP2P"); + testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); + testConfig.put("P2P_NUM_MESSAGES", 50); + + /*Message[] reports =*/ getTestSequencer().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); + Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived);*/ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC2_BasicP2P"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java new file mode 100644 index 0000000000..ad27ca63bd --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java @@ -0,0 +1,88 @@ +/* + * + * 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.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.distributedtesting.DistributedTestCase; + +import java.util.Properties; + +/** + *

+ *
CRC Card
Responsibilities Collaborations + *
Setup pub/sub test parameters and compare with test output. {@link DistributedTestCase} + *
+ */ +public class InteropTestCase3BasicPubSub extends DistributedTestCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase3BasicPubSub.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase3BasicPubSub(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicPubSub() throws Exception + { + log.debug("public void testBasicPubSub(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "TC3_BasicPubSub"); + testConfig.put("PUBSUB_KEY", "tc3route"); + testConfig.put("PUBSUB_NUM_MESSAGES", 10); + testConfig.put("PUBSUB_NUM_RECEIVERS", 5); + + /*Message[] reports =*/ getTestSequencer().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 10, messagesSent); + Assert.assertEquals("Received messages did not match up to num sent * num receivers.", messagesSent * 5, + messagesReceived);*/ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC3_BasicPubSub"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java deleted file mode 100644 index 37952d08c8..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * - * 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. - * - *

- *
CRC Card
Responsibilities - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Perform test case actions. - *
Generate test reports. - *
- */ -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 true to accept the invitation, false 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. - * return from here when you have finished the test.. this will signal the controller that the test has ended. - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void start() throws JMSException; - - /** - * Gives notice of termination of the test case actions. - * - * @throws JMSException Any JMSException resulting from allowed to fall through. - */ - public void terminate() throws JMSException, InterruptedException; - - /** - * 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/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java deleted file mode 100644 index a904bfa419..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java +++ /dev/null @@ -1,422 +0,0 @@ -/* - * - * 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 org.apache.log4j.Logger; -import org.apache.qpid.interop.testclient.testcases.TestCase1DummyRun; -import org.apache.qpid.interop.testclient.testcases.TestCase2BasicP2P; -import org.apache.qpid.util.CommandLineParser; -import org.apache.qpid.util.PropertiesUtils; - -import javax.jms.Connection; -import javax.jms.ConnectionFactory; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageConsumer; -import javax.jms.MessageListener; -import javax.jms.MessageProducer; -import javax.jms.Session; -import javax.naming.Context; -import javax.naming.InitialContext; -import javax.naming.NamingException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -/** - * 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}. - * - *

- *
Messages Handled by TestClient
Message Action - *
Invite(compulsory) Reply with Enlist. - *
Invite(test case) Reply with Enlist if test case available. - *
AssignRole(test case) Reply with Accept Role if matches an enlisted test. Keep test parameters. - *
Start Send test messages defined by test parameters. Send report on messages sent. - *
Status Request Send report on messages received. - *
- * - *

- *
CRC Card
Responsibilities Collaborations - *
Handle all incoming control messages. {@link InteropClientTestCase} - *
Configure and look up test cases by name. {@link InteropClientTestCase} - *
- */ -public class TestClient implements MessageListener -{ - private static Logger log = Logger.getLogger(TestClient.class); - - public static final String CONNECTION_PROPERTY = "connectionfactory.broker"; - public static final String CONNECTION_NAME = "broker"; - public static final String CLIENT_NAME = "java"; - public static final String DEFAULT_CONNECTION_PROPS_RESOURCE = "org/apache/qpid/interop/connection.properties"; - - /** Holds the URL of the broker to run the tests on. */ - public static String brokerUrl; - - /** Holds the virtual host to run the tests on. If null, then the default virtual host is used. */ - public static String virtualHost; - - /** Holds all the test cases loaded from the classpath. */ - Map testCases = new HashMap(); - - protected InteropClientTestCase currentTestCase; - - protected Connection _connection; - protected MessageProducer producer; - protected Session session; - - protected String clientName = CLIENT_NAME; - - /** - * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client - * identifying name. - * - * @param brokerUrl The url of the broker to connect to. - * @param virtualHost The virtual host to conect to. - * @param clientName The client name to use. - */ - public TestClient(String brokerUrl, String virtualHost, String clientName) - { - log.debug("public TestClient(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost - + ", String clientName = " + clientName + "): called"); - - // Retain the connection parameters. - this.brokerUrl = brokerUrl; - this.virtualHost = virtualHost; - this.clientName = clientName; - } - - /** - * The entry point for the interop test coordinator. This client accepts the following command line arguments: - * - *

- *
-b The broker URL. Optional. - *
-h The virtual host. Optional. - *
-n The test client name. Optional. - *
name=value Trailing argument define name/value pairs. Added to system properties. Optional. - *
- * - * @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", "false"}, - {"h", "The virtual host to use.", "virtual host", "false"}, - {"n", "The test client name.", "name", "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"); - String clientName = options.getProperty("n"); - - // 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, (clientName == null) ? CLIENT_NAME : clientName); - - // Use a class path scanner to find all the interop test case implementations. - Collection> testCaseClasses = - new ArrayList>(); - // ClasspathScanner.getMatches(InteropClientTestCase.class, "^TestCase.*", true); - // Hard code the test classes till the classpath scanner is fixed. - Collections.addAll(testCaseClasses, - new Class[]{TestCase1DummyRun.class, TestCase2BasicP2P.class, TestClient.class}); - - try - { - client.start(testCaseClasses); - } - catch (Exception e) - { - log.error("The test client was unable to start.", e); - System.exit(1); - } - } - - /** - * Starts the interop test client running. This causes it to start listening for incoming test invites. - * - * @throws JMSException Any underlying JMSExceptions are allowed to fall through. @param testCaseClasses - */ - protected void start(Collection> testCaseClasses) throws JMSException - { - log.debug("private void start(): called"); - - // Create all the test case implementations and index them by the test names. - for (Class nextClass : testCaseClasses) - { - try - { - InteropClientTestCase testCase = nextClass.newInstance(); - testCases.put(testCase.getName(), testCase); - } - catch (InstantiationException e) - { - log.warn("Could not instantiate test case class: " + nextClass.getName(), e); - // Ignored. - } - catch (IllegalAccessException e) - { - log.warn("Could not instantiate test case class due to illegal access: " + nextClass.getName(), e); - // Ignored. - } - } - - // Open a connection to communicate with the coordinator on. - _connection = createConnection(DEFAULT_CONNECTION_PROPS_RESOURCE, clientName, brokerUrl, virtualHost); - - session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Set this up to listen for control messages. - MessageConsumer consumer = session.createConsumer(session.createTopic("iop.control." + clientName)); - consumer.setMessageListener(this); - - MessageConsumer consumer2 = session.createConsumer(session.createTopic("iop.control")); - consumer2.setMessageListener(this); - - // Create a producer to send replies with. - producer = session.createProducer(null); - - // Start listening for incoming control messages. - _connection.start(); - } - - - public static Connection createConnection(String connectionPropsResource, String brokerUrl, String virtualHost) - { - return createConnection(connectionPropsResource, "clientID", brokerUrl, virtualHost); - } - - /** - * Establishes a JMS connection using a properties file and qpids built in JNDI implementation. This is a simple - * convenience method for code that does anticipate handling connection failures. All exceptions that indicate that - * the connection has failed, are wrapped as rutime exceptions, preumably handled by a top level failure handler. - * - * @param connectionPropsResource The name of the connection properties file. - * @param clientID - * @param brokerUrl The broker url to connect to, null to use the default from the - * properties. - * @param virtualHost The virtual host to connectio to, null to use the default. - * - * @return A JMS conneciton. - * - * @todo Make username/password configurable. Allow multiple urls for fail over. Once it feels right, move it to a - * Utils library class. - */ - public static Connection createConnection(String connectionPropsResource, String clientID, String brokerUrl, String virtualHost) - { - log.debug("public static Connection createConnection(String connectionPropsResource = " + connectionPropsResource - + ", String brokerUrl = " + brokerUrl + ", String clientID = " + clientID - + ", String virtualHost = " + virtualHost + " ): called"); - - try - { - Properties connectionProps = - PropertiesUtils.getProperties(TestClient.class.getClassLoader().getResourceAsStream( - connectionPropsResource)); - - if (brokerUrl != null) - { - String connectionString = - "amqp://guest:guest@" + clientID + "/" + ((virtualHost != null) ? virtualHost : "") + "?brokerlist='" + brokerUrl + "'"; - connectionProps.setProperty(CONNECTION_PROPERTY, connectionString); - } - - Context ctx = new InitialContext(connectionProps); - - ConnectionFactory cf = (ConnectionFactory) ctx.lookup(CONNECTION_NAME); - Connection connection = cf.createConnection(); - - return connection; - } - catch (IOException e) - { - throw new RuntimeException(e); - } - catch (NamingException e) - { - throw new RuntimeException(e); - } - catch (JMSException e) - { - throw new RuntimeException(e); - } - } - - /** - * Handles all incoming control messages. - * - * @param message The incoming message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - try - { - String controlType = message.getStringProperty("CONTROL_TYPE"); - String testName = message.getStringProperty("TEST_NAME"); - - log.info("onMessage(Message message = " + message + "): for '" + controlType + "' to '" + testName + "'"); - - // Check if the message is a test invite. - if ("INVITE".equals(controlType)) - { - // Flag used to indicate that an enlist should be sent. Only enlist to compulsory invites or invites - // for which test cases exist. - boolean enlist = false; - - if (testName != null) - { - log.debug("Got an invite to test: " + testName); - - // Check if the requested test case is available. - InteropClientTestCase testCase = testCases.get(testName); - - if (testCase != null) - { - // Make the requested test case the current test case. - currentTestCase = testCase; - enlist = true; - } - else - { - log.warn("'" + testName + "' not part of this clients tests."); - } - } - else - { - log.debug("Got a compulsory invite."); - - enlist = true; - } - - if (enlist) - { - // Reply with the client name in an Enlist message. - Message enlistMessage = session.createMessage(); - enlistMessage.setStringProperty("CONTROL_TYPE", "ENLIST"); - enlistMessage.setStringProperty("CLIENT_NAME", clientName); - enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); - enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - log.info("Sending Message '" + enlistMessage + "'. to " + message.getJMSReplyTo()); - - producer.send(message.getJMSReplyTo(), enlistMessage); - } - } - else if ("ASSIGN_ROLE".equals(controlType)) - { - // Assign the role to the current test case. - String roleName = message.getStringProperty("ROLE"); - - log.debug("Got a role assignment to role: " + roleName); - - InteropClientTestCase.Roles role = Enum.valueOf(InteropClientTestCase.Roles.class, roleName); - - currentTestCase.assignRole(role, message); - - // Reply by accepting the role in an Accept Role message. - Message acceptRoleMessage = session.createMessage(); - acceptRoleMessage.setStringProperty("CONTROL_TYPE", "ACCEPT_ROLE"); - acceptRoleMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - producer.send(message.getJMSReplyTo(), acceptRoleMessage); - } - else if ("START".equals(controlType) || "STATUS_REQUEST".equals(controlType)) - { - if ("START".equals(controlType)) - { - log.debug("Got a start notification."); - - // Start the current test case. - currentTestCase.start(); - } - else - { - log.debug("Got a status request."); - } - - // Generate the report from the test case and reply with it as a Report message. - Message reportMessage = currentTestCase.getReport(session); - reportMessage.setStringProperty("CONTROL_TYPE", "REPORT"); - reportMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - producer.send(message.getJMSReplyTo(), reportMessage); - } - else if ("TERMINATE".equals(controlType)) - { - log.info("Received termination instruction from coordinator."); - -// try -// { -// currentTestCase.terminate(); -// } -// catch (InterruptedException e) -// { -// // -// } - // Is a cleaner shutdown needed? - _connection.close(); - System.exit(0); - } - else - { - // Log a warning about this but otherwise ignore it. - log.warn("Got an unknown control message, controlType = " + controlType + ", message = " + message); - } - } - catch (JMSException e) - { - // Log a warning about this, but otherwise ignore it. - log.warn("A JMSException occurred whilst handling a message."); - log.debug("Got JMSException whilst handling message: " + message, e); - } - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java deleted file mode 100644 index 5f257c0b36..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * - * 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 org.apache.log4j.Logger; - -import org.apache.qpid.interop.testclient.InteropClientTestCase; - -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.Session; - -/** - * 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. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Perform test case actions. - *
Generate test reports. - *
- */ -public class TestCase1DummyRun implements InteropClientTestCase -{ - private static final Logger log = Logger.getLogger(TestCase1DummyRun.class); - - public String getName() - { - log.debug("public String getName(): called"); - - return "TC1_DummyRun"; - } - - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage): called"); - - // Test parameters don't matter, accept all invites. - return true; - } - - public void assignRole(Roles role, Message assignRoleMessage) throws JMSException - { - log.debug("public void assignRole(Roles role, Message assignRoleMessage): called"); - - // Do nothing, both roles are the same. - } - - public void start() - { - log.debug("public void start(): called"); - - // Do nothing. - } - - public void terminate() throws JMSException - { - //todo - } - - public Message getReport(Session session) throws JMSException - { - log.debug("public Message getReport(Session session): called"); - - // 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) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Ignore any messages. - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java deleted file mode 100644 index ff56ee9b93..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * - * 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.*; - -import org.apache.log4j.Logger; - -import org.apache.qpid.interop.testclient.InteropClientTestCase; -import org.apache.qpid.interop.testclient.TestClient; - -/** - * 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. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Send required number of test messages. - *
Generate test reports. - *
- */ -public class TestCase2BasicP2P implements InteropClientTestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase2BasicP2P.class); - - /** Holds the count of test messages received. */ - private int messageCount; - - /** The role to be played by the test. */ - private Roles role; - - /** The number of test messages to send. */ - private int numMessages; - - /** The routing key to send them to on the default direct exchange. */ - private Destination sendDestination; - - /** The connection to send the test messages on. */ - private Connection connection; - - /** The session to send the test messages on. */ - private Session session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "TC2_BasicP2P"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); - - // 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 - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Reset the message count for a new test. - messageCount = 0; - - // Take note of the role to be played. - this.role = role; - - // Create a new connection to pass the test messages on. - connection = - TestClient.createConnection(TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, TestClient.brokerUrl, - TestClient.virtualHost); - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Extract and retain the test parameters. - numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); - sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); - - log.debug("numMessages = " + numMessages); - log.debug("sendDestination = " + sendDestination); - log.debug("role = " + role); - - switch (role) - { - // Check if the sender role is being assigned, and set up a message producer if so. - case SENDER: - producer = session.createProducer(sendDestination); - break; - - // Otherwise the receiver role is being assigned, so set this up to listen for messages. - case RECEIVER: - MessageConsumer consumer = session.createConsumer(sendDestination); - consumer.setMessageListener(this); - break; - } - - connection.start(); - } - - /** - * Performs the test case actions. - */ - public void start() throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - if (role.equals(Roles.SENDER)) - { - Message testMessage = session.createTextMessage("test"); - - for (int i = 0; i < numMessages; i++) - { - producer.send(testMessage); - - // Increment the message count. - messageCount++; - } - } - } - - public void terminate() throws JMSException - { - //todo - } - - /** - * 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 - { - log.debug("public Message getReport(Session session): called"); - - // Close the test connection. - connection.close(); - - // Generate a report message containing the count of the number of messages passed. - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - report.setIntProperty("MESSAGE_COUNT", messageCount); - - return report; - } - - /** - * Counts incoming test messages. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Increment the message count. - messageCount++; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java deleted file mode 100644 index 7b35142c82..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * - * 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.*; - -import org.apache.log4j.Logger; - -import org.apache.qpid.interop.testclient.InteropClientTestCase; - -/** - * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the - * default topic exchange, using the specified number of receiver connections. Produces reports on the actual number of - * messages sent/received. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Send required number of test messages using pub/sub. - *
Generate test reports. - *
- */ -public class TestCase3BasicPubSub implements InteropClientTestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase3BasicPubSub.class); - - /** Holds the count of test messages received. */ - private int messageCount; - - /** The role to be played by the test. */ - private Roles role; - - /** The number of test messages to send. */ - private int numMessages; - - /** The number of receiver connection to use. */ - private int numReceivers; - - /** The routing key to send them to on the default direct exchange. */ - private Destination sendDestination; - - /** The connections to send/receive the test messages on. */ - private Connection[] connection; - - /** The sessions to send/receive the test messages on. */ - private Session[] session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "TC3_BasicPubSub"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); - - // 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 - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Reset the message count for a new test. - messageCount = 0; - - // Take note of the role to be played. - this.role = role; - - // Extract and retain the test parameters. - numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); - numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); - String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); - - log.debug("numMessages = " + numMessages); - log.debug("numReceivers = " + numReceivers); - log.debug("sendKey = " + sendKey); - log.debug("role = " + role); - - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - // Create a new connection to pass the test messages on. - connection = new Connection[1]; - session = new Session[1]; - - connection[0] = - org.apache.qpid.interop.testclient.TestClient.createConnection(org.apache.qpid.interop.testclient.TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, org.apache.qpid.interop.testclient.TestClient.brokerUrl, - org.apache.qpid.interop.testclient.TestClient.virtualHost); - session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Extract and retain the test parameters. - sendDestination = session[0].createTopic(sendKey); - - producer = session[0].createProducer(sendDestination); - break; - - // Otherwise the receiver role is being assigned, so set this up to listen for messages on the required number - // of receiver connections. - case RECEIVER: - // Create the required number of receiver connections. - connection = new Connection[numReceivers]; - session = new Session[numReceivers]; - - for (int i = 0; i < numReceivers; i++) - { - connection[i] = - org.apache.qpid.interop.testclient.TestClient.createConnection(org.apache.qpid.interop.testclient.TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, org.apache.qpid.interop.testclient.TestClient.brokerUrl, - org.apache.qpid.interop.testclient.TestClient.virtualHost); - session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); - - sendDestination = session[i].createTopic(sendKey); - - MessageConsumer consumer = session[i].createConsumer(sendDestination); - consumer.setMessageListener(this); - } - - break; - } - - // Start all the connection dispatcher threads running. - for (int i = 0; i < connection.length; i++) - { - connection[i].start(); - } - } - - /** - * Performs the test case actions. - */ - public void start() throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - if (role.equals(Roles.SENDER)) - { - Message testMessage = session[0].createTextMessage("test"); - - for (int i = 0; i < numMessages; i++) - { - producer.send(testMessage); - - // Increment the message count. - messageCount++; - } - } - } - - public void terminate() throws JMSException, InterruptedException - { - //todo - } - - /** - * 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 - { - log.debug("public Message getReport(Session session): called"); - - // Close the test connections. - for (int i = 0; i < connection.length; i++) - { - connection[i].close(); - } - - // Generate a report message containing the count of the number of messages passed. - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - report.setIntProperty("MESSAGE_COUNT", messageCount); - - return report; - } - - /** - * Counts incoming test messages. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Increment the message count. - messageCount++; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java new file mode 100644 index 0000000000..65e05fab4b --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java @@ -0,0 +1,905 @@ +/* + * + * 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.sustained; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQNoConsumersException; +import org.apache.qpid.client.AMQNoRouteException; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub; +import org.apache.qpid.test.framework.TestUtils; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +/** + * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the + * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of + * messages sent/received. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages using pub/sub.
Generate test reports. + *
+ */ +public class SustainedClientTestCase extends TestCase3BasicPubSub implements ExceptionListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(SustainedClientTestCase.class); + + /** Used to log to the console. */ + private static final Logger console = Logger.getLogger("SustainedTest"); + + /** The role to be played by the test. */ + private Roles role; + + /** The number of receivers connection to use. */ + private int numReceivers; + + /** The routing key to send them to on the default direct exchange. */ + private Destination sendDestination; + + /** The routing key to send updates to on the default direct exchange. */ + private Destination sendUpdateDestination; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** Adapter that adjusts the send rate based on the updates from clients. */ + SustainedRateAdapter _rateAdapter; + + /** */ + int _batchSize; + + private static final long TEN_MILLI_SEC = 10000000; + private static final int DEBUG_LOG_UPATE_INTERVAL = 10; + private static final int LOG_UPATE_INTERVAL = 10; + private static final boolean SLEEP_PER_MESSAGE = Boolean.getBoolean("sleepPerMessage"); + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "Perf_SustainedPubSub"; + } + + /** + * 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 receivers. + * @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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numReceivers = assignRoleMessage.getIntProperty("SUSTAINED_NUM_RECEIVERS"); + _batchSize = assignRoleMessage.getIntProperty("SUSTAINED_UPDATE_INTERVAL"); + String sendKey = assignRoleMessage.getStringProperty("SUSTAINED_KEY"); + String sendUpdateKey = assignRoleMessage.getStringProperty("SUSTAINED_UPDATE_KEY"); + int ackMode = assignRoleMessage.getIntProperty("ACKNOWLEDGE_MODE"); + String clientName = assignRoleMessage.getStringProperty("CLIENT_NAME"); + + if (log.isDebugEnabled()) + { + log.debug("numReceivers = " + numReceivers); + log.debug("_batchSize = " + _batchSize); + log.debug("ackMode = " + ackMode); + log.debug("sendKey = " + sendKey); + log.debug("sendUpdateKey = " + sendUpdateKey); + log.debug("role = " + role); + } + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + console.info("Creating Sender"); + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = TestUtils.createConnection(TestClient.testContextProperties); + session[0] = connection[0].createSession(false, ackMode); + + // Extract and retain the test parameters. + sendDestination = session[0].createTopic(sendKey); + + connection[0].setExceptionListener(this); + + producer = session[0].createProducer(sendDestination); + + sendUpdateDestination = session[0].createTopic(sendUpdateKey); + MessageConsumer updateConsumer = session[0].createConsumer(sendUpdateDestination); + + _rateAdapter = new SustainedRateAdapter(this); + updateConsumer.setMessageListener(_rateAdapter); + + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number + // of receivers connections. + case RECEIVER: + console.info("Creating Receiver"); + // Create the required number of receivers connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = TestUtils.createConnection(TestClient.testContextProperties); + session[i] = connection[i].createSession(false, ackMode); + + sendDestination = session[i].createTopic(sendKey); + + sendUpdateDestination = session[i].createTopic(sendUpdateKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + + consumer.setMessageListener(new SustainedListener(clientName + "-" + i, _batchSize, session[i], + sendUpdateDestination)); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (int i = 0; i < connection.length; i++) + { + connection[i].start(); + } + } + + /** Performs the test case actions. */ + public void start() throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + _rateAdapter.run(); + break; + case RECEIVER: + + } + + // return from here when you have finished the test.. this will signal the controller and + } + + public void terminate() throws JMSException, InterruptedException + { + if (_rateAdapter != null) + { + _rateAdapter.stop(); + } + } + + /** + * 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 + { + log.debug("public Message getReport(Session session): called"); + + // Close the test connections. + for (int i = 0; i < connection.length; i++) + { + connection[i].close(); + } + + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + + return report; + } + + public void onException(JMSException jmsException) + { + Exception linked = jmsException.getLinkedException(); + + if (linked != null) + { + if (log.isDebugEnabled()) + { + log.debug("Linked Exception:" + linked); + } + + if ((linked instanceof AMQNoRouteException) || (linked instanceof AMQNoConsumersException)) + { + if (log.isDebugEnabled()) + { + if (linked instanceof AMQNoConsumersException) + { + log.warn("No clients currently available for message:" + + ((AMQNoConsumersException) linked).getUndeliveredMessage()); + } + else + { + log.warn("No route for message"); + } + } + + // Tell the rate adapter that there are no clients ready yet + _rateAdapter.NO_CLIENTS = true; + } + } + else + { + log.warn("Exception:" + linked); + } + } + + /** + * Inner class that listens for messages and sends a report for the time taken between receiving the 'start' and + * 'end' messages. + */ + class SustainedListener implements MessageListener + { + /** Number of messages received */ + private long _received = 0; + /** The number of messages in the batch */ + private int _batchSize = 0; + /** Record of the when the 'start' messagse was sen */ + private Long _startTime; + /** Message producer to use to send reports */ + MessageProducer _updater; + /** Session to create the report message on */ + Session _session; + /** Record of the client ID used for this SustainedListnener */ + String _client; + + /** + * Main Constructor + * + * @param clientname The _client id used to identify this connection. + * @param batchSize The number of messages that are to be sent per batch. Note: This is not used to + * control the interval between sending reports. + * @param session The session used for communication. + * @param sendDestination The destination that update reports should be sent to. + * + * @throws JMSException My occur if creatingthe Producer fails + */ + public SustainedListener(String clientname, int batchSize, Session session, Destination sendDestination) + throws JMSException + { + _batchSize = batchSize; + _client = clientname; + _session = session; + _updater = session.createProducer(sendDestination); + } + + public void onMessage(Message message) + { + if (log.isTraceEnabled()) + { + log.trace("Message " + _received + "received in listener"); + } + + if (message instanceof TextMessage) + { + try + { + _received++; + if (((TextMessage) message).getText().equals("start")) + { + log.debug("Starting Batch"); + _startTime = System.nanoTime(); + } + else if (((TextMessage) message).getText().equals("end")) + { + if (_startTime != null) + { + long currentTime = System.nanoTime(); + sendStatus(currentTime - _startTime, _received, message.getIntProperty("BATCH")); + log.debug("End Batch"); + } + } + } + catch (JMSException e) + { + // ignore error + } + } + + } + + /** + * sendStatus creates and sends the report back to the publisher + * + * @param time taken for the the last batch + * @param received Total number of messages received. + * @param batchNumber the batch number + * @throws JMSException if an error occurs during the send + */ + private void sendStatus(long time, long received, int batchNumber) throws JMSException + { + Message updateMessage = _session.createTextMessage("update"); + updateMessage.setStringProperty("CLIENT_ID", ":" + _client); + updateMessage.setStringProperty("CONTROL_TYPE", "UPDATE"); + updateMessage.setLongProperty("RECEIVED", received); + updateMessage.setIntProperty("BATCH", batchNumber); + updateMessage.setLongProperty("DURATION", time); + + if (log.isInfoEnabled()) + { + log.info("**** SENDING [" + batchNumber + "]**** " + "CLIENT_ID:" + _client + " RECEIVED:" + received + + " BATCH:" + batchNumber + " DURATION:" + time); + } + + // Output on the main console.info the details of this batch + if ((batchNumber % 10) == 0) + { + console.info("Sending Report [" + batchNumber + "] " + "CLIENT_ID:" + _client + " RECEIVED:" + received + + " BATCH:" + batchNumber + " DURATION:" + time); + } + + _updater.send(updateMessage); + } + } + + /** + * This class is used here to adjust the _delay value which in turn is used to control the number of messages/second + * that are sent through the test system. + * + * By keeping a record of the messages recevied and the average time taken to process the batch size can be + * calculated and so the delay can be adjusted to maintain that rate. + * + * Given that delays of < 10ms can be rounded up the delay is only used between messages if the _delay > 10ms * no + * messages in the batch. Otherwise the delay is used at the end of the batch. + */ + class SustainedRateAdapter implements MessageListener, Runnable + { + private SustainedClientTestCase _client; + private long _batchVariance = Integer.getInteger("batchVariance", 3); // no. batches to allow drifting + private long _timeVariance = TEN_MILLI_SEC * 5; // no. nanos between send and report delay (10ms) + private volatile long _delay; // in nanos + private long _sent; + private Map _slowClients = new HashMap(); + private static final long PAUSE_SLEEP = TEN_MILLI_SEC / 1000; // 10 ms + private static final long NO_CLIENT_SLEEP = 1000; // 1s + private volatile boolean NO_CLIENTS = true; + private int _delayShifting; + private final int REPORTS_WITHOUT_CHANGE = Integer.getInteger("stableReportCount", 5); + private boolean _warmedup = false; + private static final long EXPECTED_TIME_PER_BATCH = 100000L; + private int _warmUpBatches = Integer.getInteger("warmUpBatches", 10); + + SustainedRateAdapter(SustainedClientTestCase client) + { + _client = client; + } + + public void onMessage(Message message) + { + if (log.isDebugEnabled()) + { + log.debug("SustainedRateAdapter onMessage(Message message = " + message + "): called"); + } + + try + { + String controlType = message.getStringProperty("CONTROL_TYPE"); + + // Check if the message is a test invite. + if ("UPDATE".equals(controlType)) + { + NO_CLIENTS = false; + long duration = message.getLongProperty("DURATION"); + long totalReceived = message.getLongProperty("RECEIVED"); + String client = message.getStringProperty("CLIENT_ID"); + int batchNumber = message.getIntProperty("BATCH"); + + if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + log.info("Update Report: CLIENT_ID:" + client + " RECEIVED:" + totalReceived + " Recevied BATCH:" + + batchNumber + " DURATION:" + duration); + } + + recordSlow(client, totalReceived, batchNumber); + + adjustDelay(client, batchNumber, duration); + + // Warm up completes when: + // we haven't warmed up + // and the number of batches sent to each client is at least half of the required warmup batches + if (!_warmedup && (batchNumber >= _warmUpBatches)) + { + _warmedup = true; + _warmup.countDown(); + + } + } + } + catch (JMSException e) + { + // + } + } + + CountDownLatch _warmup = new CountDownLatch(1); + + int _numBatches = 10000; + + // long[] _timings = new long[_numBatches]; + private boolean _running = true; + + public void run() + { + console.info("Warming up"); + + doBatch(_warmUpBatches); + + try + { + // wait for warmup to complete. + _warmup.await(); + + // set delay to the average length of the batches + _delay = _totalDuration / _warmUpBatches / delays.size(); + + console.info("Warmup complete delay set : " + _delay + " based on _totalDuration: " + _totalDuration + + " over no. batches: " + _warmUpBatches + " with client count: " + delays.size()); + + _totalDuration = 0L; + _totalReceived = 0L; + _sent = 0L; + } + catch (InterruptedException e) + { + // + } + + doBatch(_numBatches); + + } + + private void doBatch(int batchSize) // long[] timings, + { + TextMessage testMessage = null; + try + { + testMessage = _client.session[0].createTextMessage("start"); + + for (int batch = 0; batch <= batchSize; batch++) + // while (_running) + { + long start = System.nanoTime(); + + testMessage.setText("start"); + testMessage.setIntProperty("BATCH", batch); + + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + + testMessage.setText("test"); + // start at 2 so start and end count as part of batch + for (int m = 2; m < _batchSize; m++) + { + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + } + + testMessage.setText("end"); + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + + long end = System.nanoTime(); + + long sendtime = end - start; + + if (log.isDebugEnabled()) + { + log.info("Sent batch[" + batch + "](" + _batchSize + ") in " + sendtime); // timings[batch]); + } + + if ((batch % LOG_UPATE_INTERVAL) == 0) + { + console.info("Sent Batch[" + batch + "](" + _batchSize + ")" + status()); + } + + _rateAdapter.sleepBatch(); + + } + } + catch (JMSException e) + { + console.error("Runner ended"); + } + } + + private String status() + { + return " TotalDuration: " + _totalDuration + " for " + delays.size() + " consumers" + " Delay is " + _delay + + " resulting in " + + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch")); + } + + private void sleepBatch() + { + if (checkForSlowClients()) + { // if there werwe slow clients we have already slept so don't sleep anymore again. + return; + } + + if (!SLEEP_PER_MESSAGE) + { + // per batch sleep.. if sleep is to small to spread over the batch. + if (_delay <= (TEN_MILLI_SEC * _batchSize)) + { + sleepLong(_delay); + } + else + { + log.info("Not sleeping _delay > ten*batch is:" + _delay); + } + } + } + + public void stop() + { + _running = false; + } + + Map delays = new HashMap(); + Long _totalReceived = 0L; + Long _totalDuration = 0L; + int _skipUpdate = 0; + + /** + * Adjust the delay for sending messages based on this update from the client + * + * @param client The client that send this update + * @param duration The time taken for the last batch of messagse + * @param batchNumber The reported batchnumber from the client + */ + private void adjustDelay(String client, int batchNumber, long duration) + { + // Retrieve the current total time taken for this client. + Long currentTime = delays.get(client); + + // Add the new duration time to this client + if (currentTime == null) + { + currentTime = duration; + } + else + { + currentTime += duration; + } + + delays.put(client, currentTime); + + long batchesSent = _sent / _batchSize; + + // ensure we don't divide by zero + if (batchesSent == 0) + { + batchesSent = 1L; + } + + _totalReceived += _batchSize; + _totalDuration += duration; + + // calculate average duration accross clients per batch + long averageDuration = _totalDuration / delays.size() / batchesSent; + + // calculate the difference between current send delay and average report delay + long diff = (duration) - averageDuration; + + if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + log.info("TotalDuration:" + _totalDuration + " for " + delays.size() + " consumers." + " on batch: " + + batchesSent + " received batch: " + batchNumber + " Batch Duration: " + duration + " Average: " + + averageDuration + " so diff: " + diff + " for : " + client + " Delay is " + _delay + " resulting in " + + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch"))); + } + + // if the averageDuration differs from the current by more than the specified variane then adjust delay. + if (Math.abs(diff) > _timeVariance) + { + + // if the the _delay is larger than the required duration to send report + // speed up + if (diff > TEN_MILLI_SEC) + { + _delay -= TEN_MILLI_SEC; + + if (_delay < 0) + { + _delay = 0; + log.info("Reset _delay to 0"); + delayStable(); + } + else + { + delayChanged(); + } + + } + else if (diff < 0) // diff < 0 diff cannot be 0 as it is > _timeVariance + { + // the report took longer + _delay += TEN_MILLI_SEC; + delayChanged(); + } + } + else + { + delayStable(); + } + + // If we have a consumer that is behind with the batches. + if ((batchesSent - batchNumber) > _batchVariance) + { + log.debug("Increasing _delay as sending more than receiving"); + + _delay += 2 * TEN_MILLI_SEC; + delayChanged(); + } + + } + + /** Reset the number of iterations before we say the delay has stabilised. */ + private void delayChanged() + { + _delayShifting = REPORTS_WITHOUT_CHANGE; + } + + /** + * Record the fact that delay has stabilised If delay has stablised for REPORTS_WITHOUT_CHANGE then it will + * output Delay stabilised + */ + private void delayStable() + { + _delayShifting--; + + if (_delayShifting < 0) + { + _delayShifting = 0; + console.debug("Delay stabilised:" + _delay); + } + } + + /** + * Checks that the client has received enough messages. If the client has fallen behind then they are put in the + * _slowClients lists which will increase the delay. + * + * @param client The client identifier to check + * @param received the number of messages received by that client + * @param batchNumber + */ + private void recordSlow(String client, long received, int batchNumber) + { + if (Math.abs(batchNumber - (_sent / _batchSize)) > _batchVariance) + { + _slowClients.put(client, received); + } + else + { + _slowClients.remove(client); + } + } + + /** Incrment the number of sent messages and then sleep, if required. */ + public void sentMessage() + { + + _sent++; + + if (_delay > (TEN_MILLI_SEC * _batchSize)) + { + long batchDelay = _delay / _batchSize; + // less than 10ms sleep doesn't always work. + // _delay is in nano seconds + // if (batchDelay < (TEN_MILLI_SEC)) + // { + // sleep(0, (int) batchDelay); + // } + // else + { + // if (batchDelay < 30000000000L) + { + sleepLong(batchDelay); + } + } + } + else + { + if (SLEEP_PER_MESSAGE && (_delay > 0)) + { + sleepLong(_delay / _batchSize); + } + } + } + + /** + * Check at the end of each batch and pause sending messages to allow slow clients to catch up. + * + * @return true if there were slow clients that caught up. + */ + private boolean checkForSlowClients() + { + // This will allways be true as we are running this at the end of each batchSize + // if (_sent % _batchSize == 0) + { + // Cause test to pause when we have slow + if (!_slowClients.isEmpty() || NO_CLIENTS) + { + + while (!_slowClients.isEmpty()) + { + if (log.isInfoEnabled() && ((_sent / _batchSize % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + String clients = ""; + Iterator it = _slowClients.keySet().iterator(); + while (it.hasNext()) + { + clients += it.next(); + if (it.hasNext()) + { + clients += ", "; + } + } + + log.info("Pausing for slow clients:" + clients); + } + + if (console.isDebugEnabled() && ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0)) + { + console.debug(_slowClients.size() + " slow clients."); + } + + sleep(PAUSE_SLEEP); + } + + if (NO_CLIENTS) + { + sleep(NO_CLIENT_SLEEP); + } + + log.debug("Continuing"); + + return true; + } + else + { + if ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0) + { + console.info("Total Delay :" + _delay + " " + + ((_delayShifting == 0) ? "Stablised" : ("Not Stablised(" + _delayShifting + ")"))); + } + } + + } + + return false; + } + + /** + * Sleep normally takes micro-seconds this allows the use of a nano-second value. + * + * @param delay nanoseconds to sleep for. + */ + private void sleepLong(long delay) + { + sleep(delay / 1000000, (int) (delay % 1000000)); + } + + /** + * Sleep for the specified micro-seconds. + * @param sleep microseconds to sleep for. + */ + private void sleep(long sleep) + { + sleep(sleep, 0); + } + + /** + * Perform the sleep , swallowing any InteruptException. + * + * NOTE: If a sleep request is > 10s then reset only sleep for 5s + * + * @param milli to sleep for + * @param nano sub miliseconds to sleep for + */ + private void sleep(long milli, int nano) + { + try + { + log.debug("Sleep:" + milli + ":" + nano); + if (milli > 10000) + { + + if (_delay == milli) + { + _totalDuration = _totalReceived / _batchSize * EXPECTED_TIME_PER_BATCH; + log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + + "s. Reset _totalDuration:" + _totalDuration); + } + else + { + log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + "s"); + } + + milli = 5000; + } + + Thread.sleep(milli, nano); + } + catch (InterruptedException e) + { + // + } + } + + public void setClient(SustainedClientTestCase client) + { + _client = client; + } + } + +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java new file mode 100644 index 0000000000..36f9b4eaf1 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java @@ -0,0 +1,126 @@ +/* + * + * 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.sustained; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.test.framework.distributedtesting.DistributedTestCase; +import org.apache.qpid.test.framework.DropInTest; + +import javax.jms.JMSException; +import javax.jms.Message; + +import java.util.Properties; + +/** + * SustainedTestCase is a {@link org.apache.qpid.test.framework.distributedtesting.DistributedTestCase} that runs the "Perf_SustainedPubSub" test case. This consists of one + * test client sending, and several receiving, and attempts to find the highest rate at which messages can be broadcast + * to the receivers. It is also a {@link DropInTest} to which more test clients may be added during a test run. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
+ *
+ */ +public class SustainedTestCase extends DistributedTestCase implements DropInTest +{ + /** Used for debugging. */ + Logger log = Logger.getLogger(SustainedTestCase.class); + + /** Holds the root name of the topic on which to send the test messages. */ + private static final String SUSTAINED_KEY = "Perf_SustainedPubSub"; + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public SustainedTestCase(String name) + { + super(name); + } + + /** + * Performs a single test run of the sustained test. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicPubSub() throws Exception + { + log.debug("public void testSinglePubSubCycle(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "Perf_SustainedPubSub"); + testConfig.put("SUSTAINED_KEY", SUSTAINED_KEY); + testConfig.put("SUSTAINED_NUM_RECEIVERS", Integer.getInteger("numReceives", 2)); + testConfig.put("SUSTAINED_UPDATE_INTERVAL", Integer.getInteger("batchSize", 1000)); + testConfig.put("SUSTAINED_UPDATE_KEY", SUSTAINED_KEY + ".UPDATE"); + testConfig.put("ACKNOWLEDGE_MODE", Integer.getInteger("ackMode", AMQSession.AUTO_ACKNOWLEDGE)); + + log.info("Created Config: " + testConfig.entrySet().toArray()); + + getTestSequencer().sequenceTest(null, null, testConfig); + } + + /** + * Accepts a late joining client into this test case. The client will be enlisted with a control message + * with the 'CONTROL_TYPE' field set to the value 'LATEJOIN'. It should also provide values for the fields: + * + *

+ *
CLIENT_NAME A unique name for the new client. + *
CLIENT_PRIVATE_CONTROL_KEY The key for the route on which the client receives its control messages. + *
+ * + * @param message The late joiners join message. + * + * @throws JMSException Any JMS Exception are allowed to fall through, indicating that the join failed. + */ + public void lateJoin(Message message) throws JMSException + { + throw new RuntimeException("Not implemented."); + /* + // Extract the joining clients details from its join request message. + TestClientDetails clientDetails = new TestClientDetails(); + clientDetails.clientName = message.getStringProperty("CLIENT_NAME"); + clientDetails.privateControlKey = message.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); + + // Register the joining client, but do block for confirmation as cannot do a synchronous receivers during this + // method call, as it may have been called from an 'onMessage' method. + assignReceiverRole(clientDetails, new Properties(), false); + */ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as known to the test + * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test + * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case + * name "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "Perf_SustainedPubSub"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestClient.java index 6f2089290a..79707bafa5 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestClient.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestClient.java @@ -425,7 +425,7 @@ public class SustainedTestClient extends TestCase3BasicPubSub implements Excepti class SustainedRateAdapter implements MessageListener, Runnable { private SustainedTestClient _client; - private long _batchVariance = 3; //no. batches to allow drifting + private long _batchVariance = Integer.getInteger("batchVariance", 3); //no. batches to allow drifting private long _timeVariance = TEN_MILLI_SEC * 5; // no. nanos between send and report delay (10ms) private volatile long _delay; //in nanos private long _sent; @@ -434,9 +434,11 @@ public class SustainedTestClient extends TestCase3BasicPubSub implements Excepti private static final long NO_CLIENT_SLEEP = 1000; // 1s private volatile boolean NO_CLIENTS = true; private int _delayShifting; - private static final int REPORTS_WITHOUT_CHANGE = 5; + private final int REPORTS_WITHOUT_CHANGE = Integer.getInteger("stableReportCount", 5); private boolean _warmedup = false; private static final long EXPECTED_TIME_PER_BATCH = 100000L; + private int _warmUpBatches = Integer.getInteger("warmUpBatches", 10); + SustainedRateAdapter(SustainedTestClient client) { @@ -493,8 +495,6 @@ public class SustainedTestClient extends TestCase3BasicPubSub implements Excepti CountDownLatch _warmup = new CountDownLatch(1); - int _warmUpBatches = Integer.getInteger("warmUpBatches", 10); - int _numBatches = 10000; // long[] _timings = new long[_numBatches]; diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCoordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCoordinator.java deleted file mode 100644 index 0075e45a8c..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCoordinator.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * 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.sustained; - -import org.apache.log4j.Logger; -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.interop.coordinator.ListeningCoordinatorTest; -import org.apache.qpid.interop.coordinator.TestClientDetails; -import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase3BasicPubSub; -import org.apache.qpid.util.ConversationFactory; - -import javax.jms.Destination; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.Session; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -public class SustainedTestCoordinator extends CoordinatingTestCase3BasicPubSub implements ListeningCoordinatorTest -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(SustainedTestCoordinator.class); - private List _receivers; - private static final String SUSTAINED_KEY = "Perf_SustainedPubSub"; - Map _testProperties; - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public SustainedTestCoordinator(String name) - { - super(name); - _receivers = new LinkedList(); - } - - /** - * Adds a receiver to this test. - * - * @param receiver The contact details of the sending client in the test. - */ - public void setReceiver(TestClientDetails receiver) - { - _receivers.add(receiver); - } - - - /** - * Performs the a single test run - * - * @throws Exception if there was a problem running the test. - */ - public void testBasicPubSub() throws Exception - { - log.debug("public void testSinglePubSubCycle(): called"); - - Map testConfig = new HashMap(); - testConfig.put("TEST_NAME", "Perf_SustainedPubSub"); - testConfig.put("SUSTAINED_KEY", SUSTAINED_KEY); - testConfig.put("SUSTAINED_NUM_RECEIVERS", Integer.getInteger("numReceives", 2)); - testConfig.put("SUSTAINED_UPDATE_INTERVAL", Integer.getInteger("batchSize", 1000)); - testConfig.put("SUSTAINED_UPDATE_KEY", SUSTAINED_KEY + ".UPDATE"); - testConfig.put("ACKNOWLEDGE_MODE", Integer.getInteger("ackMode", AMQSession.AUTO_ACKNOWLEDGE)); - - log.info("Created Config: " + testConfig.entrySet().toArray()); - - sequenceTest(testConfig); - } - - /** - * 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 assigning the test roles, begining the test and gathering the test reports from the - * participants. - * - * @param testProperties The test case definition. - * - * @return The test results from the senders and receivers. - * - * @throws javax.jms.JMSException All underlying JMSExceptions are allowed to fall through. - */ - protected Message[] sequenceTest(Map testProperties) throws JMSException - { - log.debug("protected Message[] sequenceTest(Object... testProperties = " + testProperties + "): called"); - - Session session = conversationFactory.getSession(); - Destination senderControlTopic = session.createTopic(sender.privateControlKey); - - ConversationFactory.Conversation senderConversation = conversationFactory.startConversation(); - - // Assign the sender role to the sending test client. - Message assignSender = conversationFactory.getSession().createMessage(); - setPropertiesOnMessage(assignSender, testProperties); - assignSender.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); - assignSender.setStringProperty("ROLE", "SENDER"); - assignSender.setStringProperty("CLIENT_NAME", "Sustained_SENDER"); - - senderConversation.send(senderControlTopic, assignSender); - - //Assign and wait for the receiver ckuebts to be ready. - _testProperties = testProperties; - - // Wait for the senders to confirm their roles. - senderConversation.receive(); - - assignReceivers(); - - // Start the test. - Message start = session.createMessage(); - start.setStringProperty("CONTROL_TYPE", "START"); - - senderConversation.send(senderControlTopic, start); - - // Wait for the test sender to return its report. - Message senderReport = senderConversation.receive(); - - try - { - Thread.sleep(500); - } - catch (InterruptedException e) - { - } - - // Ask the receiver for its report. - Message statusRequest = session.createMessage(); - statusRequest.setStringProperty("CONTROL_TYPE", "STATUS_REQUEST"); - - - return new Message[]{senderReport}; - } - - private void assignReceivers() - { - for (TestClientDetails receiver : _receivers) - { - registerReceiver(receiver); - } - } - - private void registerReceiver(TestClientDetails receiver) - { - log.info("registerReceiver called for receiver:" + receiver); - try - { - Session session = conversationFactory.getSession(); - Destination receiverControlTopic = session.createTopic(receiver.privateControlKey); - ConversationFactory.Conversation receiverConversation = conversationFactory.startConversation(); - // Assign the receiver role the receiving client. - Message assignReceiver = session.createMessage(); - setPropertiesOnMessage(assignReceiver, _testProperties); - assignReceiver.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); - assignReceiver.setStringProperty("ROLE", "RECEIVER"); - assignReceiver.setStringProperty("CLIENT_NAME", "Sustained_RECEIVER_" + receiver.clientName); - - receiverConversation.send(receiverControlTopic, assignReceiver); - - //Don't wait for receiver to be ready.... we can't this is being done in - // the dispatcher thread, and most likely the acceptance message we - // want is sitting in the Dispatcher._queue waiting its turn for being - // dispatched so if we block here we won't can't get the message. - // So assume consumer is ready for action. - //receiverConversation.receive(); - } - catch (JMSException e) - { - log.warn("Unable to assign receiver:" + receiver + ". Due to:" + e.getMessage()); - } - } - - public void latejoin(Message message) - { - try - { - - TestClientDetails clientDetails = new TestClientDetails(); - clientDetails.clientName = message.getStringProperty("CLIENT_NAME"); - clientDetails.privateControlKey = message.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); - - - registerReceiver(clientDetails); - } - catch (JMSException e) - { - //swallow - } - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the interop - * testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "Perf_SustainedPubSub"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestClient.java deleted file mode 100644 index 44fc090410..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestClient.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * 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.sustained; - -import org.apache.log4j.Logger; -import org.apache.qpid.interop.testclient.InteropClientTestCase; -import org.apache.qpid.util.CommandLineParser; - -import javax.jms.JMSException; -import javax.jms.Message; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Properties; - -public class TestClient extends org.apache.qpid.interop.testclient.TestClient -{ - private static Logger log = Logger.getLogger(TestClient.class); - - /** - * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client - * identifying name. - * - * @param brokerUrl The url of the broker to connect to. - * @param virtualHost The virtual host to conect to. - * @param clientName The client name to use. - */ - public TestClient(String brokerUrl, String virtualHost, String clientName) - { - super(brokerUrl, virtualHost, clientName); - } - - /** - * The entry point for the interop test coordinator. This client accepts the following command line arguments: - * - *

-b The broker URL. Optional.
-h The virtual - * host. Optional.
-n The test client name. Optional.
name=value - * Trailing argument define name/value pairs. Added to system properties. Optional.
- * - * @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", "false"}, - {"h", "The virtual host to use.", "virtual host", "false"}, - {"n", "The test client name.", "name", "false"}, - {"j", "Join this test client to running test.", "join", ""} - }); - - // 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"); - String clientName = options.getProperty("n"); - String join = options.getProperty("j"); - - // 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, (clientName == null) ? CLIENT_NAME : clientName); - - // Use a class path scanner to find all the interop test case implementations. - Collection> testCaseClasses = - new ArrayList>(); - // ClasspathScanner.getMatches(InteropClientTestCase.class, "^TestCase.*", true); - // Hard code the test classes till the classpath scanner is fixed. - Collections.addAll(testCaseClasses, - SustainedTestClient.class); - - - try - { - client.start(testCaseClasses, join); - } - catch (Exception e) - { - log.error("The test client was unable to start.", e); - System.exit(1); - } - } - - protected void start(Collection> testCaseClasses, String join) throws JMSException, ClassNotFoundException - { - super.start(testCaseClasses); - log.debug("private void start(): called"); - - if (join != null && !join.equals("")) - { - Message latejoin = session.createMessage(); - - try - { - Object test = Class.forName(join).newInstance(); - if (test instanceof InteropClientTestCase) - { - currentTestCase = (InteropClientTestCase) test; - } - else - { - throw new RuntimeException("Requested to join class '" + join + "' but this is not a InteropClientTestCase."); - } - - latejoin.setStringProperty("CONTROL_TYPE", "LATEJOIN"); - latejoin.setStringProperty("CLIENT_NAME", clientName); - latejoin.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); - producer.send(session.createTopic("iop.control.test." + currentTestCase.getName()), latejoin); - } - catch (InstantiationException e) - { - log.warn("Unable to request latejoining of test:" + currentTestCase); - } - catch (IllegalAccessException e) - { - log.warn("Unable to request latejoining of test:" + currentTestCase); - } - } - } - -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestCoordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestCoordinator.java deleted file mode 100644 index 7e12fe39fb..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestCoordinator.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.sustained; - -import org.apache.qpid.interop.coordinator.Coordinator; -import org.apache.qpid.interop.coordinator.ListeningTestDecorator; -import org.apache.qpid.interop.coordinator.TestClientDetails; -import org.apache.qpid.util.CommandLineParser; -import org.apache.qpid.util.ConversationFactory; -import org.apache.log4j.Logger; - -import java.util.Properties; -import java.util.Set; - -import junit.framework.TestResult; -import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; - -import javax.jms.Connection; - -public class TestCoordinator extends Coordinator -{ - - private static final Logger log = Logger.getLogger(TestCoordinator.class); - - /** - * 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 null. - */ - TestCoordinator(String brokerUrl, String virtualHost) - { - super(brokerUrl, virtualHost); - } - - protected WrappedSuiteTestDecorator newTestDecorator(WrappedSuiteTestDecorator targetTest, Set enlistedClients, ConversationFactory conversationFactory, Connection connection) - { - return new ListeningTestDecorator(targetTest, enlistedClients, conversationFactory, connection); - } - - - /** - * The entry point for the interop test coordinator. This client accepts the following command line arguments: - * - *

-b The broker URL. Mandatory.
-h The virtual host. - * Optional.
name=value Trailing argument define name/value pairs. Added to system properties. - * Optional.
- * - * @param args The command line arguments. - */ - public static void main(String[] args) - { - try - { - // Use the command line parser to evaluate the command line with standard handling behaviour (print errors - // and usage then exist if there are errors). - Properties options = - CommandLineParser.processCommandLine(args, - new CommandLineParser( - new String[][] - { - {"b", "The broker URL.", "broker", "false"}, - {"h", "The virtual host to use.", "virtual host", "false"}, - {"o", "The name of the directory to output test timings to.", "dir", "false"} - })); - - // Extract the command line options. - String brokerUrl = options.getProperty("b"); - String virtualHost = options.getProperty("h"); - String reportDir = options.getProperty("o"); - reportDir = (reportDir == null) ? "." : reportDir; - - - String[] testClassNames = {SustainedTestCoordinator.class.getName()}; - - // Create a coordinator and begin its test procedure. - Coordinator coordinator = new TestCoordinator(brokerUrl, virtualHost); - - coordinator.setReportDir(reportDir); - - TestResult testResult = coordinator.start(testClassNames); - - if (testResult.failureCount() > 0) - { - System.exit(FAILURE_EXIT); - } - else - { - System.exit(SUCCESS_EXIT); - } - } - catch (Exception e) - { - System.err.println(e.getMessage()); - log.error("Top level handler caught execption.", e); - System.exit(EXCEPTION_EXIT); - } - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropClientTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropClientTestCase.java new file mode 100644 index 0000000000..5e6d61a9e0 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropClientTestCase.java @@ -0,0 +1,101 @@ +/* + * + * 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.test.framework.distributedtesting; + +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. + * + *

+ *
CRC Card
Responsibilities + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Perform test case actions. + *
Generate test reports. + *
+ */ +public interface InteropClientTestCase extends MessageListener +{ + /** Defines the possible test case roles that an interop test case can take on. */ + public enum Roles + { + /** Specifies the sender role. */ + SENDER, + + /** Specifies the receivers role. */ + 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 true to accept the invitation, false 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 receivers. + * @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. Returning from here, indicates that the sending role has completed its test. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start() throws JMSException; + + /** + * 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/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java new file mode 100644 index 0000000000..12c0d0aa69 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java @@ -0,0 +1,377 @@ +/* + * + * 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.test.framework.distributedtesting; + +import org.apache.log4j.Logger; + +import org.apache.qpid.interop.clienttestcases.TestCase1DummyRun; +import org.apache.qpid.interop.clienttestcases.TestCase2BasicP2P; +import org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub; +import org.apache.qpid.sustained.SustainedClientTestCase; +import org.apache.qpid.test.framework.MessagingTestConfigProperties; +import org.apache.qpid.test.framework.TestUtils; + +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; +import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; + +import javax.jms.*; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * 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.test.framework.distributedtesting.Coordinator}. + * + *

+ *
Messages Handled by SustainedTestClient
Message Action + *
Invite(compulsory) Reply with Enlist. + *
Invite(test case) Reply with Enlist if test case available. + *
AssignRole(test case) Reply with Accept Role if matches an enlisted test. Keep test parameters. + *
Start Send test messages defined by test parameters. Send report on messages sent. + *
Status Request Send report on messages received. + *
Terminate Terminate the test client. + *
+ * + *

+ *
CRC Card
Responsibilities Collaborations + *
Handle all incoming control messages. {@link InteropClientTestCase} + *
Configure and look up test cases by name. {@link InteropClientTestCase} + *
+ */ +public class TestClient implements MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestClient.class); + + /** Used for reporting to the console. */ + private static final Logger console = Logger.getLogger("CONSOLE"); + + /** Holds the default identifying name of the test client. */ + public static final String CLIENT_NAME = "java"; + + /** Holds the URL of the broker to run the tests on. */ + public static String brokerUrl; + + /** Holds the virtual host to run the tests on. If null, then the default virtual host is used. */ + public static String virtualHost; + + /** + * Holds the test context properties that provides the default test parameters, plus command line overrides. + * This is initialized with the default test parameters, to which command line overrides may be applied. + */ + public static ParsedProperties testContextProperties = + TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + /** Holds all the test cases loaded from the classpath. */ + Map testCases = new HashMap(); + + /** Holds the test case currently being run by this client. */ + protected InteropClientTestCase currentTestCase; + + /** Holds the connection to the broker that the test is being coordinated on. */ + protected Connection connection; + + /** Holds the message producer to hold the test coordination over. */ + protected MessageProducer producer; + + /** Holds the JMS session for the test coordination. */ + protected Session session; + + /** Holds the name of this client, with a default value. */ + protected String clientName = CLIENT_NAME; + + /** This flag indicates that the test client should attempt to join the currently running test case on start up. */ + protected boolean join; + + /** + * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client + * identifying name. + * + * @param brokerUrl The url of the broker to connect to. + * @param virtualHost The virtual host to conect to. + * @param clientName The client name to use. + */ + public TestClient(String brokerUrl, String virtualHost, String clientName, boolean join) + { + log.debug("public SustainedTestClient(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost + + ", String clientName = " + clientName + "): called"); + + // Retain the connection parameters. + this.brokerUrl = brokerUrl; + this.virtualHost = virtualHost; + this.clientName = clientName; + this.join = join; + } + + /** + * The entry point for the interop test coordinator. This client accepts the following command line arguments: + * + *

+ *
-b The broker URL. Optional. + *
-h The virtual host. Optional. + *
-n The test client name. Optional. + *
name=value Trailing argument define name/value pairs. Added to system properties. Optional. + *
+ * + * @param args The command line arguments. + */ + public static void main(String[] args) + { + // Override the default broker url to be localhost:5672. + testContextProperties.setProperty(MessagingTestConfigProperties.BROKER_PROPNAME, "tcp://localhost:5672"); + + // Use the command line parser to evaluate the command line with standard handling behaviour (print errors + // and usage then exist if there are errors). + // Any options and trailing name=value pairs are also injected into the test context properties object, + // to override any defaults that may have been set up. + ParsedProperties options = + new ParsedProperties(uk.co.thebadgerset.junit.extensions.util.CommandLineParser.processCommandLine(args, + new uk.co.thebadgerset.junit.extensions.util.CommandLineParser( + new String[][] + { + { "b", "The broker URL.", "broker", "false" }, + { "h", "The virtual host to use.", "virtual host", "false" }, + { "o", "The name of the directory to output test timings to.", "dir", "false" }, + { "n", "The name of the test client.", "name", "false" }, + { "j", "Join this test client to running test.", "false" } + }), testContextProperties)); + + // Extract the command line options. + String brokerUrl = options.getProperty("b"); + String virtualHost = options.getProperty("h"); + String clientName = options.getProperty("n"); + boolean join = options.getPropertyAsBoolean("j"); + + // Create a test client and start it running. + TestClient client = new TestClient(brokerUrl, virtualHost, (clientName == null) ? CLIENT_NAME : clientName, join); + + // Use a class path scanner to find all the interop test case implementations. + // Hard code the test classes till the classpath scanner is fixed. + Collection> testCaseClasses = + new ArrayList>(); + // ClasspathScanner.getMatches(InteropClientTestCase.class, "^TestCase.*", true); + Collections.addAll(testCaseClasses, TestCase1DummyRun.class, TestCase2BasicP2P.class, TestCase3BasicPubSub.class, + SustainedClientTestCase.class); + + try + { + client.start(testCaseClasses); + } + catch (Exception e) + { + log.error("The test client was unable to start.", e); + System.exit(1); + } + } + + /** + * Starts the interop test client running. This causes it to start listening for incoming test invites. + * + * @param testCaseClasses The classes of the available test cases. The test case names from these are used to + * matchin incoming test invites against. + * + * @throws JMSException Any underlying JMSExceptions are allowed to fall through. + */ + protected void start(Collection> testCaseClasses) throws JMSException + { + log.debug("private void start(): called"); + + // Create all the test case implementations and index them by the test names. + for (Class nextClass : testCaseClasses) + { + try + { + InteropClientTestCase testCase = nextClass.newInstance(); + testCases.put(testCase.getName(), testCase); + } + catch (InstantiationException e) + { + log.warn("Could not instantiate test case class: " + nextClass.getName(), e); + // Ignored. + } + catch (IllegalAccessException e) + { + log.warn("Could not instantiate test case class due to illegal access: " + nextClass.getName(), e); + // Ignored. + } + } + + // Open a connection to communicate with the coordinator on. + connection = TestUtils.createConnection(testContextProperties); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Set this up to listen for control messages. + Topic privateControlTopic = session.createTopic("iop.control." + clientName); + MessageConsumer consumer = session.createConsumer(privateControlTopic); + consumer.setMessageListener(this); + + Topic controlTopic = session.createTopic("iop.control"); + MessageConsumer consumer2 = session.createConsumer(controlTopic); + consumer2.setMessageListener(this); + + // Create a producer to send replies with. + producer = session.createProducer(null); + + // If the join flag was set, then broadcast a join message to notify the coordinator that a new test client + // is available to join the current test case, if it supports it. This message may be ignored, or it may result + // in this test client receiving a test invite. + if (join) + { + Message joinMessage = session.createMessage(); + + joinMessage.setStringProperty("CONTROL_TYPE", "JOIN"); + joinMessage.setStringProperty("CLIENT_NAME", clientName); + joinMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); + producer.send(controlTopic, joinMessage); + } + + // Start listening for incoming control messages. + connection.start(); + } + + /** + * Handles all incoming control messages. + * + * @param message The incoming message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + try + { + String controlType = message.getStringProperty("CONTROL_TYPE"); + String testName = message.getStringProperty("TEST_NAME"); + + log.info("onMessage(Message message = " + message + "): for '" + controlType + "' to '" + testName + "'"); + + // Check if the message is a test invite. + if ("INVITE".equals(controlType)) + { + // Flag used to indicate that an enlist should be sent. Only enlist to compulsory invites or invites + // for which test cases exist. + boolean enlist = false; + + if (testName != null) + { + log.debug("Got an invite to test: " + testName); + + // Check if the requested test case is available. + InteropClientTestCase testCase = testCases.get(testName); + + if (testCase != null) + { + // Make the requested test case the current test case. + currentTestCase = testCase; + enlist = true; + } + else + { + log.debug("Received an invite to the test '" + testName + "' but this test is not known."); + } + } + else + { + log.debug("Got a compulsory invite."); + + enlist = true; + } + + if (enlist) + { + // Reply with the client name in an Enlist message. + Message enlistMessage = session.createMessage(); + enlistMessage.setStringProperty("CONTROL_TYPE", "ENLIST"); + enlistMessage.setStringProperty("CLIENT_NAME", clientName); + enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); + enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.info("Sending Message '" + enlistMessage + "'. to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), enlistMessage); + } + } + else if ("ASSIGN_ROLE".equals(controlType)) + { + // Assign the role to the current test case. + String roleName = message.getStringProperty("ROLE"); + + log.debug("Got a role assignment to role: " + roleName); + + InteropClientTestCase.Roles role = Enum.valueOf(InteropClientTestCase.Roles.class, roleName); + + currentTestCase.assignRole(role, message); + + // Reply by accepting the role in an Accept Role message. + Message acceptRoleMessage = session.createMessage(); + acceptRoleMessage.setStringProperty("CONTROL_TYPE", "ACCEPT_ROLE"); + acceptRoleMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + producer.send(message.getJMSReplyTo(), acceptRoleMessage); + } + else if ("START".equals(controlType) || "STATUS_REQUEST".equals(controlType)) + { + if ("START".equals(controlType)) + { + log.debug("Got a start notification."); + + // Start the current test case. + currentTestCase.start(); + } + else + { + log.debug("Got a status request."); + } + + // Generate the report from the test case and reply with it as a Report message. + Message reportMessage = currentTestCase.getReport(session); + reportMessage.setStringProperty("CONTROL_TYPE", "REPORT"); + reportMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + producer.send(message.getJMSReplyTo(), reportMessage); + } + else if ("TERMINATE".equals(controlType)) + { + log.info("Received termination instruction from coordinator."); + + // Is a cleaner shutdown needed? + connection.close(); + System.exit(0); + } + else + { + // Log a warning about this but otherwise ignore it. + log.warn("Got an unknown control message, controlType = " + controlType + ", message = " + message); + } + } + catch (JMSException e) + { + // Log a warning about this, but otherwise ignore it. + log.warn("A JMSException occurred whilst handling a message."); + log.debug("Got JMSException whilst handling message: " + message, e); + } + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java deleted file mode 100644 index bad49060ca..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * - * 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.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.log4j.Logger; - -/** - * An ClasspathScanner scans the classpath for classes that implement an interface or extend a base class and have names - * that match a regular expression. - * - *

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. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Find all classes matching type and name pattern on the classpath. - *
- * - * @todo Add logic to scan jars as well as directories. - */ -public class ClasspathScanner -{ - private static final Logger log = Logger.getLogger(ClasspathScanner.class); - - /** - * 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 regular 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> getMatches(Class matchingClass, String matchingRegexp, - boolean beanOnly) - { - log.debug("public static Collection> getMatches(Class matchingClass = " + matchingClass - + ", String matchingRegexp = " + matchingRegexp + ", boolean beanOnly = " + beanOnly + "): called"); - - // Build a compiled regular expression from the pattern to match. - Pattern matchPattern = Pattern.compile(matchingRegexp); - - String classPath = System.getProperty("java.class.path"); - Map> result = new HashMap>(); - - log.debug("classPath = " + classPath); - - // Find matching classes starting from all roots in the classpath. - for (String path : splitClassPath(classPath)) - { - gatherFiles(new File(path), "", result, matchPattern, matchingClass); - } - - return result.values(); - } - - /** - * Finds all matching classes rooted at a given location in the file system. If location is a directory it - * is recursively examined. - * - * @param classRoot The root of the current point in the file system being examined. - * @param classFileName The name of the current file or directory to examine. - * @param result The accumulated mapping from class names to classes that match the scan. - * - * @todo Recursion ok as file system depth is not likely to exhaust the stack. Might be better to replace with - * iteration. - */ - private static void gatherFiles(File classRoot, String classFileName, Map> result, - Pattern matchPattern, Class matchClass) - { - log.debug("private static void gatherFiles(File classRoot = " + classRoot + ", String classFileName = " - + classFileName + ", Map> result, Pattern matchPattern = " + matchPattern - + ", Class matchClass = " + matchClass + "): called"); - - File thisRoot = new File(classRoot, classFileName); - - // If the current location is a file, check if it is a matching class. - if (thisRoot.isFile()) - { - // Check that the file has a matching name. - if (matchesName(thisRoot.getName(), matchPattern)) - { - String className = classNameFromFile(thisRoot.getName()); - - // Check that the class has matching type. - try - { - Class candidateClass = Class.forName(className); - - Class matchedClass = matchesClass(candidateClass, matchClass); - - if (matchedClass != null) - { - result.put(className, matchedClass); - } - } - catch (ClassNotFoundException e) - { - // Ignore this. The matching class could not be loaded. - log.debug("Got ClassNotFoundException, ignoring.", e); - } - } - - return; - } - // Otherwise the current location is a directory, so examine all of its contents. - else - { - String[] contents = thisRoot.list(); - - if (contents != null) - { - for (String content : contents) - { - gatherFiles(classRoot, classFileName + File.separatorChar + content, result, matchPattern, matchClass); - } - } - } - } - - /** - * Checks if the specified class file name corresponds to a class with name matching the specified regular expression. - * - * @param classFileName The class file name. - * @param matchPattern The regular expression pattern to match. - * - * @return true if the class name matches, false otherwise. - */ - private static boolean matchesName(String classFileName, Pattern matchPattern) - { - String className = classNameFromFile(classFileName); - Matcher matcher = matchPattern.matcher(className); - - return matcher.matches(); - } - - /** - * Checks if the specified class to compare extends the base class being scanned for. - * - * @param matchingClass The base class to match against. - * @param toMatch The class to match against the base class. - * - * @return The class to check, cast as an instance of the class to match if the class extends the base class, or - * null otherwise. - */ - private static Class matchesClass(Class matchingClass, Class toMatch) - { - try - { - return matchingClass.asSubclass(toMatch); - } - catch (ClassCastException e) - { - return null; - } - } - - /** - * 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 splitClassPath(String classPath) - { - List result = new LinkedList(); - String separator = System.getProperty("path.separator"); - StringTokenizer tokenizer = new StringTokenizer(classPath, separator); - - while (tokenizer.hasMoreTokens()) - { - result.add(tokenizer.nextToken()); - } - - return result; - } - - /** - * Translates from the filename of a class to its fully qualified classname. Files are named using forward slash - * seperators and end in ".class", whereas fully qualified class names use "." sperators and no ".class" ending. - * - * @param classFileName The filename of the class to translate to a class name. - * - * @return The fully qualified class name. - */ - private static String classNameFromFile(String classFileName) - { - log.debug("private static String classNameFromFile(String classFileName = " + classFileName + "): called"); - - // Remove the .class ending. - String s = classFileName.substring(0, classFileName.length() - ".class".length()); - - // Turn / seperators in . seperators. - String s2 = s.replace(File.separatorChar, '.'); - - // Knock off any leading . caused by a leading /. - if (s2.startsWith(".")) - { - return s2.substring(1); - } - - return s2; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationFactory.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationFactory.java deleted file mode 100644 index 0090bec3d0..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationFactory.java +++ /dev/null @@ -1,479 +0,0 @@ -/* - * - * 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 org.apache.log4j.Logger; - -import javax.jms.*; - -import java.util.*; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -/** - * 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. - * - *

One use of this is 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 conversation - * 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): - * - *

- * class Initiator
- * {
- * ConversationHelper conversation = new ConversationHelper(connection, null,
- *                                                          java.util.concurrent.LinkedBlockingQueue.class);
- *
- * initiateConversation()
- * {
- *  try {
- *   // Exchange greetings.
- *   conversation.send(sendDestination, conversation.getSession().createTextMessage("Hello."));
- *   Message greeting = conversation.receive();
- *
- *   // Exchange goodbyes.
- *   conversation.send(conversation.getSession().createTextMessage("Goodbye."));
- *   Message goodbye = conversation.receive();
- *  } finally {
- *   conversation.end();
- *  }
- * }
- * }
- *
- * class Responder
- * {
- * ConversationHelper conversation = new ConversationHelper(connection, receiveDestination,
- *                                                          java.util.concurrent.LinkedBlockingQueue.class);
- *
- * 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();
- *  }
- * }
- * }
- * 
- * - *

Conversation correlation id's are generated on a per thread basis. - * - *

The same session is shared amongst all conversations. Calls to send are therefore synchronized because JMS - * sessions are not multi-threaded. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Associate messages to an ongoing conversation using correlation ids. - *
Auto manage sessions for conversations. - *
Store messages not in a conversation in dead letter box. - *
- */ -public class ConversationFactory -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(ConversationFactory.class); - - /** Holds a map from correlation id's to queues. */ - private Map> idsToQueues = new HashMap>(); - - /** Holds the connection over which the conversation is conducted. */ - private Connection connection; - - /** Holds the session over which the conversation is conduxted. */ - private Session session; - - /** The message consumer for incoming messages. */ - MessageConsumer consumer; - - /** The message producer for outgoing messages. */ - MessageProducer producer; - - /** The well-known or temporary destination to receive replies on. */ - Destination receiveDestination; - - /** Holds the queue implementation class for the reply queue. */ - Class queueClass; - - /** Used to hold any replies that are received outside of the context of a conversation. */ - BlockingQueue deadLetterBox = new LinkedBlockingQueue(); - - /* Used to hold conversation state on a per thread basis. */ - /* - ThreadLocal threadLocals = - new ThreadLocal() - { - protected Conversation initialValue() - { - Conversation settings = new Conversation(); - settings.conversationId = conversationIdGenerator.getAndIncrement(); - - return settings; - } - }; - */ - - /** Generates new coversation id's as needed. */ - AtomicLong conversationIdGenerator = new AtomicLong(); - - /** - * 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 receiveDestination The destination to listen to for incoming messages. This may be null to use a temporary - * queue. - * @param queueClass The queue implementation class. - * - * @throws JMSException All underlying JMSExceptions are allowed to fall through. - */ - public ConversationFactory(Connection connection, Destination receiveDestination, - Class queueClass) throws JMSException - { - log.debug("public ConversationFactory(Connection connection, Destination receiveDestination = " + receiveDestination - + ", Class queueClass = " + queueClass + "): called"); - - this.connection = connection; - this.queueClass = queueClass; - - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Check if a well-known receive destination has been provided, or use a temporary queue if not. - this.receiveDestination = (receiveDestination != null) ? receiveDestination : session.createTemporaryQueue(); - - consumer = session.createConsumer(receiveDestination); - producer = session.createProducer(null); - - consumer.setMessageListener(new Receiver()); - } - - /** - * Creates a new conversation context. - * - * @return A new conversation context. - */ - public Conversation startConversation() - { - log.debug("public Conversation startConversation(): called"); - - Conversation conversation = new Conversation(); - conversation.conversationId = conversationIdGenerator.getAndIncrement(); - - return conversation; - } - - /** - * Ensures that the reply queue for a conversation exists. - * - * @param conversationId The conversation correlation id. - */ - private void initQueueForId(long conversationId) - { - if (!idsToQueues.containsKey(conversationId)) - { - idsToQueues.put(conversationId, ReflectionUtils.newInstance(queueClass)); - } - } - - /** - * Clears the dead letter box, returning all messages that were in it. - * - * @return All messages in the dead letter box. - */ - public Collection emptyDeadLetterBox() - { - log.debug("public Collection emptyDeadLetterBox(): called"); - - Collection result = new ArrayList(); - deadLetterBox.drainTo(result); - - return result; - } - - /** - * Gets the session over which the conversation is conducted. - * - * @return The session over which the conversation is conducted. - */ - public Session getSession() - { - // Conversation settings = threadLocals.get(); - - return session; - } - - /** - * Used to hold a conversation context. This consists of a correlating id for the conversation, and a reply - * destination automatically updated to the last received reply-to destination. - */ - public class Conversation - { - /** Holds the correlation id for the context. */ - long conversationId; - - /** - * Holds the send destination for the context. This will automatically be updated to the most recently received - * reply-to destination. - */ - Destination sendDestination; - - /** - * 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 sendDestination The destination to send to. This may be null to use the last received reply-to - * destination. - * @param message The message to send. - * - * @throws JMSException All undelying JMSExceptions are allowed to fall through. This will also be thrown if no - * send destination is specified and there is no most recent reply-to destination available - * to use. - */ - public void send(Destination sendDestination, Message message) throws JMSException - { - log.debug("public void send(Destination sendDestination = " + sendDestination + ", Message message = " + message - + "): called"); - - // Conversation settings = threadLocals.get(); - // long conversationId = conversationId; - message.setJMSCorrelationID(Long.toString(conversationId)); - message.setJMSReplyTo(receiveDestination); - - // Ensure that the reply queue for this conversation exists. - initQueueForId(conversationId); - - // Check if an overriding send to destination has been set or use the last reply-to if not. - Destination sendTo = null; - - if (sendDestination != null) - { - sendTo = sendDestination; - } - else if (sendDestination != null) - { - sendTo = sendDestination; - } - else - { - throw new JMSException("The send destination was specified, and no most recent reply-to available to use."); - } - - // Send the message. - synchronized (this) - { - producer.send(sendTo, 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. - * - * @throws JMSException All undelying JMSExceptions are allowed to fall through. Thrown if the received message - * did not have its reply-to destination set up. - */ - public Message receive() throws JMSException - { - log.debug("public Message receive(): called"); - - // Conversation settings = threadLocals.get(); - // long conversationId = settings.conversationId; - - // Ensure that the reply queue for this conversation exists. - initQueueForId(conversationId); - - BlockingQueue queue = idsToQueues.get(conversationId); - - try - { - Message result = queue.take(); - - // Keep the reply-to destination to send replies to. - sendDestination = result.getJMSReplyTo(); - - return result; - } - catch (InterruptedException e) - { - return null; - } - } - - /** - * Gets many messages in an ongoing conversation. If a limit is specified, then once that many messages are - * received they will be returned. If a timeout is specified, then all messages up to the limit, received within - * that timespan will be returned. At least one of the message count or timeout should be set to a value of - * 1 or greater. - * - * @param num The number of messages to receive, or all if this is less than 1. - * @param timeout The timeout in milliseconds to receive the messages in, or forever if this is less than 1. - * - * @return All messages received within the count limit and the timeout. - * - * @throws JMSException All undelying JMSExceptions are allowed to fall through. - */ - public Collection receiveAll(int num, long timeout) throws JMSException - { - log.debug("public Collection receiveAll(int num = " + num + ", long timeout = " + timeout - + "): called"); - - // Check that a timeout or message count was set. - if ((num < 1) && (timeout < 1)) - { - throw new IllegalArgumentException("At least one of message count (num) or timeout must be set."); - } - - // Ensure that the reply queue for this conversation exists. - initQueueForId(conversationId); - BlockingQueue queue = idsToQueues.get(conversationId); - - // Used to collect the received messages in. - Collection result = new ArrayList(); - - // Used to indicate when the timeout or message count has expired. - boolean receiveMore = true; - - int messageCount = 0; - - // Receive messages until the timeout or message count expires. - do - { - try - { - Message next = null; - - // Try to receive the message with a timeout if one has been set. - if (timeout > 0) - { - next = queue.poll(timeout, TimeUnit.MILLISECONDS); - - // Check if the timeout expired, and stop receiving if so. - if (next == null) - { - receiveMore = false; - } - } - // Receive the message without a timeout. - else - { - next = queue.take(); - } - - // Increment the message count if a message was received. - messageCount += (next != null) ? 1 : 0; - - // Check if all the requested messages were received, and stop receiving if so. - if ((num > 0) && (messageCount >= num)) - { - receiveMore = false; - } - - // Keep the reply-to destination to send replies to. - sendDestination = (next != null) ? next.getJMSReplyTo() : sendDestination; - - if (next != null) - { - result.add(next); - } - } - catch (InterruptedException e) - { - // Restore the threads interrupted status. - Thread.currentThread().interrupt(); - - // Stop receiving but return the messages received so far. - receiveMore = false; - } - } - while (receiveMore); - - return result; - } - - /** - * Completes the conversation. 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() - { - log.debug("public void end(): called"); - - // Ensure that the thread local for the current thread is cleaned up. - // Conversation settings = threadLocals.get(); - // long conversationId = settings.conversationId; - // threadLocals.remove(); - - // Ensure that its queue is removed from the queue map. - BlockingQueue queue = idsToQueues.remove(conversationId); - - // Move any outstanding messages on the threads conversation id into the dead letter box. - queue.drainTo(deadLetterBox); - } - } - - /** - * 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) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - try - { - Long conversationId = Long.parseLong(message.getJMSCorrelationID()); - - // Find the converstaion queue to place the message on. If there is no conversation for the message id, - // the the dead letter box queue is used. - BlockingQueue queue = idsToQueues.get(conversationId); - queue = (queue == null) ? deadLetterBox : queue; - - queue.put(message); - } - catch (JMSException e) - { - throw new RuntimeException(e); - } - catch (InterruptedException e) - { - throw new RuntimeException(e); - } - } - } -} diff --git a/qpid/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties b/qpid/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties deleted file mode 100644 index a5fb611dfa..0000000000 --- a/qpid/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# 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. -# -java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory -connectionfactory.broker = amqp://guest:guest@clientid/?brokerlist='tcp://localhost:5672' -- cgit v1.2.1 From c06860ce45b4f52be1ba934fd4d92da10c9cc25f Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 31 Jul 2007 22:34:12 +0000 Subject: Rolled back revision 561365 and commented out some broken code in ClientSession.java. The trunk should now build. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@561578 13f79535-47bb-0310-9956-ffa450edef68 --- .../docs/RunningSustainedTests.txt | 14 +- qpid/java/integrationtests/pom.xml | 6 +- .../interop/clienttestcases/TestCase1DummyRun.java | 133 --- .../interop/clienttestcases/TestCase2BasicP2P.java | 207 ----- .../clienttestcases/TestCase3BasicPubSub.java | 237 ------ .../interop/coordinator/CoordinatingTestCase.java | 263 ++++++ .../qpid/interop/coordinator/Coordinator.java | 388 +++++++++ .../interop/coordinator/InvitingTestDecorator.java | 220 +++++ .../coordinator/ListeningCoordinatorTest.java | 28 + .../coordinator/ListeningTestDecorator.java | 200 +++++ .../qpid/interop/coordinator/OptOutTestCase.java | 65 ++ .../interop/coordinator/TestClientDetails.java | 87 ++ .../qpid/interop/coordinator/XMLTestListener.java | 402 +++++++++ .../testcases/CoordinatingTestCase1DummyRun.java | 85 ++ .../testcases/CoordinatingTestCase2BasicP2P.java | 90 ++ .../CoordinatingTestCase3BasicPubSub.java | 92 +++ .../java/org/apache/qpid/interop/old/Listener.java | 291 +++++++ .../org/apache/qpid/interop/old/Publisher.java | 244 ++++++ .../qpid/interop/testcases/CircuitTestCase.java | 101 --- .../testcases/InteropTestCase1DummyRun.java | 84 -- .../testcases/InteropTestCase2BasicP2P.java | 90 -- .../testcases/InteropTestCase3BasicPubSub.java | 88 -- .../interop/testclient/InteropClientTestCase.java | 104 +++ .../apache/qpid/interop/testclient/TestClient.java | 422 ++++++++++ .../testclient/testcases/TestCase1DummyRun.java | 96 +++ .../testclient/testcases/TestCase2BasicP2P.java | 214 +++++ .../testclient/testcases/TestCase3BasicPubSub.java | 249 ++++++ .../qpid/sustained/SustainedClientTestCase.java | 905 --------------------- .../apache/qpid/sustained/SustainedTestCase.java | 126 --- .../apache/qpid/sustained/SustainedTestClient.java | 8 +- .../qpid/sustained/SustainedTestCoordinator.java | 222 +++++ .../java/org/apache/qpid/sustained/TestClient.java | 157 ++++ .../org/apache/qpid/sustained/TestCoordinator.java | 117 +++ .../distributedtesting/InteropClientTestCase.java | 101 --- .../framework/distributedtesting/TestClient.java | 377 --------- .../org/apache/qpid/util/ClasspathScanner.java | 234 ++++++ .../org/apache/qpid/util/ConversationFactory.java | 479 +++++++++++ .../org/apache/qpid/interop/connection.properties | 20 + 38 files changed, 4780 insertions(+), 2466 deletions(-) delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningCoordinatorTest.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningTestDecorator.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/XMLTestListener.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase1DummyRun.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase3BasicPubSub.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/CircuitTestCase.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCoordinator.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestClient.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestCoordinator.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropClientTestCase.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationFactory.java create mode 100644 qpid/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/docs/RunningSustainedTests.txt b/qpid/java/integrationtests/docs/RunningSustainedTests.txt index db4405a32d..2b37f4c5a7 100644 --- a/qpid/java/integrationtests/docs/RunningSustainedTests.txt +++ b/qpid/java/integrationtests/docs/RunningSustainedTests.txt @@ -1,17 +1,15 @@ In addition to the integration tests the framework provided by this package also allows for sustained tests to be run. Currently avaible tests: -- org.apache.qpid.sustained.SustainedClientTestCase : Pub Sub test to determine steady state throughput. +- org.apache.qpid.sustained.SustainedTestClient : Pub Sub test to determine steady state throughput. Running Tests. Run the tests as per the integration tests. -- Start a broker -- Start at least one test client [java org.apache.qpid.interop.TestClient], ensuring unique naming. - -- Start the test coordinator with the 'fanout' engine, on the sustained test case [java org.apache.qpid.test.framework.distributedtesting.Coordinator] - -- Additional Test clients can be started and joined into the running test: [java org.apache.qpid.interop.TestClient -j] - + - Start a broker + - Start at least one Client [java org.apache.qpid.sustained.TestClient], ensuring unique naming + - Start Test Controller [java org.apache.qpid.sustained.TestCoordinator] + - Additional Test clients can be started: + [java org.apache.qpid.sustained.TestClient -j org.apache.qpid.sustained.SustainedTestClient] diff --git a/qpid/java/integrationtests/pom.xml b/qpid/java/integrationtests/pom.xml index 89fd5ede28..9ccd153f54 100644 --- a/qpid/java/integrationtests/pom.xml +++ b/qpid/java/integrationtests/pom.xml @@ -40,16 +40,12 @@ + org.apache.qpid qpid-client - - org.apache.qpid - qpid-systests - - org.slf4j slf4j-log4j12 diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java deleted file mode 100644 index b119d13a3d..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * - * 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.clienttestcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.distributedtesting.InteropClientTestCase; - -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.Session; - -/** - * 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. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Perform test case actions. - *
Generate test reports. - *
- */ -public class TestCase1DummyRun implements InteropClientTestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase1DummyRun.class); - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "TC1_DummyRun"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage): called"); - - // Test parameters don't matter, accept all invites. - 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 receivers. - * @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 - { - log.debug("public void assignRole(Roles role, Message assignRoleMessage): called"); - - // Do nothing, both roles are the same. - } - - /** - * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. - */ - public void start() - { - log.debug("public void start(): called"); - - // Do nothing. - } - - /** - * 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 - { - log.debug("public Message getReport(Session session): called"); - - // Generate a dummy report, the coordinator expects a report but doesn't care what it is. - return session.createTextMessage("Dummy Run, Ok."); - } - - /** - * Handles incoming test messages. Does nothing. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Ignore any messages. - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java deleted file mode 100644 index 080bd846ee..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * - * 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.clienttestcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.distributedtesting.InteropClientTestCase; -import org.apache.qpid.test.framework.distributedtesting.TestClient; -import org.apache.qpid.test.framework.TestUtils; - -import javax.jms.*; - -/** - * 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. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Send required number of test messages. - *
Generate test reports. - *
- */ -public class TestCase2BasicP2P implements InteropClientTestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase2BasicP2P.class); - - /** Holds the count of test messages received. */ - private int messageCount; - - /** The role to be played by the test. */ - private Roles role; - - /** The number of test messages to send. */ - private int numMessages; - - /** The connection to send the test messages on. */ - private Connection connection; - - /** The session to send the test messages on. */ - private Session session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "TC2_BasicP2P"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); - - // 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 receivers. - * - * @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 - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Reset the message count for a new test. - messageCount = 0; - - // Take note of the role to be played. - this.role = role; - - // Create a new connection to pass the test messages on. - connection = TestUtils.createConnection(TestClient.testContextProperties); - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Extract and retain the test parameters. - numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); - Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); - - log.debug("numMessages = " + numMessages); - log.debug("sendDestination = " + sendDestination); - log.debug("role = " + role); - - switch (role) - { - // Check if the sender role is being assigned, and set up a message producer if so. - case SENDER: - producer = session.createProducer(sendDestination); - break; - - // Otherwise the receivers role is being assigned, so set this up to listen for messages. - case RECEIVER: - MessageConsumer consumer = session.createConsumer(sendDestination); - consumer.setMessageListener(this); - break; - } - - connection.start(); - } - - /** - * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void start() throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - if (role.equals(Roles.SENDER)) - { - Message testMessage = session.createTextMessage("test"); - - for (int i = 0; i < numMessages; i++) - { - producer.send(testMessage); - - // Increment the message count. - messageCount++; - } - } - } - - /** - * 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 - { - log.debug("public Message getReport(Session session): called"); - - // Close the test connection. - connection.close(); - - // Generate a report message containing the count of the number of messages passed. - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - report.setIntProperty("MESSAGE_COUNT", messageCount); - - return report; - } - - /** - * Counts incoming test messages. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Increment the message count. - messageCount++; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java deleted file mode 100644 index a11d045e89..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * - * 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.clienttestcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.distributedtesting.InteropClientTestCase; -import org.apache.qpid.test.framework.distributedtesting.TestClient; -import org.apache.qpid.test.framework.TestUtils; - -import javax.jms.*; - -/** - * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the - * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of - * messages sent/received. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Send required number of test messages using pub/sub. - *
Generate test reports. - *
- */ -public class TestCase3BasicPubSub implements InteropClientTestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase3BasicPubSub.class); - - /** Holds the count of test messages received. */ - private int messageCount; - - /** The role to be played by the test. */ - private Roles role; - - /** The number of test messages to send. */ - private int numMessages; - - /** The connections to send/receive the test messages on. */ - private Connection[] connection; - - /** The sessions to send/receive the test messages on. */ - private Session[] session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "TC3_BasicPubSub"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); - - // 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 receivers. - * - * @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 - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Reset the message count for a new test. - messageCount = 0; - - // Take note of the role to be played. - this.role = role; - - // Extract and retain the test parameters. - numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); - int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); - String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); - - log.debug("numMessages = " + numMessages); - log.debug("numReceivers = " + numReceivers); - log.debug("sendKey = " + sendKey); - log.debug("role = " + role); - - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - // Create a new connection to pass the test messages on. - connection = new Connection[1]; - session = new Session[1]; - - connection[0] = TestUtils.createConnection(TestClient.testContextProperties); - session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Extract and retain the test parameters. - Destination sendDestination = session[0].createTopic(sendKey); - - producer = session[0].createProducer(sendDestination); - break; - - // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number - // of receivers connections. - case RECEIVER: - // Create the required number of receivers connections. - connection = new Connection[numReceivers]; - session = new Session[numReceivers]; - - for (int i = 0; i < numReceivers; i++) - { - connection[i] = TestUtils.createConnection(TestClient.testContextProperties); - session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); - - sendDestination = session[i].createTopic(sendKey); - - MessageConsumer consumer = session[i].createConsumer(sendDestination); - consumer.setMessageListener(this); - } - - break; - } - - // Start all the connection dispatcher threads running. - for (Connection conn : connection) - { - conn.start(); - } - } - - /** - * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void start() throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - if (role.equals(Roles.SENDER)) - { - Message testMessage = session[0].createTextMessage("test"); - - for (int i = 0; i < numMessages; i++) - { - producer.send(testMessage); - - // Increment the message count. - messageCount++; - } - } - } - - /** - * 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 - { - log.debug("public Message getReport(Session session): called"); - - // Close the test connections. - for (Connection conn : connection) - { - conn.close(); - } - - // Generate a report message containing the count of the number of messages passed. - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - report.setIntProperty("MESSAGE_COUNT", messageCount); - - return report; - } - - /** - * Counts incoming test messages. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Increment the message count. - messageCount++; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java new file mode 100644 index 0000000000..d2042be741 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java @@ -0,0 +1,263 @@ +/* + * + * 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 junit.framework.TestCase; + +import org.apache.log4j.Logger; + +import org.apache.qpid.util.ConversationFactory; + +import javax.jms.*; + +import java.util.Map; + +/** + * A 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). + * + *

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: + * + *

+ * 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.");
+ *   }
+ * }
+ *
+ * 
+ * + *

+ *
CRC Card
Responsibilities Collaborations + *
Accept notification of test case participants. {@link InvitingTestDecorator} + *
Accpet JMS Connection to carry out the coordination over. + *
Coordinate the test sequence amongst participants. {@link ConversationFactory} + *
Supply test properties + *
+ */ +public abstract class CoordinatingTestCase extends TestCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(CoordinatingTestCase.class); + + /** Holds the contact details for the sending test client. */ + protected TestClientDetails sender; + + /** Holds the contact details for the receving test client. */ + protected TestClientDetails receiver; + + /** Holds the conversation factory over which to coordinate the test. */ + protected ConversationFactory conversationFactory; + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public CoordinatingTestCase(String name) + { + super(name); + } + + /** + * Sets the sender test client to coordinate the test with. + * + * @param sender The contact details of the sending client in the test. + */ + public void setSender(TestClientDetails sender) + { + log.debug("public void setSender(TestClientDetails sender = " + sender + "): called"); + + this.sender = sender; + } + + /** + * Sets the receiving test client to coordinate the test with. + * + * @param receiver The contact details of the sending client in the test. + */ + public void setReceiver(TestClientDetails receiver) + { + log.debug("public void setReceiver(TestClientDetails receiver = " + receiver + "): called"); + + this.receiver = receiver; + } + + /** + * Supplies the sending test client. + * + * @return The sending test client. + */ + public TestClientDetails getSender() + { + return sender; + } + + /** + * Supplies the receiving test client. + * + * @return The receiving test client. + */ + public TestClientDetails getReceiver() + { + return receiver; + } + + /** + * Returns the name of the current test method of this test class, with the sending and receiving client names + * appended on to it, so that the resulting name unqiuely identifies the test and the clients that participated + * in it. + * + * @return The unique test and client name. + */ + public String getName() + { + if ((sender == null) || (receiver == null)) + { + return super.getName(); + } + else + { + return super.getName() + "_sender_" + sender.clientName + "_receiver_" + receiver.clientName; + } + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public abstract String getTestCaseNameForTestMethod(String methodName); + + /** + * Accepts the conversation factory over which to hold the test coordinating conversation. + * + * @param conversationFactory The conversation factory to coordinate the test over. + */ + public void setConversationFactory(ConversationFactory conversationFactory) + { + this.conversationFactory = conversationFactory; + } + + /** + * 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 assigning the test roles, begining the test and gathering the test reports + * from the participants. + * + * @param testProperties The test case definition. + * + * @return The test results from the senders and receivers. + * + * @throws JMSException All underlying JMSExceptions are allowed to fall through. + */ + protected Message[] sequenceTest(Map testProperties) throws JMSException + { + log.debug("protected Message[] sequenceTest(Object... testProperties = " + testProperties + "): called"); + + Session session = conversationFactory.getSession(); + Destination senderControlTopic = session.createTopic(sender.privateControlKey); + Destination receiverControlTopic = session.createTopic(receiver.privateControlKey); + + ConversationFactory.Conversation senderConversation = conversationFactory.startConversation(); + ConversationFactory.Conversation receiverConversation = conversationFactory.startConversation(); + + // Assign the sender role to the sending test client. + Message assignSender = conversationFactory.getSession().createMessage(); + setPropertiesOnMessage(assignSender, testProperties); + assignSender.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); + assignSender.setStringProperty("ROLE", "SENDER"); + + senderConversation.send(senderControlTopic, assignSender); + + // Assign the receiver role the receiving client. + Message assignReceiver = session.createMessage(); + setPropertiesOnMessage(assignReceiver, testProperties); + assignReceiver.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); + assignReceiver.setStringProperty("ROLE", "RECEIVER"); + + receiverConversation.send(receiverControlTopic, assignReceiver); + + // Wait for the senders and receivers to confirm their roles. + senderConversation.receive(); + receiverConversation.receive(); + + // Start the test. + Message start = session.createMessage(); + start.setStringProperty("CONTROL_TYPE", "START"); + + senderConversation.send(senderControlTopic, start); + + // Wait for the test sender to return its report. + Message senderReport = senderConversation.receive(); + + try + { + Thread.sleep(500); + } + catch (InterruptedException e) + { } + + // Ask the receiver for its report. + Message statusRequest = session.createMessage(); + statusRequest.setStringProperty("CONTROL_TYPE", "STATUS_REQUEST"); + + receiverConversation.send(receiverControlTopic, statusRequest); + + // Wait for the receiver to send its report. + Message receiverReport = receiverConversation.receive(); + + return new Message[] { senderReport, receiverReport }; + } + + /** + * Sets properties of different types on a JMS Message. + * + * @param message The message to set properties on. + * @param properties The property name/value pairs to set. + * + * @throws JMSException All underlying JMSExceptions are allowed to fall through. + */ + public void setPropertiesOnMessage(Message message, Map properties) throws JMSException + { + for (Map.Entry entry : properties.entrySet()) + { + String name = entry.getKey(); + Object value = entry.getValue(); + + message.setObjectProperty(name, value); + } + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java new file mode 100644 index 0000000000..6eec20769a --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java @@ -0,0 +1,388 @@ +/* + * + * 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.io.*; +import java.util.*; +import java.util.concurrent.LinkedBlockingQueue; +import javax.jms.*; +import junit.framework.Test; +import junit.framework.TestResult; +import junit.framework.TestSuite; +import org.apache.log4j.Logger; +import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase1DummyRun; +import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase2BasicP2P; +import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase3BasicPubSub; +import org.apache.qpid.interop.testclient.TestClient; +import org.apache.qpid.util.CommandLineParser; +import org.apache.qpid.util.ConversationFactory; +import org.apache.qpid.util.PrettyPrintingUtils; +import uk.co.thebadgerset.junit.extensions.TKTestResult; +import uk.co.thebadgerset.junit.extensions.TKTestRunner; +import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; +import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; + +/** + *

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. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Find out what test clients are available. {@link ConversationFactory} + *
Decorate available tests to run all available clients. {@link InvitingTestDecorator} + *
Attach XML test result logger. + *
Terminate the interop testing framework. + *
+ */ +public class Coordinator extends TKTestRunner +{ + private static final Logger log = Logger.getLogger(Coordinator.class); + + public static final String DEFAULT_CONNECTION_PROPS_RESOURCE = "org/apache/qpid/interop/connection.properties"; + + /** Holds the URL of the broker to coordinate the tests on. */ + protected String brokerUrl; + + /** Holds the virtual host to coordinate the tests on. If null, then the default virtual host is used. */ + protected String virtualHost; + + /** Holds the list of all clients that enlisted, when the compulsory invite was issued. */ + protected Set enlistedClients = new HashSet(); + + /** Holds the conversation helper for the control conversation. */ + protected ConversationFactory conversationFactory; + + /** Holds the connection that the coordinating messages are sent over. */ + protected Connection connection; + + /** + * Holds the name of the class of the test currently being run. Ideally passed into the {@link #createTestResult} + * method, but as the signature is already fixed for this, the current value gets pushed here as a member variable. + */ + protected String currentTestClassName; + + /** Holds the path of the directory to output test results too, if one is defined. */ + protected static String _reportDir; + + /** + * 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 null. + */ + public Coordinator(String brokerUrl, String virtualHost) + { + log.debug("Coordinator(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost + "): called"); + + // 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: + * + *

+ *
-b The broker URL. Mandatory. + *
-h The virtual host. Optional. + *
name=value Trailing argument define name/value pairs. Added to system properties. Optional. + *
+ * + * @param args The command line arguments. + */ + public static void main(String[] args) + { + try + { + // Use the command line parser to evaluate the command line with standard handling behaviour (print errors + // and usage then exit if there are errors). + Properties options = + CommandLineParser.processCommandLine(args, + new CommandLineParser( + new String[][] + { + {"b", "The broker URL.", "broker", "false"}, + {"h", "The virtual host to use.", "virtual host", "false"}, + {"o", "The name of the directory to output test timings to.", "dir", "false"} + })); + + // Extract the command line options. + String brokerUrl = options.getProperty("b"); + String virtualHost = options.getProperty("h"); + _reportDir = options.getProperty("o"); + _reportDir = (_reportDir == null) ? "." : _reportDir; + + // Scan for available test cases using a classpath scanner. + Collection> testCaseClasses = + new ArrayList>(); + // ClasspathScanner.getMatches(CoordinatingTestCase.class, "^Test.*", true); + // Hard code the test classes till the classpath scanner is fixed. + Collections.addAll(testCaseClasses, + CoordinatingTestCase1DummyRun.class, + CoordinatingTestCase2BasicP2P.class, + CoordinatingTestCase3BasicPubSub.class); + + // Check that some test classes were actually found. + if (testCaseClasses.isEmpty()) + { + throw new RuntimeException( + "No test classes implementing CoordinatingTestCase were found on the class path."); + } + + int i = 0; + String[] testClassNames = new String[testCaseClasses.size()]; + + for (Class testClass : testCaseClasses) + { + testClassNames[i++] = testClass.getName(); + } + + // Create a coordinator and begin its test procedure. + Coordinator coordinator = new Coordinator(brokerUrl, virtualHost); + + boolean failure = false; + + TestResult testResult = coordinator.start(testClassNames); + + if (failure) + { + System.exit(FAILURE_EXIT); + } + else + { + System.exit(SUCCESS_EXIT); + } + } + catch (Exception e) + { + System.err.println(e.getMessage()); + log.error("Top level handler caught execption.", e); + System.exit(EXCEPTION_EXIT); + } + } + + /** + * Starts all of the test classes to be run by this coordinator running. + * + * @param testClassNames An array of all the coordinating test case implementations. + * + * @return A JUnit TestResult to run the tests with. + * + * @throws Exception Any underlying exceptions are allowed to fall through, and fail the test process. + */ + public TestResult start(String[] testClassNames) throws Exception + { + log.debug("public TestResult start(String[] testClassNames = " + PrettyPrintingUtils.printArray(testClassNames) + + ": called"); + + // Connect to the broker. + connection = TestClient.createConnection(DEFAULT_CONNECTION_PROPS_RESOURCE, brokerUrl, virtualHost); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + Destination controlTopic = session.createTopic("iop.control"); + Destination responseQueue = session.createQueue("coordinator"); + + conversationFactory = new ConversationFactory(connection, responseQueue, LinkedBlockingQueue.class); + ConversationFactory.Conversation conversation = conversationFactory.startConversation(); + + connection.start(); + + // Broadcast the compulsory invitation to find out what clients are available to test. + Message invite = session.createMessage(); + invite.setStringProperty("CONTROL_TYPE", "INVITE"); + invite.setJMSReplyTo(responseQueue); + + conversation.send(controlTopic, invite); + + // Wait for a short time, to give test clients an opportunity to reply to the invitation. + Collection enlists = conversation.receiveAll(0, 3000); + + enlistedClients = extractEnlists(enlists); + + // Run the test in the suite using JUnit. + TestResult result = null; + + for (String testClassName : testClassNames) + { + // Record the current test class, so that the test results can be output to a file incorporating this name. + this.currentTestClassName = testClassName; + + result = super.start(new String[]{testClassName}); + } + + // At this point in time, all tests have completed. Broadcast the shutdown message. + Message terminate = session.createMessage(); + terminate.setStringProperty("CONTROL_TYPE", "TERMINATE"); + + conversation.send(controlTopic, terminate); + + return result; + } + + /** + * For a collection of enlist messages, this method pulls out of the client details for the enlisting clients. + * + * @param enlists The enlist messages. + * + * @return A set of enlisting clients, extracted from the enlist messages. + * + * @throws JMSException Any underlying JMSException is allowed to fall through. + */ + public static Set extractEnlists(Collection enlists) throws JMSException + { + log.debug("public static Set extractEnlists(Collection enlists = " + enlists + + "): called"); + + Set enlistedClients = new HashSet(); + + // Retain the list of all available clients. + for (Message enlist : enlists) + { + TestClientDetails clientDetails = new TestClientDetails(); + clientDetails.clientName = enlist.getStringProperty("CLIENT_NAME"); + clientDetails.privateControlKey = enlist.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); + + enlistedClients.add(clientDetails); + } + + return enlistedClients; + } + + /** + * 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 coordinators ability to invite test clients to participate in + * tests. + * + * @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) + { + log.debug("public TestResult doRun(Test \"" + test + "\", boolean " + wait + "): called"); + + // Wrap all tests in the test suite with WrappedSuiteTestDecorators. This is quite ugly and a bit baffling, + // but the reason it is done is because the JUnit implementation of TestDecorator has some bugs in it. + WrappedSuiteTestDecorator targetTest = null; + + if (test instanceof TestSuite) + { + log.debug("targetTest is a TestSuite"); + + TestSuite suite = (TestSuite) test; + + int numTests = suite.countTestCases(); + log.debug("There are " + numTests + " in the suite."); + + for (int i = 0; i < numTests; i++) + { + Test nextTest = suite.testAt(i); + log.debug("suite.testAt(" + i + ") = " + nextTest); + + if (nextTest instanceof CoordinatingTestCase) + { + log.debug("nextTest is a CoordinatingTestCase"); + } + } + + targetTest = new WrappedSuiteTestDecorator(suite); + log.debug("Wrapped with a WrappedSuiteTestDecorator."); + } + // Wrap the tests in an inviting test decorator, to perform the invite/test cycle. + + targetTest = newTestDecorator(targetTest, enlistedClients, conversationFactory, connection); + + TestSuite suite = new TestSuite(); + suite.addTest(targetTest); + + // Wrap the tests in a scaled test decorator to them them as a 'batch' in one thread. + // targetTest = new ScaledTestDecorator(targetTest, new int[] { 1 }); + + return super.doRun(suite, wait); + } + + protected WrappedSuiteTestDecorator newTestDecorator(WrappedSuiteTestDecorator targetTest, Set enlistedClients, ConversationFactory conversationFactory, Connection connection) + { + return new InvitingTestDecorator(targetTest, enlistedClients, conversationFactory, connection); + } + + /** + * Creates the TestResult object to be used for test runs. + * + * @return An instance of the test result object. + */ + protected TestResult createTestResult() + { + log.debug("protected TestResult createTestResult(): called"); + + TKTestResult result = new TKTestResult(fPrinter.getWriter(), delay, verbose, testCaseName); + + // Check if a directory to output reports to has been specified and attach test listeners if so. + if (_reportDir != null) + { + // Create the report directory if it does not already exist. + File reportDirFile = new File(_reportDir); + + if (!reportDirFile.exists()) + { + reportDirFile.mkdir(); + } + + // Create the timings file (make the name of this configurable as a command line parameter). + Writer timingsWriter = null; + + try + { + File timingsFile = new File(reportDirFile, "TEST." + currentTestClassName + ".xml"); + timingsWriter = new BufferedWriter(new FileWriter(timingsFile), 20000); + } + catch (IOException e) + { + throw new RuntimeException("Unable to create the log file to write test results to: " + e, e); + } + + // Set up a CSV results listener to output the timings to the results file. + XMLTestListener listener = new XMLTestListener(timingsWriter, currentTestClassName); + result.addListener(listener); + result.addTKTestListener(listener); + + // Register the results listeners shutdown hook to flush its data if the test framework is shutdown + // prematurely. + // registerShutdownHook(listener); + + // Record the start time of the batch. + // result.notifyStartBatch(); + + // At this point in time the test class has been instantiated, giving it an opportunity to read its parameters. + // Inform any test listers of the test properties. + result.notifyTestProperties(TestContextProperties.getAccessedProps()); + } + + return result; + } + + public void setReportDir(String reportDir) + { + _reportDir = reportDir; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java new file mode 100644 index 0000000000..8695f7f66f --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java @@ -0,0 +1,220 @@ +/* + * + * 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.*; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; + +import junit.framework.Test; +import junit.framework.TestResult; + +import org.apache.log4j.Logger; + +import org.apache.qpid.util.ConversationFactory; + +import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; + +/** + *

+ *
CRC Card
Responsibilities Collaborations + *
Broadcast test invitations and collect enlists. {@link ConversationFactory}. + *
Output test failures for clients unwilling to run the test case. {@link Coordinator} + *
Execute coordinated test cases. {@link CoordinatingTestCase} + *
+ */ +public class InvitingTestDecorator extends WrappedSuiteTestDecorator +{ + private static final Logger log = Logger.getLogger(InvitingTestDecorator.class); + + /** Holds the contact information for all test clients that are available and that may take part in the test. */ + Set allClients; + + /** Holds the conversation helper for the control level conversation for coordinating the test through. */ + ConversationFactory conversationFactory; + + /** Holds the connection that the control conversation is held over. */ + Connection connection; + + /** Holds the underlying {@link CoordinatingTestCase}s that this decorator wraps. */ + WrappedSuiteTestDecorator testSuite; + + /** + * Creates a wrapped suite test decorator from another one. + * + * @param suite The test suite. + * @param availableClients The list of all clients that responded to the compulsory invite. + * @param controlConversation The conversation helper for the control level, test coordination conversation. + * @param controlConnection The connection that the coordination messages are sent over. + */ + public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set availableClients, + ConversationFactory controlConversation, Connection controlConnection) + { + super(suite); + + log.debug("public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set allClients = " + + availableClients + ", ConversationHelper controlConversation = " + controlConversation + "): called"); + + testSuite = suite; + allClients = availableClients; + conversationFactory = controlConversation; + connection = controlConnection; + } + + /** + * Broadcasts a test invitation and accetps enlisting from participating clients. The wrapped test case is + * then repeated for every combination of test clients (provided the wrapped test case extends + * {@link CoordinatingTestCase}. + * + *

Any JMSExceptions during the invite/enlist conversation will be allowed to fall through as runtime exceptions, + * resulting in the non-completion of the test run. + * + * @todo Better error recovery for failure of the invite/enlist conversation could be added. + * + * @param testResult The the results object to monitor the test results with. + */ + public void run(TestResult testResult) + { + log.debug("public void run(TestResult testResult): called"); + + Collection tests = testSuite.getAllUnderlyingTests(); + + for (Test test : tests) + { + CoordinatingTestCase coordTest = (CoordinatingTestCase) test; + + // Broadcast the invitation to find out what clients are available to test. + Set enlists; + try + { + Message invite = conversationFactory.getSession().createMessage(); + Destination controlTopic = conversationFactory.getSession().createTopic("iop.control"); + ConversationFactory.Conversation conversation = conversationFactory.startConversation(); + + invite.setStringProperty("CONTROL_TYPE", "INVITE"); + invite.setStringProperty("TEST_NAME", coordTest.getTestCaseNameForTestMethod(coordTest.getName())); + + conversation.send(controlTopic, invite); + + // Wait for a short time, to give test clients an opportunity to reply to the invitation. + Collection replies = conversation.receiveAll(allClients.size(), 3000); + enlists = Coordinator.extractEnlists(replies); + } + catch (JMSException e) + { + throw new RuntimeException("There was a JMSException during the invite/enlist conversation.", e); + } + + // Compare the list of willing clients to the list of all available. + Set optOuts = new HashSet(allClients); + optOuts.removeAll(enlists); + + // Output test failures for clients that will not particpate in the test. + Set> failPairs = allPairs(optOuts, allClients); + + for (List failPair : failPairs) + { + CoordinatingTestCase failTest = new OptOutTestCase("testOptOut"); + failTest.setSender(failPair.get(0)); + failTest.setReceiver(failPair.get(1)); + + failTest.run(testResult); + } + + // Loop over all combinations of clients, willing to run the test. + Set> enlistedPairs = allPairs(enlists, enlists); + + for (List enlistedPair : enlistedPairs) + { + // Set the sending and receiving client details on the test case. + coordTest.setSender(enlistedPair.get(0)); + coordTest.setReceiver(enlistedPair.get(1)); + + // Pass down the connection to hold the coordination conversation over. + coordTest.setConversationFactory(conversationFactory); + + // Execute the test case. + coordTest.run(testResult); + } + } + } + + /** + * Prints a string summarizing this test decorator, mainly for debugging purposes. + * + * @return String representation for debugging purposes. + */ + public String toString() + { + return "InvitingTestDecorator: [ testSuite = " + testSuite + " ]"; + } + + /** + * Produces all pairs of combinations of elements from two sets. The ordering of the elements in the pair is + * important, that is the pair is distinct from ; both pairs are generated. For any element, i, in + * both the left and right sets, the reflexive pair is not generated. + * + * @param left The left set. + * @param right The right set. + * + * @return All pairs formed from the permutations of all elements of the left and right sets. + */ + private Set> allPairs(Set left, Set right) + { + log.debug("private Set> allPairs(Set left = " + left + ", Set right = " + right + "): called"); + + Set> results = new HashSet>(); + + // Form all pairs from left to right. + // Form all pairs from right to left. + for (E le : left) + { + for (E re : right) + { + if (!le.equals(re)) + { + results.add(new Pair(le, re)); + results.add(new Pair(re, le)); + } + } + } + + log.debug("results = " + results); + + return results; + } + + /** + * A simple implementation of a pair, using a list. + */ + private class Pair extends ArrayList + { + public Pair(T first, T second) + { + super(); + super.add(first); + super.add(second); + } + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningCoordinatorTest.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningCoordinatorTest.java new file mode 100644 index 0000000000..1b4461f8c2 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningCoordinatorTest.java @@ -0,0 +1,28 @@ +/* + * 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 javax.jms.Message; + +public interface ListeningCoordinatorTest +{ + public void latejoin(Message message); +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningTestDecorator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningTestDecorator.java new file mode 100644 index 0000000000..4312dfbcc6 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningTestDecorator.java @@ -0,0 +1,200 @@ +/* + * + * 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 junit.framework.Test; +import junit.framework.TestResult; +import org.apache.log4j.Logger; +import org.apache.qpid.util.ConversationFactory; +import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +/** + *

CRC Card
Responsibilities Collaborations
Broadcast test + * invitations and collect enlists. {@link ConversationFactory}.
Output test failures for clients + * unwilling to run the test case. {@link Coordinator}
Execute coordinated test cases. {@link + * CoordinatingTestCase}
+ */ +public class ListeningTestDecorator extends WrappedSuiteTestDecorator implements MessageListener +{ + private static final Logger log = Logger.getLogger(ListeningTestDecorator.class); + + /** Holds the contact information for all test clients that are available and that may take part in the test. */ + Set allClients; + + /** Holds the conversation helper for the control level conversation for coordinating the test through. */ + ConversationFactory conversationFactory; + + /** Holds the connection that the control conversation is held over. */ + Connection connection; + + /** Holds the underlying {@link CoordinatingTestCase}s that this decorator wraps. */ + WrappedSuiteTestDecorator testSuite; + + /** Hold the current running test case. */ + CoordinatingTestCase _currentTest = null; + + /** + * Creates a wrapped suite test decorator from another one. + * + * @param suite The test suite. + * @param availableClients The list of all clients that responded to the compulsory invite. + * @param controlConversation The conversation helper for the control level, test coordination conversation. + * @param controlConnection The connection that the coordination messages are sent over. + */ + public ListeningTestDecorator(WrappedSuiteTestDecorator suite, Set availableClients, + ConversationFactory controlConversation, Connection controlConnection) + { + super(suite); + + log.debug("public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set allClients = " + + availableClients + ", ConversationHelper controlConversation = " + controlConversation + "): called"); + + testSuite = suite; + allClients = availableClients; + conversationFactory = controlConversation; + connection = controlConnection; + } + + /** + * Broadcasts a test invitation and accetps enlisting from participating clients. The wrapped test case is then + * repeated for every combination of test clients (provided the wrapped test case extends {@link + * CoordinatingTestCase}. + * + *

Any JMSExceptions during the invite/enlist conversation will be allowed to fall through as runtime + * exceptions, resulting in the non-completion of the test run. + * + * @param testResult The the results object to monitor the test results with. + * + * @todo Better error recovery for failure of the invite/enlist conversation could be added. + */ + public void run(TestResult testResult) + { + log.debug("public void run(TestResult testResult): called"); + + Collection tests = testSuite.getAllUnderlyingTests(); + + for (Test test : tests) + { + CoordinatingTestCase coordTest = (CoordinatingTestCase) test; + + Set enlists = signupClients(coordTest); + + if (enlists.size() == 0) + { + throw new RuntimeException("No clients to test with"); + } + + Iterator clients = enlists.iterator(); + coordTest.setSender(clients.next()); + + while (clients.hasNext()) + { + // Set the sending and receiving client details on the test case. + coordTest.setReceiver(clients.next()); + } + + // Pass down the connection to hold the coordination conversation over. + coordTest.setConversationFactory(conversationFactory); + + + if (coordTest instanceof ListeningCoordinatorTest) + { + _currentTest = coordTest; + } + // Execute the test case. + coordTest.run(testResult); + + _currentTest = null; + } + } + + private Set signupClients(CoordinatingTestCase coordTest) + { + // Broadcast the invitation to find out what clients are available to test. + Set enlists; + try + { + Message invite = conversationFactory.getSession().createMessage(); + Destination controlTopic = conversationFactory.getSession().createTopic("iop.control"); + ConversationFactory.Conversation conversation = conversationFactory.startConversation(); + + invite.setStringProperty("CONTROL_TYPE", "INVITE"); + invite.setStringProperty("TEST_NAME", coordTest.getTestCaseNameForTestMethod(coordTest.getName())); + + conversation.send(controlTopic, invite); + + // Wait for a short time, to give test clients an opportunity to reply to the invitation. + Collection replies = conversation.receiveAll(allClients.size(), 5000); + + log.debug("Received " + replies.size() + " enlist replies"); + + enlists = Coordinator.extractEnlists(replies); + + //Create topic to listen on for latejoiners + Destination listenTopic = conversationFactory.getSession().createTopic("iop.control.test." + coordTest.getTestCaseNameForTestMethod(coordTest.getName())); + + //Listen for joiners + conversationFactory.getSession().createConsumer(listenTopic).setMessageListener(this); + log.debug("Created consumer on :" + listenTopic); + } + catch (JMSException e) + { + throw new RuntimeException("There was a JMSException during the invite/enlist conversation.", e); + } + + return enlists; + } + + /** + * Prints a string summarizing this test decorator, mainly for debugging purposes. + * + * @return String representation for debugging purposes. + */ + public String toString() + { + return "ListeningTestDecorator: [ testSuite = " + testSuite + " ]"; + } + + + public void onMessage(Message message) + { + try + { + if (message.getStringProperty("CONTROL_TYPE").equals("LATEJOIN")) + { + ((ListeningCoordinatorTest) _currentTest).latejoin(message); + } + } + catch (JMSException e) + { + log.debug("Unable to process message:" + message); + } + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java new file mode 100644 index 0000000000..42a382a898 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java @@ -0,0 +1,65 @@ +/* + * + * 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 junit.framework.Assert; + +/** + * An OptOutTestCase is a test case that automatically fails. It is used when a list of test clients has been generated + * from a compulsory invite, but only some of those clients have responded to a specific test case invite. The clients + * that did not respond, are automatically given a fail for the test. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Fail the test with a suitable reason. + *
+ */ +public class OptOutTestCase extends CoordinatingTestCase +{ + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public OptOutTestCase(String name) + { + super(name); + } + + /** Generates an appropriate test failure assertion. */ + public void testOptOut() + { + Assert.fail("One of " + getSender() + " and " + getReceiver() + " opted out of the test."); + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "OptOutTest"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java new file mode 100644 index 0000000000..c4a9d39cd8 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java @@ -0,0 +1,87 @@ +/* + * + * 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; + +/** + *

+ *
CRC Card
Responsibilities Collaborations + *
+ */ +public class TestClientDetails +{ + /** The test clients name. */ + public String clientName; + + /* The test clients unique sequence number. Not currently used. */ + + /** The routing key of the test clients control topic. */ + public String privateControlKey; + + /** + * Two TestClientDetails are considered to be equal, iff they have the same client name. + * + * @param o The object to compare to. + * + * @return If the object to compare to is a TestClientDetails equal to this one, false otherwise. + */ + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + + if (!(o instanceof TestClientDetails)) + { + return false; + } + + final TestClientDetails testClientDetails = (TestClientDetails) o; + + if ((clientName != null) ? (!clientName.equals(testClientDetails.clientName)) + : (testClientDetails.clientName != null)) + { + return false; + } + + return true; + } + + /** + * Computes a hash code compatible with the equals method; based on the client name alone. + * + * @return A hash code for this. + */ + public int hashCode() + { + return ((clientName != null) ? clientName.hashCode() : 0); + } + + /** + * Outputs the client name and address details. Mostly used for debugging purposes. + * + * @return The client name and address. + */ + public String toString() + { + return "TestClientDetails: [ clientName = " + clientName + ", privateControlKey = " + privateControlKey + " ]"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/XMLTestListener.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/XMLTestListener.java new file mode 100644 index 0000000000..747ba0dd0b --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/XMLTestListener.java @@ -0,0 +1,402 @@ +/* + * + * 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.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; +import java.util.*; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestCase; + +import org.apache.log4j.Logger; + +import uk.co.thebadgerset.junit.extensions.listeners.TKTestListener; + +/** + * Listens for test results for a named test and outputs these in the standard JUnit XML format to the specified + * writer. + * + *

The API for this listener accepts notifications about different aspects of a tests results through different + * methods, so some assumption needs to be made as to which test result a notification refers to. For example + * {@link #startTest} will be called, then possibly {@link #timing} will be called, even though the test instance is + * passed in both cases, it is not enough to distinguish a particular run of the test, as the test case instance may + * be being shared between multiple threads, or being run a repeated number of times, and can therfore be re-used + * between calls. The listeners make the assumption that, for every test, a unique thread will call {@link #startTest} + * and {@link #endTest} to delimit each test. All calls to set test parameters, timings, state and so on, will occur + * between the start and end and will be given with the same thread id as the start and end, so the thread id provides + * a unqiue value to identify a particular test run against. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
+ * + * @todo Merge this class with CSV test listener, making the collection of results common to both, and only factoring + * out the results printing code into sub-classes. Provide a simple XML results formatter with the same format as + * the ant XML formatter, and a more structured one for outputing results with timings and summaries from + * performance tests. + */ +public class XMLTestListener implements TKTestListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(XMLTestListener.class); + + /** The results file writer. */ + protected Writer writer; + + /** Holds the results for individual tests. */ + // protected Map results = new LinkedHashMap(); + // protected List results = new ArrayList(); + + /** + * Map for holding results on a per thread basis as they come in. A ThreadLocal is not used as sometimes an + * explicit thread id must be used, where notifications come from different threads than the ones that called + * the test method. + */ + Map threadLocalResults = Collections.synchronizedMap(new LinkedHashMap()); + + /** + * Holds results for tests that have ended. Transferring these results here from the per-thread results map, means + * that the thread id is freed for the thread to generate more results. + */ + List results = new ArrayList(); + + /** Holds the overall error count. */ + protected int errors = 0; + + /** Holds the overall failure count. */ + protected int failures = 0; + + /** Holds the overall tests run count. */ + protected int runs = 0; + + /** Holds the name of the class that tests are being run for. */ + String testClassName; + + /** + * Creates a new XML results output listener that writes to the specified location. + * + * @param writer The location to write results to. + */ + public XMLTestListener(Writer writer, String testClassName) + { + log.debug("public XMLTestListener(Writer writer, String testClassName = " + testClassName + "): called"); + + this.writer = writer; + this.testClassName = testClassName; + } + + /** + * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed. + * + * @param test The test to resest any results for. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void reset(Test test, Long threadId) + { + log.debug("public void reset(Test test = " + test + ", Long threadId = " + threadId + "): called"); + + XMLTestListener.Result r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + r.error = null; + r.failure = null; + + } + + /** + * A test started. + */ + public void startTest(Test test) + { + log.debug("public void startTest(Test test = " + test + "): called"); + + Result newResult = new Result(test.getClass().getName(), ((TestCase) test).getName()); + + // Initialize the thread local test results. + threadLocalResults.put(Thread.currentThread().getId(), newResult); + runs++; + } + + /** + * Should be called every time a test completes with the run time of that test. + * + * @param test The name of the test. + * @param nanos The run time of the test in nanoseconds. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void timing(Test test, long nanos, Long threadId) + { } + + /** + * Should be called every time a test completed with the amount of memory used before and after the test was run. + * + * @param test The test which memory was measured for. + * @param memStart The total JVM memory used before the test was run. + * @param memEnd The total JVM memory used after the test was run. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void memoryUsed(Test test, long memStart, long memEnd, Long threadId) + { } + + /** + * Should be called every time a parameterized test completed with the int value of its test parameter. + * + * @param test The test which memory was measured for. + * @param parameter The int parameter value. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void parameterValue(Test test, int parameter, Long threadId) + { } + + /** + * Should be called every time a test completes with the current number of test threads running. + * + * @param test The test for which the measurement is being generated. + * @param threads The number of tests being run concurrently. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void concurrencyLevel(Test test, int threads, Long threadId) + { } + + /** + * Notifies listeners of the tests read/set properties. + * + * @param properties The tests read/set properties. + */ + public void properties(Properties properties) + { } + + /** + * A test ended. + */ + public void endTest(Test test) + { + log.debug("public void endTest(Test test = " + test + "): called"); + + // Move complete test results into the completed tests list. + Result r = threadLocalResults.get(Thread.currentThread().getId()); + results.add(r); + + // Clear all the test results for the thread. + threadLocalResults.remove(Thread.currentThread().getId()); + } + + /** + * Called when a test completes. Success, failure and errors. This method should be used when registering an + * end test from a different thread than the one that started the test. + * + * @param test The test which completed. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void endTest(Test test, Long threadId) + { + log.debug("public void endTest(Test test = " + test + ", Long threadId = " + threadId + "): called"); + + // Move complete test results into the completed tests list. + Result r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + results.add(r); + + // Clear all the test results for the thread. + threadLocalResults.remove(Thread.currentThread().getId()); + } + + /** + * An error occurred. + */ + public void addError(Test test, Throwable t) + { + log.debug("public void addError(Test test = " + test + ", Throwable t = " + t + "): called"); + + Result r = threadLocalResults.get(Thread.currentThread().getId()); + r.error = t; + errors++; + } + + /** + * A failure occurred. + */ + public void addFailure(Test test, AssertionFailedError t) + { + log.debug("public void addFailure(Test test = " + test + ", AssertionFailedError t = " + t + "): called"); + + Result r = threadLocalResults.get(Thread.currentThread().getId()); + r.failure = t; + failures++; + } + + /** + * Called when a test completes to mark it as a test fail. This method should be used when registering a + * failure from a different thread than the one that started the test. + * + * @param test The test which failed. + * @param e The assertion that failed the test. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void addFailure(Test test, AssertionFailedError e, Long threadId) + { + log.debug("public void addFailure(Test test, AssertionFailedError e, Long threadId): called"); + + Result r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + r.failure = e; + failures++; + } + + /** + * Notifies listeners of the start of a complete run of tests. + */ + public void startBatch() + { + log.debug("public void startBatch(): called"); + + // Reset all results counts. + threadLocalResults = Collections.synchronizedMap(new HashMap()); + errors = 0; + failures = 0; + runs = 0; + + // Write out the file header. + try + { + writer.write("\n"); + } + catch (IOException e) + { + throw new RuntimeException("Unable to write the test results.", e); + } + } + + /** + * Notifies listeners of the end of a complete run of tests. + * + * @param parameters The optional test parameters to log out with the batch results. + */ + public void endBatch(Properties parameters) + { + log.debug("public void endBatch(Properties parameters = " + parameters + "): called"); + + // Write out the results. + try + { + // writer.write("\n"); + writer.write("\n"); + + for (Result result : results) + { + writer.write(" \n"); + + if (result.error != null) + { + writer.write(" "); + result.error.printStackTrace(new PrintWriter(writer)); + writer.write(" "); + } + else if (result.failure != null) + { + writer.write(" "); + result.failure.printStackTrace(new PrintWriter(writer)); + writer.write(" "); + } + + writer.write(" \n"); + } + + writer.write("\n"); + writer.flush(); + } + catch (IOException e) + { + throw new RuntimeException("Unable to write the test results.", e); + } + } + + /** + * Used to capture the results of a particular test run. + */ + protected static class Result + { + public Result(String testClass, String testName) + { + this.testClass = testClass; + this.testName = testName; + } + + public String testClass; + public String testName; + + /** Holds the exception that caused error in this test. */ + public Throwable error; + + /** Holds the assertion exception that caused failure in this test. */ + public AssertionFailedError failure; + + /** Holds the error count for this test. */ + // public int errors = 0; + + /** Holds the failure count for this tests. */ + // public int failures = 0; + + /** Holds the overall tests run count for this test. */ + // public int runs = 0; + + /*public boolean equals(Object o) + { + if (this == o) + { + return true; + } + + if (!(o instanceof Result)) + { + return false; + } + + final Result result = (Result) o; + + if ((testClass != null) ? (!testClass.equals(result.testClass)) : (result.testClass != null)) + { + return false; + } + + if ((testName != null) ? (!testName.equals(result.testName)) : (result.testName != null)) + { + return false; + } + + return true; + } + + public int hashCode() + { + int result; + result = ((testClass != null) ? testClass.hashCode() : 0); + result = (29 * result) + ((testName != null) ? testName.hashCode() : 0); + + return result; + }*/ + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase1DummyRun.java new file mode 100644 index 0000000000..e642ef792b --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase1DummyRun.java @@ -0,0 +1,85 @@ +/* + * + * 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.testcases; + +import java.util.HashMap; +import java.util.Map; + +import javax.jms.Message; + +import junit.framework.Assert; + +import org.apache.log4j.Logger; + +import org.apache.qpid.interop.coordinator.CoordinatingTestCase; + +/** + *

+ *
CRC Card
Responsibilities Collaborations + *
Exercises the interop testing framework without actually sending any test messages. + * {@link org.apache.qpid.interop.coordinator.CoordinatingTestCase} + *
+ */ +public class CoordinatingTestCase1DummyRun extends CoordinatingTestCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(CoordinatingTestCase1DummyRun.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public CoordinatingTestCase1DummyRun(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + */ + public void testDummyRun() throws Exception + { + log.debug("public void testDummyRun(): called"); + + Map testConfig = new HashMap(); + testConfig.put("TEST_NAME", "TC1_DummyRun"); + + Message[] reports = sequenceTest(testConfig); + + // Compare sender and receiver reports. + Assert.assertEquals("Expected to get 2 dummy reports.", 2, reports.length); + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC1_DummyRun"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java new file mode 100644 index 0000000000..b1b2d9f847 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java @@ -0,0 +1,90 @@ +/* + * + * 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.testcases; + +import java.util.HashMap; +import java.util.Map; + +import javax.jms.Message; + +import junit.framework.Assert; + +import org.apache.log4j.Logger; + +import org.apache.qpid.interop.coordinator.CoordinatingTestCase; + +/** + *

+ *
CRC Card
Responsibilities Collaborations + *
Setup p2p test parameters and compare with test output. {@link CoordinatingTestCase} + *
+ */ +public class CoordinatingTestCase2BasicP2P extends CoordinatingTestCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(CoordinatingTestCase2BasicP2P.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public CoordinatingTestCase2BasicP2P(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + */ + public void testBasicP2P() throws Exception + { + log.debug("public void testBasicP2P(): called"); + + Map testConfig = new HashMap(); + testConfig.put("TEST_NAME", "TC2_BasicP2P"); + testConfig.put("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); + testConfig.put("P2P_NUM_MESSAGES", 50); + + Message[] reports = sequenceTest(testConfig); + + // Compare sender and receiver reports. + int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); + Assert.assertEquals("Sender and receiver messages sent did not match up.", messagesSent, messagesReceived); + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC2_BasicP2P"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase3BasicPubSub.java new file mode 100644 index 0000000000..702c240e9a --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase3BasicPubSub.java @@ -0,0 +1,92 @@ +/* + * + * 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.testcases; + +import java.util.HashMap; +import java.util.Map; + +import javax.jms.Message; + +import junit.framework.Assert; + +import org.apache.log4j.Logger; + +import org.apache.qpid.interop.coordinator.CoordinatingTestCase; + +/** + *

+ *
CRC Card
Responsibilities Collaborations + *
Setup pub/sub test parameters and compare with test output. {@link CoordinatingTestCase} + *
+ */ +public class CoordinatingTestCase3BasicPubSub extends CoordinatingTestCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(CoordinatingTestCase3BasicPubSub.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public CoordinatingTestCase3BasicPubSub(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + */ + public void testBasicPubSub() throws Exception + { + log.debug("public void testBasicPubSub(): called"); + + Map testConfig = new HashMap(); + testConfig.put("TEST_NAME", "TC3_BasicPubSub"); + testConfig.put("PUBSUB_KEY", "tc3route"); + testConfig.put("PUBSUB_NUM_MESSAGES", 10); + testConfig.put("PUBSUB_NUM_RECEIVERS", 5); + + Message[] reports = sequenceTest(testConfig); + + // Compare sender and receiver reports. + int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 10, messagesSent); + Assert.assertEquals("Received messages did not match up to num sent * num receivers.", messagesSent * 5, + messagesReceived); + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC3_BasicPubSub"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java new file mode 100644 index 0000000000..5545f8d2dc --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java @@ -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}. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Count messages received on a topic. {@link org.apache.qpid.interop.old.Publisher} + *
Send reports on messages received, when requested to. {@link org.apache.qpid.interop.old.Publisher} + *
Shutdown, when requested to. {@link org.apache.qpid.interop.old.Publisher} + *
+ * + * @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 true if it is a termination request control message, false 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 true if it is a report request control message, false 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 trueIf the specified field has the specified value, fals 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); + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java new file mode 100644 index 0000000000..f3a545f580 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java @@ -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}. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
+ * + * @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(); + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/CircuitTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/CircuitTestCase.java deleted file mode 100644 index 966a545e16..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/CircuitTestCase.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * - * 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.testcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.sequencers.TestCaseSequencer; -import org.apache.qpid.test.framework.Circuit; -import org.apache.qpid.test.framework.FrameworkBaseCase; -import org.apache.qpid.test.framework.MessagingTestConfigProperties; - -import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; -import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; - -/** - * CircuitTestCase runs a test over a {@link Circuit} controlled by the test parameters. - * - *

- *
CRC Card
Responsibilities Collaborations - *
- *
- * - * @todo When working with test context properties, add overrides to defaults to the singleton instance, but when taking - * a starting point to add specific test case parameters to, take a copy. Use the copy with test case specifics - * to control the test. - */ -public class CircuitTestCase extends FrameworkBaseCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(CircuitTestCase.class); - - /** - * Creates a new test case with the specified name. - * - * @param name The test case name. - */ - public CircuitTestCase(String name) - { - super(name); - } - - /** - * Performs the a basic P2P test case. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testBasicP2P() throws Exception - { - log.debug("public void testBasicP2P(): called"); - - // Get the test parameters, any overrides on the command line will have been applied. - ParsedProperties testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); - - // Customize the test parameters. - testProps.setProperty("TEST_NAME", "DEFAULT_CIRCUIT_TEST"); - testProps.setProperty(MessagingTestConfigProperties.SEND_DESTINATION_NAME_ROOT_PROPNAME, "testqueue"); - - // Get the test sequencer to create test circuits and run the standard test procedure through. - TestCaseSequencer sequencer = getTestSequencer(); - - // Send the test messages, and check that there were no errors doing so. - Circuit testCircuit = sequencer.createCircuit(testProps); - sequencer.sequenceTest(testCircuit, assertionList(testCircuit.getPublisher().noExceptionsAssertion()), testProps); - - // Check that all of the message were sent. - // Check that the receiving end got the same number of messages as the publishing end. - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as known to the test - * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test - * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case - * name "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "DEFAULT_CIRCUIT_TEST"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java deleted file mode 100644 index 73e08b578e..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * - * 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.testcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.distributedtesting.DistributedTestCase; - -import java.util.Properties; - -/** - * Coordinates test case 1, from the interop test specification. This test connects up the sender and receivers roles, - * and gets some dummy test reports from them, in order to check that the test framework itself is operational. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Exercises the interop testing framework without actually sending any test messages. - * {@link org.apache.qpid.test.framework.distributedtesting.DistributedTestCase} - *
- */ -public class InteropTestCase1DummyRun extends DistributedTestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(InteropTestCase1DummyRun.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public InteropTestCase1DummyRun(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testDummyRun() throws Exception - { - log.debug("public void testDummyRun(): called"); - - Properties testConfig = new Properties(); - testConfig.put("TEST_NAME", "TC1_DummyRun"); - - /*Message[] reports =*/ getTestSequencer().sequenceTest(null, null, testConfig); - - // Compare sender and receivers reports. - // Assert.assertEquals("Expected to get 2 dummy reports.", 2, reports.length); - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC1_DummyRun"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java deleted file mode 100644 index f77bbf032f..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * - * 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.testcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.distributedtesting.DistributedTestCase; - -import java.util.Properties; - -/** - * Implements test case 2, from the interop test specification. This test sets up the TC2_BasicP2P test for 50 - * messages. It checks that the sender and receivers reports both indicate that all the test messages were transmitted - * successfully. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Setup p2p test parameters and compare with test output. {@link DistributedTestCase} - *
- */ -public class InteropTestCase2BasicP2P extends DistributedTestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(InteropTestCase2BasicP2P.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public InteropTestCase2BasicP2P(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testBasicP2P() throws Exception - { - log.debug("public void testBasicP2P(): called"); - - Properties testConfig = new Properties(); - testConfig.setProperty("TEST_NAME", "TC2_BasicP2P"); - testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); - testConfig.put("P2P_NUM_MESSAGES", 50); - - /*Message[] reports =*/ getTestSequencer().sequenceTest(null, null, testConfig); - - // Compare sender and receivers reports. - /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); - int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); - - Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); - Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived);*/ - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC2_BasicP2P"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java deleted file mode 100644 index ad27ca63bd..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * - * 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.testcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.distributedtesting.DistributedTestCase; - -import java.util.Properties; - -/** - *

- *
CRC Card
Responsibilities Collaborations - *
Setup pub/sub test parameters and compare with test output. {@link DistributedTestCase} - *
- */ -public class InteropTestCase3BasicPubSub extends DistributedTestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(InteropTestCase3BasicPubSub.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public InteropTestCase3BasicPubSub(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testBasicPubSub() throws Exception - { - log.debug("public void testBasicPubSub(): called"); - - Properties testConfig = new Properties(); - testConfig.put("TEST_NAME", "TC3_BasicPubSub"); - testConfig.put("PUBSUB_KEY", "tc3route"); - testConfig.put("PUBSUB_NUM_MESSAGES", 10); - testConfig.put("PUBSUB_NUM_RECEIVERS", 5); - - /*Message[] reports =*/ getTestSequencer().sequenceTest(null, null, testConfig); - - // Compare sender and receivers reports. - /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); - int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); - - Assert.assertEquals("The requested number of messages were not sent.", 10, messagesSent); - Assert.assertEquals("Received messages did not match up to num sent * num receivers.", messagesSent * 5, - messagesReceived);*/ - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC3_BasicPubSub"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java new file mode 100644 index 0000000000..37952d08c8 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java @@ -0,0 +1,104 @@ +/* + * + * 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. + * + *

+ *
CRC Card
Responsibilities + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Perform test case actions. + *
Generate test reports. + *
+ */ +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 true to accept the invitation, false 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. + * return from here when you have finished the test.. this will signal the controller that the test has ended. + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start() throws JMSException; + + /** + * Gives notice of termination of the test case actions. + * + * @throws JMSException Any JMSException resulting from allowed to fall through. + */ + public void terminate() throws JMSException, InterruptedException; + + /** + * 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/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java new file mode 100644 index 0000000000..a904bfa419 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java @@ -0,0 +1,422 @@ +/* + * + * 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 org.apache.log4j.Logger; +import org.apache.qpid.interop.testclient.testcases.TestCase1DummyRun; +import org.apache.qpid.interop.testclient.testcases.TestCase2BasicP2P; +import org.apache.qpid.util.CommandLineParser; +import org.apache.qpid.util.PropertiesUtils; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * 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}. + * + *

+ *
Messages Handled by TestClient
Message Action + *
Invite(compulsory) Reply with Enlist. + *
Invite(test case) Reply with Enlist if test case available. + *
AssignRole(test case) Reply with Accept Role if matches an enlisted test. Keep test parameters. + *
Start Send test messages defined by test parameters. Send report on messages sent. + *
Status Request Send report on messages received. + *
+ * + *

+ *
CRC Card
Responsibilities Collaborations + *
Handle all incoming control messages. {@link InteropClientTestCase} + *
Configure and look up test cases by name. {@link InteropClientTestCase} + *
+ */ +public class TestClient implements MessageListener +{ + private static Logger log = Logger.getLogger(TestClient.class); + + public static final String CONNECTION_PROPERTY = "connectionfactory.broker"; + public static final String CONNECTION_NAME = "broker"; + public static final String CLIENT_NAME = "java"; + public static final String DEFAULT_CONNECTION_PROPS_RESOURCE = "org/apache/qpid/interop/connection.properties"; + + /** Holds the URL of the broker to run the tests on. */ + public static String brokerUrl; + + /** Holds the virtual host to run the tests on. If null, then the default virtual host is used. */ + public static String virtualHost; + + /** Holds all the test cases loaded from the classpath. */ + Map testCases = new HashMap(); + + protected InteropClientTestCase currentTestCase; + + protected Connection _connection; + protected MessageProducer producer; + protected Session session; + + protected String clientName = CLIENT_NAME; + + /** + * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client + * identifying name. + * + * @param brokerUrl The url of the broker to connect to. + * @param virtualHost The virtual host to conect to. + * @param clientName The client name to use. + */ + public TestClient(String brokerUrl, String virtualHost, String clientName) + { + log.debug("public TestClient(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost + + ", String clientName = " + clientName + "): called"); + + // Retain the connection parameters. + this.brokerUrl = brokerUrl; + this.virtualHost = virtualHost; + this.clientName = clientName; + } + + /** + * The entry point for the interop test coordinator. This client accepts the following command line arguments: + * + *

+ *
-b The broker URL. Optional. + *
-h The virtual host. Optional. + *
-n The test client name. Optional. + *
name=value Trailing argument define name/value pairs. Added to system properties. Optional. + *
+ * + * @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", "false"}, + {"h", "The virtual host to use.", "virtual host", "false"}, + {"n", "The test client name.", "name", "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"); + String clientName = options.getProperty("n"); + + // 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, (clientName == null) ? CLIENT_NAME : clientName); + + // Use a class path scanner to find all the interop test case implementations. + Collection> testCaseClasses = + new ArrayList>(); + // ClasspathScanner.getMatches(InteropClientTestCase.class, "^TestCase.*", true); + // Hard code the test classes till the classpath scanner is fixed. + Collections.addAll(testCaseClasses, + new Class[]{TestCase1DummyRun.class, TestCase2BasicP2P.class, TestClient.class}); + + try + { + client.start(testCaseClasses); + } + catch (Exception e) + { + log.error("The test client was unable to start.", e); + System.exit(1); + } + } + + /** + * Starts the interop test client running. This causes it to start listening for incoming test invites. + * + * @throws JMSException Any underlying JMSExceptions are allowed to fall through. @param testCaseClasses + */ + protected void start(Collection> testCaseClasses) throws JMSException + { + log.debug("private void start(): called"); + + // Create all the test case implementations and index them by the test names. + for (Class nextClass : testCaseClasses) + { + try + { + InteropClientTestCase testCase = nextClass.newInstance(); + testCases.put(testCase.getName(), testCase); + } + catch (InstantiationException e) + { + log.warn("Could not instantiate test case class: " + nextClass.getName(), e); + // Ignored. + } + catch (IllegalAccessException e) + { + log.warn("Could not instantiate test case class due to illegal access: " + nextClass.getName(), e); + // Ignored. + } + } + + // Open a connection to communicate with the coordinator on. + _connection = createConnection(DEFAULT_CONNECTION_PROPS_RESOURCE, clientName, brokerUrl, virtualHost); + + session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Set this up to listen for control messages. + MessageConsumer consumer = session.createConsumer(session.createTopic("iop.control." + clientName)); + consumer.setMessageListener(this); + + MessageConsumer consumer2 = session.createConsumer(session.createTopic("iop.control")); + consumer2.setMessageListener(this); + + // Create a producer to send replies with. + producer = session.createProducer(null); + + // Start listening for incoming control messages. + _connection.start(); + } + + + public static Connection createConnection(String connectionPropsResource, String brokerUrl, String virtualHost) + { + return createConnection(connectionPropsResource, "clientID", brokerUrl, virtualHost); + } + + /** + * Establishes a JMS connection using a properties file and qpids built in JNDI implementation. This is a simple + * convenience method for code that does anticipate handling connection failures. All exceptions that indicate that + * the connection has failed, are wrapped as rutime exceptions, preumably handled by a top level failure handler. + * + * @param connectionPropsResource The name of the connection properties file. + * @param clientID + * @param brokerUrl The broker url to connect to, null to use the default from the + * properties. + * @param virtualHost The virtual host to connectio to, null to use the default. + * + * @return A JMS conneciton. + * + * @todo Make username/password configurable. Allow multiple urls for fail over. Once it feels right, move it to a + * Utils library class. + */ + public static Connection createConnection(String connectionPropsResource, String clientID, String brokerUrl, String virtualHost) + { + log.debug("public static Connection createConnection(String connectionPropsResource = " + connectionPropsResource + + ", String brokerUrl = " + brokerUrl + ", String clientID = " + clientID + + ", String virtualHost = " + virtualHost + " ): called"); + + try + { + Properties connectionProps = + PropertiesUtils.getProperties(TestClient.class.getClassLoader().getResourceAsStream( + connectionPropsResource)); + + if (brokerUrl != null) + { + String connectionString = + "amqp://guest:guest@" + clientID + "/" + ((virtualHost != null) ? virtualHost : "") + "?brokerlist='" + brokerUrl + "'"; + connectionProps.setProperty(CONNECTION_PROPERTY, connectionString); + } + + Context ctx = new InitialContext(connectionProps); + + ConnectionFactory cf = (ConnectionFactory) ctx.lookup(CONNECTION_NAME); + Connection connection = cf.createConnection(); + + return connection; + } + catch (IOException e) + { + throw new RuntimeException(e); + } + catch (NamingException e) + { + throw new RuntimeException(e); + } + catch (JMSException e) + { + throw new RuntimeException(e); + } + } + + /** + * Handles all incoming control messages. + * + * @param message The incoming message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + try + { + String controlType = message.getStringProperty("CONTROL_TYPE"); + String testName = message.getStringProperty("TEST_NAME"); + + log.info("onMessage(Message message = " + message + "): for '" + controlType + "' to '" + testName + "'"); + + // Check if the message is a test invite. + if ("INVITE".equals(controlType)) + { + // Flag used to indicate that an enlist should be sent. Only enlist to compulsory invites or invites + // for which test cases exist. + boolean enlist = false; + + if (testName != null) + { + log.debug("Got an invite to test: " + testName); + + // Check if the requested test case is available. + InteropClientTestCase testCase = testCases.get(testName); + + if (testCase != null) + { + // Make the requested test case the current test case. + currentTestCase = testCase; + enlist = true; + } + else + { + log.warn("'" + testName + "' not part of this clients tests."); + } + } + else + { + log.debug("Got a compulsory invite."); + + enlist = true; + } + + if (enlist) + { + // Reply with the client name in an Enlist message. + Message enlistMessage = session.createMessage(); + enlistMessage.setStringProperty("CONTROL_TYPE", "ENLIST"); + enlistMessage.setStringProperty("CLIENT_NAME", clientName); + enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); + enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.info("Sending Message '" + enlistMessage + "'. to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), enlistMessage); + } + } + else if ("ASSIGN_ROLE".equals(controlType)) + { + // Assign the role to the current test case. + String roleName = message.getStringProperty("ROLE"); + + log.debug("Got a role assignment to role: " + roleName); + + InteropClientTestCase.Roles role = Enum.valueOf(InteropClientTestCase.Roles.class, roleName); + + currentTestCase.assignRole(role, message); + + // Reply by accepting the role in an Accept Role message. + Message acceptRoleMessage = session.createMessage(); + acceptRoleMessage.setStringProperty("CONTROL_TYPE", "ACCEPT_ROLE"); + acceptRoleMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + producer.send(message.getJMSReplyTo(), acceptRoleMessage); + } + else if ("START".equals(controlType) || "STATUS_REQUEST".equals(controlType)) + { + if ("START".equals(controlType)) + { + log.debug("Got a start notification."); + + // Start the current test case. + currentTestCase.start(); + } + else + { + log.debug("Got a status request."); + } + + // Generate the report from the test case and reply with it as a Report message. + Message reportMessage = currentTestCase.getReport(session); + reportMessage.setStringProperty("CONTROL_TYPE", "REPORT"); + reportMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + producer.send(message.getJMSReplyTo(), reportMessage); + } + else if ("TERMINATE".equals(controlType)) + { + log.info("Received termination instruction from coordinator."); + +// try +// { +// currentTestCase.terminate(); +// } +// catch (InterruptedException e) +// { +// // +// } + // Is a cleaner shutdown needed? + _connection.close(); + System.exit(0); + } + else + { + // Log a warning about this but otherwise ignore it. + log.warn("Got an unknown control message, controlType = " + controlType + ", message = " + message); + } + } + catch (JMSException e) + { + // Log a warning about this, but otherwise ignore it. + log.warn("A JMSException occurred whilst handling a message."); + log.debug("Got JMSException whilst handling message: " + message, e); + } + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java new file mode 100644 index 0000000000..5f257c0b36 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java @@ -0,0 +1,96 @@ +/* + * + * 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 org.apache.log4j.Logger; + +import org.apache.qpid.interop.testclient.InteropClientTestCase; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; + +/** + * 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. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Perform test case actions. + *
Generate test reports. + *
+ */ +public class TestCase1DummyRun implements InteropClientTestCase +{ + private static final Logger log = Logger.getLogger(TestCase1DummyRun.class); + + public String getName() + { + log.debug("public String getName(): called"); + + return "TC1_DummyRun"; + } + + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage): called"); + + // Test parameters don't matter, accept all invites. + return true; + } + + public void assignRole(Roles role, Message assignRoleMessage) throws JMSException + { + log.debug("public void assignRole(Roles role, Message assignRoleMessage): called"); + + // Do nothing, both roles are the same. + } + + public void start() + { + log.debug("public void start(): called"); + + // Do nothing. + } + + public void terminate() throws JMSException + { + //todo + } + + public Message getReport(Session session) throws JMSException + { + log.debug("public Message getReport(Session session): called"); + + // 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) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Ignore any messages. + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java new file mode 100644 index 0000000000..ff56ee9b93 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java @@ -0,0 +1,214 @@ +/* + * + * 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.*; + +import org.apache.log4j.Logger; + +import org.apache.qpid.interop.testclient.InteropClientTestCase; +import org.apache.qpid.interop.testclient.TestClient; + +/** + * 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. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages. + *
Generate test reports. + *
+ */ +public class TestCase2BasicP2P implements InteropClientTestCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase2BasicP2P.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The routing key to send them to on the default direct exchange. */ + private Destination sendDestination; + + /** The connection to send the test messages on. */ + private Connection connection; + + /** The session to send the test messages on. */ + private Session session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC2_BasicP2P"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // 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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Create a new connection to pass the test messages on. + connection = + TestClient.createConnection(TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, TestClient.brokerUrl, + TestClient.virtualHost); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); + sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); + + log.debug("numMessages = " + numMessages); + log.debug("sendDestination = " + sendDestination); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a message producer if so. + case SENDER: + producer = session.createProducer(sendDestination); + break; + + // Otherwise the receiver role is being assigned, so set this up to listen for messages. + case RECEIVER: + MessageConsumer consumer = session.createConsumer(sendDestination); + consumer.setMessageListener(this); + break; + } + + connection.start(); + } + + /** + * Performs the test case actions. + */ + public void start() throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = session.createTextMessage("test"); + + for (int i = 0; i < numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + public void terminate() throws JMSException + { + //todo + } + + /** + * 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 + { + log.debug("public Message getReport(Session session): called"); + + // Close the test connection. + connection.close(); + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java new file mode 100644 index 0000000000..7b35142c82 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java @@ -0,0 +1,249 @@ +/* + * + * 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.*; + +import org.apache.log4j.Logger; + +import org.apache.qpid.interop.testclient.InteropClientTestCase; + +/** + * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the + * default topic exchange, using the specified number of receiver connections. Produces reports on the actual number of + * messages sent/received. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages using pub/sub. + *
Generate test reports. + *
+ */ +public class TestCase3BasicPubSub implements InteropClientTestCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase3BasicPubSub.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The number of receiver connection to use. */ + private int numReceivers; + + /** The routing key to send them to on the default direct exchange. */ + private Destination sendDestination; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC3_BasicPubSub"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // 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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); + numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); + String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); + + log.debug("numMessages = " + numMessages); + log.debug("numReceivers = " + numReceivers); + log.debug("sendKey = " + sendKey); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = + org.apache.qpid.interop.testclient.TestClient.createConnection(org.apache.qpid.interop.testclient.TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, org.apache.qpid.interop.testclient.TestClient.brokerUrl, + org.apache.qpid.interop.testclient.TestClient.virtualHost); + session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + sendDestination = session[0].createTopic(sendKey); + + producer = session[0].createProducer(sendDestination); + break; + + // Otherwise the receiver role is being assigned, so set this up to listen for messages on the required number + // of receiver connections. + case RECEIVER: + // Create the required number of receiver connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = + org.apache.qpid.interop.testclient.TestClient.createConnection(org.apache.qpid.interop.testclient.TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, org.apache.qpid.interop.testclient.TestClient.brokerUrl, + org.apache.qpid.interop.testclient.TestClient.virtualHost); + session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); + + sendDestination = session[i].createTopic(sendKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + consumer.setMessageListener(this); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (int i = 0; i < connection.length; i++) + { + connection[i].start(); + } + } + + /** + * Performs the test case actions. + */ + public void start() throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = session[0].createTextMessage("test"); + + for (int i = 0; i < numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + public void terminate() throws JMSException, InterruptedException + { + //todo + } + + /** + * 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 + { + log.debug("public Message getReport(Session session): called"); + + // Close the test connections. + for (int i = 0; i < connection.length; i++) + { + connection[i].close(); + } + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java deleted file mode 100644 index 65e05fab4b..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java +++ /dev/null @@ -1,905 +0,0 @@ -/* - * - * 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.sustained; - -import org.apache.log4j.Logger; - -import org.apache.qpid.client.AMQNoConsumersException; -import org.apache.qpid.client.AMQNoRouteException; -import org.apache.qpid.test.framework.distributedtesting.TestClient; -import org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub; -import org.apache.qpid.test.framework.TestUtils; - -import javax.jms.Connection; -import javax.jms.Destination; -import javax.jms.ExceptionListener; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageConsumer; -import javax.jms.MessageListener; -import javax.jms.MessageProducer; -import javax.jms.Session; -import javax.jms.TextMessage; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.CountDownLatch; - -/** - * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the - * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of - * messages sent/received. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Send required number of test messages using pub/sub.
Generate test reports. - *
- */ -public class SustainedClientTestCase extends TestCase3BasicPubSub implements ExceptionListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(SustainedClientTestCase.class); - - /** Used to log to the console. */ - private static final Logger console = Logger.getLogger("SustainedTest"); - - /** The role to be played by the test. */ - private Roles role; - - /** The number of receivers connection to use. */ - private int numReceivers; - - /** The routing key to send them to on the default direct exchange. */ - private Destination sendDestination; - - /** The routing key to send updates to on the default direct exchange. */ - private Destination sendUpdateDestination; - - /** The connections to send/receive the test messages on. */ - private Connection[] connection; - - /** The sessions to send/receive the test messages on. */ - private Session[] session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** Adapter that adjusts the send rate based on the updates from clients. */ - SustainedRateAdapter _rateAdapter; - - /** */ - int _batchSize; - - private static final long TEN_MILLI_SEC = 10000000; - private static final int DEBUG_LOG_UPATE_INTERVAL = 10; - private static final int LOG_UPATE_INTERVAL = 10; - private static final boolean SLEEP_PER_MESSAGE = Boolean.getBoolean("sleepPerMessage"); - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "Perf_SustainedPubSub"; - } - - /** - * 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 receivers. - * @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 - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Take note of the role to be played. - this.role = role; - - // Extract and retain the test parameters. - numReceivers = assignRoleMessage.getIntProperty("SUSTAINED_NUM_RECEIVERS"); - _batchSize = assignRoleMessage.getIntProperty("SUSTAINED_UPDATE_INTERVAL"); - String sendKey = assignRoleMessage.getStringProperty("SUSTAINED_KEY"); - String sendUpdateKey = assignRoleMessage.getStringProperty("SUSTAINED_UPDATE_KEY"); - int ackMode = assignRoleMessage.getIntProperty("ACKNOWLEDGE_MODE"); - String clientName = assignRoleMessage.getStringProperty("CLIENT_NAME"); - - if (log.isDebugEnabled()) - { - log.debug("numReceivers = " + numReceivers); - log.debug("_batchSize = " + _batchSize); - log.debug("ackMode = " + ackMode); - log.debug("sendKey = " + sendKey); - log.debug("sendUpdateKey = " + sendUpdateKey); - log.debug("role = " + role); - } - - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - console.info("Creating Sender"); - // Create a new connection to pass the test messages on. - connection = new Connection[1]; - session = new Session[1]; - - connection[0] = TestUtils.createConnection(TestClient.testContextProperties); - session[0] = connection[0].createSession(false, ackMode); - - // Extract and retain the test parameters. - sendDestination = session[0].createTopic(sendKey); - - connection[0].setExceptionListener(this); - - producer = session[0].createProducer(sendDestination); - - sendUpdateDestination = session[0].createTopic(sendUpdateKey); - MessageConsumer updateConsumer = session[0].createConsumer(sendUpdateDestination); - - _rateAdapter = new SustainedRateAdapter(this); - updateConsumer.setMessageListener(_rateAdapter); - - break; - - // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number - // of receivers connections. - case RECEIVER: - console.info("Creating Receiver"); - // Create the required number of receivers connections. - connection = new Connection[numReceivers]; - session = new Session[numReceivers]; - - for (int i = 0; i < numReceivers; i++) - { - connection[i] = TestUtils.createConnection(TestClient.testContextProperties); - session[i] = connection[i].createSession(false, ackMode); - - sendDestination = session[i].createTopic(sendKey); - - sendUpdateDestination = session[i].createTopic(sendUpdateKey); - - MessageConsumer consumer = session[i].createConsumer(sendDestination); - - consumer.setMessageListener(new SustainedListener(clientName + "-" + i, _batchSize, session[i], - sendUpdateDestination)); - } - - break; - } - - // Start all the connection dispatcher threads running. - for (int i = 0; i < connection.length; i++) - { - connection[i].start(); - } - } - - /** Performs the test case actions. */ - public void start() throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - _rateAdapter.run(); - break; - case RECEIVER: - - } - - // return from here when you have finished the test.. this will signal the controller and - } - - public void terminate() throws JMSException, InterruptedException - { - if (_rateAdapter != null) - { - _rateAdapter.stop(); - } - } - - /** - * 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 - { - log.debug("public Message getReport(Session session): called"); - - // Close the test connections. - for (int i = 0; i < connection.length; i++) - { - connection[i].close(); - } - - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - - return report; - } - - public void onException(JMSException jmsException) - { - Exception linked = jmsException.getLinkedException(); - - if (linked != null) - { - if (log.isDebugEnabled()) - { - log.debug("Linked Exception:" + linked); - } - - if ((linked instanceof AMQNoRouteException) || (linked instanceof AMQNoConsumersException)) - { - if (log.isDebugEnabled()) - { - if (linked instanceof AMQNoConsumersException) - { - log.warn("No clients currently available for message:" - + ((AMQNoConsumersException) linked).getUndeliveredMessage()); - } - else - { - log.warn("No route for message"); - } - } - - // Tell the rate adapter that there are no clients ready yet - _rateAdapter.NO_CLIENTS = true; - } - } - else - { - log.warn("Exception:" + linked); - } - } - - /** - * Inner class that listens for messages and sends a report for the time taken between receiving the 'start' and - * 'end' messages. - */ - class SustainedListener implements MessageListener - { - /** Number of messages received */ - private long _received = 0; - /** The number of messages in the batch */ - private int _batchSize = 0; - /** Record of the when the 'start' messagse was sen */ - private Long _startTime; - /** Message producer to use to send reports */ - MessageProducer _updater; - /** Session to create the report message on */ - Session _session; - /** Record of the client ID used for this SustainedListnener */ - String _client; - - /** - * Main Constructor - * - * @param clientname The _client id used to identify this connection. - * @param batchSize The number of messages that are to be sent per batch. Note: This is not used to - * control the interval between sending reports. - * @param session The session used for communication. - * @param sendDestination The destination that update reports should be sent to. - * - * @throws JMSException My occur if creatingthe Producer fails - */ - public SustainedListener(String clientname, int batchSize, Session session, Destination sendDestination) - throws JMSException - { - _batchSize = batchSize; - _client = clientname; - _session = session; - _updater = session.createProducer(sendDestination); - } - - public void onMessage(Message message) - { - if (log.isTraceEnabled()) - { - log.trace("Message " + _received + "received in listener"); - } - - if (message instanceof TextMessage) - { - try - { - _received++; - if (((TextMessage) message).getText().equals("start")) - { - log.debug("Starting Batch"); - _startTime = System.nanoTime(); - } - else if (((TextMessage) message).getText().equals("end")) - { - if (_startTime != null) - { - long currentTime = System.nanoTime(); - sendStatus(currentTime - _startTime, _received, message.getIntProperty("BATCH")); - log.debug("End Batch"); - } - } - } - catch (JMSException e) - { - // ignore error - } - } - - } - - /** - * sendStatus creates and sends the report back to the publisher - * - * @param time taken for the the last batch - * @param received Total number of messages received. - * @param batchNumber the batch number - * @throws JMSException if an error occurs during the send - */ - private void sendStatus(long time, long received, int batchNumber) throws JMSException - { - Message updateMessage = _session.createTextMessage("update"); - updateMessage.setStringProperty("CLIENT_ID", ":" + _client); - updateMessage.setStringProperty("CONTROL_TYPE", "UPDATE"); - updateMessage.setLongProperty("RECEIVED", received); - updateMessage.setIntProperty("BATCH", batchNumber); - updateMessage.setLongProperty("DURATION", time); - - if (log.isInfoEnabled()) - { - log.info("**** SENDING [" + batchNumber + "]**** " + "CLIENT_ID:" + _client + " RECEIVED:" + received - + " BATCH:" + batchNumber + " DURATION:" + time); - } - - // Output on the main console.info the details of this batch - if ((batchNumber % 10) == 0) - { - console.info("Sending Report [" + batchNumber + "] " + "CLIENT_ID:" + _client + " RECEIVED:" + received - + " BATCH:" + batchNumber + " DURATION:" + time); - } - - _updater.send(updateMessage); - } - } - - /** - * This class is used here to adjust the _delay value which in turn is used to control the number of messages/second - * that are sent through the test system. - * - * By keeping a record of the messages recevied and the average time taken to process the batch size can be - * calculated and so the delay can be adjusted to maintain that rate. - * - * Given that delays of < 10ms can be rounded up the delay is only used between messages if the _delay > 10ms * no - * messages in the batch. Otherwise the delay is used at the end of the batch. - */ - class SustainedRateAdapter implements MessageListener, Runnable - { - private SustainedClientTestCase _client; - private long _batchVariance = Integer.getInteger("batchVariance", 3); // no. batches to allow drifting - private long _timeVariance = TEN_MILLI_SEC * 5; // no. nanos between send and report delay (10ms) - private volatile long _delay; // in nanos - private long _sent; - private Map _slowClients = new HashMap(); - private static final long PAUSE_SLEEP = TEN_MILLI_SEC / 1000; // 10 ms - private static final long NO_CLIENT_SLEEP = 1000; // 1s - private volatile boolean NO_CLIENTS = true; - private int _delayShifting; - private final int REPORTS_WITHOUT_CHANGE = Integer.getInteger("stableReportCount", 5); - private boolean _warmedup = false; - private static final long EXPECTED_TIME_PER_BATCH = 100000L; - private int _warmUpBatches = Integer.getInteger("warmUpBatches", 10); - - SustainedRateAdapter(SustainedClientTestCase client) - { - _client = client; - } - - public void onMessage(Message message) - { - if (log.isDebugEnabled()) - { - log.debug("SustainedRateAdapter onMessage(Message message = " + message + "): called"); - } - - try - { - String controlType = message.getStringProperty("CONTROL_TYPE"); - - // Check if the message is a test invite. - if ("UPDATE".equals(controlType)) - { - NO_CLIENTS = false; - long duration = message.getLongProperty("DURATION"); - long totalReceived = message.getLongProperty("RECEIVED"); - String client = message.getStringProperty("CLIENT_ID"); - int batchNumber = message.getIntProperty("BATCH"); - - if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) - { - log.info("Update Report: CLIENT_ID:" + client + " RECEIVED:" + totalReceived + " Recevied BATCH:" - + batchNumber + " DURATION:" + duration); - } - - recordSlow(client, totalReceived, batchNumber); - - adjustDelay(client, batchNumber, duration); - - // Warm up completes when: - // we haven't warmed up - // and the number of batches sent to each client is at least half of the required warmup batches - if (!_warmedup && (batchNumber >= _warmUpBatches)) - { - _warmedup = true; - _warmup.countDown(); - - } - } - } - catch (JMSException e) - { - // - } - } - - CountDownLatch _warmup = new CountDownLatch(1); - - int _numBatches = 10000; - - // long[] _timings = new long[_numBatches]; - private boolean _running = true; - - public void run() - { - console.info("Warming up"); - - doBatch(_warmUpBatches); - - try - { - // wait for warmup to complete. - _warmup.await(); - - // set delay to the average length of the batches - _delay = _totalDuration / _warmUpBatches / delays.size(); - - console.info("Warmup complete delay set : " + _delay + " based on _totalDuration: " + _totalDuration - + " over no. batches: " + _warmUpBatches + " with client count: " + delays.size()); - - _totalDuration = 0L; - _totalReceived = 0L; - _sent = 0L; - } - catch (InterruptedException e) - { - // - } - - doBatch(_numBatches); - - } - - private void doBatch(int batchSize) // long[] timings, - { - TextMessage testMessage = null; - try - { - testMessage = _client.session[0].createTextMessage("start"); - - for (int batch = 0; batch <= batchSize; batch++) - // while (_running) - { - long start = System.nanoTime(); - - testMessage.setText("start"); - testMessage.setIntProperty("BATCH", batch); - - _client.producer.send(testMessage); - _rateAdapter.sentMessage(); - - testMessage.setText("test"); - // start at 2 so start and end count as part of batch - for (int m = 2; m < _batchSize; m++) - { - _client.producer.send(testMessage); - _rateAdapter.sentMessage(); - } - - testMessage.setText("end"); - _client.producer.send(testMessage); - _rateAdapter.sentMessage(); - - long end = System.nanoTime(); - - long sendtime = end - start; - - if (log.isDebugEnabled()) - { - log.info("Sent batch[" + batch + "](" + _batchSize + ") in " + sendtime); // timings[batch]); - } - - if ((batch % LOG_UPATE_INTERVAL) == 0) - { - console.info("Sent Batch[" + batch + "](" + _batchSize + ")" + status()); - } - - _rateAdapter.sleepBatch(); - - } - } - catch (JMSException e) - { - console.error("Runner ended"); - } - } - - private String status() - { - return " TotalDuration: " + _totalDuration + " for " + delays.size() + " consumers" + " Delay is " + _delay - + " resulting in " - + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch")); - } - - private void sleepBatch() - { - if (checkForSlowClients()) - { // if there werwe slow clients we have already slept so don't sleep anymore again. - return; - } - - if (!SLEEP_PER_MESSAGE) - { - // per batch sleep.. if sleep is to small to spread over the batch. - if (_delay <= (TEN_MILLI_SEC * _batchSize)) - { - sleepLong(_delay); - } - else - { - log.info("Not sleeping _delay > ten*batch is:" + _delay); - } - } - } - - public void stop() - { - _running = false; - } - - Map delays = new HashMap(); - Long _totalReceived = 0L; - Long _totalDuration = 0L; - int _skipUpdate = 0; - - /** - * Adjust the delay for sending messages based on this update from the client - * - * @param client The client that send this update - * @param duration The time taken for the last batch of messagse - * @param batchNumber The reported batchnumber from the client - */ - private void adjustDelay(String client, int batchNumber, long duration) - { - // Retrieve the current total time taken for this client. - Long currentTime = delays.get(client); - - // Add the new duration time to this client - if (currentTime == null) - { - currentTime = duration; - } - else - { - currentTime += duration; - } - - delays.put(client, currentTime); - - long batchesSent = _sent / _batchSize; - - // ensure we don't divide by zero - if (batchesSent == 0) - { - batchesSent = 1L; - } - - _totalReceived += _batchSize; - _totalDuration += duration; - - // calculate average duration accross clients per batch - long averageDuration = _totalDuration / delays.size() / batchesSent; - - // calculate the difference between current send delay and average report delay - long diff = (duration) - averageDuration; - - if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) - { - log.info("TotalDuration:" + _totalDuration + " for " + delays.size() + " consumers." + " on batch: " - + batchesSent + " received batch: " + batchNumber + " Batch Duration: " + duration + " Average: " - + averageDuration + " so diff: " + diff + " for : " + client + " Delay is " + _delay + " resulting in " - + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch"))); - } - - // if the averageDuration differs from the current by more than the specified variane then adjust delay. - if (Math.abs(diff) > _timeVariance) - { - - // if the the _delay is larger than the required duration to send report - // speed up - if (diff > TEN_MILLI_SEC) - { - _delay -= TEN_MILLI_SEC; - - if (_delay < 0) - { - _delay = 0; - log.info("Reset _delay to 0"); - delayStable(); - } - else - { - delayChanged(); - } - - } - else if (diff < 0) // diff < 0 diff cannot be 0 as it is > _timeVariance - { - // the report took longer - _delay += TEN_MILLI_SEC; - delayChanged(); - } - } - else - { - delayStable(); - } - - // If we have a consumer that is behind with the batches. - if ((batchesSent - batchNumber) > _batchVariance) - { - log.debug("Increasing _delay as sending more than receiving"); - - _delay += 2 * TEN_MILLI_SEC; - delayChanged(); - } - - } - - /** Reset the number of iterations before we say the delay has stabilised. */ - private void delayChanged() - { - _delayShifting = REPORTS_WITHOUT_CHANGE; - } - - /** - * Record the fact that delay has stabilised If delay has stablised for REPORTS_WITHOUT_CHANGE then it will - * output Delay stabilised - */ - private void delayStable() - { - _delayShifting--; - - if (_delayShifting < 0) - { - _delayShifting = 0; - console.debug("Delay stabilised:" + _delay); - } - } - - /** - * Checks that the client has received enough messages. If the client has fallen behind then they are put in the - * _slowClients lists which will increase the delay. - * - * @param client The client identifier to check - * @param received the number of messages received by that client - * @param batchNumber - */ - private void recordSlow(String client, long received, int batchNumber) - { - if (Math.abs(batchNumber - (_sent / _batchSize)) > _batchVariance) - { - _slowClients.put(client, received); - } - else - { - _slowClients.remove(client); - } - } - - /** Incrment the number of sent messages and then sleep, if required. */ - public void sentMessage() - { - - _sent++; - - if (_delay > (TEN_MILLI_SEC * _batchSize)) - { - long batchDelay = _delay / _batchSize; - // less than 10ms sleep doesn't always work. - // _delay is in nano seconds - // if (batchDelay < (TEN_MILLI_SEC)) - // { - // sleep(0, (int) batchDelay); - // } - // else - { - // if (batchDelay < 30000000000L) - { - sleepLong(batchDelay); - } - } - } - else - { - if (SLEEP_PER_MESSAGE && (_delay > 0)) - { - sleepLong(_delay / _batchSize); - } - } - } - - /** - * Check at the end of each batch and pause sending messages to allow slow clients to catch up. - * - * @return true if there were slow clients that caught up. - */ - private boolean checkForSlowClients() - { - // This will allways be true as we are running this at the end of each batchSize - // if (_sent % _batchSize == 0) - { - // Cause test to pause when we have slow - if (!_slowClients.isEmpty() || NO_CLIENTS) - { - - while (!_slowClients.isEmpty()) - { - if (log.isInfoEnabled() && ((_sent / _batchSize % DEBUG_LOG_UPATE_INTERVAL) == 0)) - { - String clients = ""; - Iterator it = _slowClients.keySet().iterator(); - while (it.hasNext()) - { - clients += it.next(); - if (it.hasNext()) - { - clients += ", "; - } - } - - log.info("Pausing for slow clients:" + clients); - } - - if (console.isDebugEnabled() && ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0)) - { - console.debug(_slowClients.size() + " slow clients."); - } - - sleep(PAUSE_SLEEP); - } - - if (NO_CLIENTS) - { - sleep(NO_CLIENT_SLEEP); - } - - log.debug("Continuing"); - - return true; - } - else - { - if ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0) - { - console.info("Total Delay :" + _delay + " " - + ((_delayShifting == 0) ? "Stablised" : ("Not Stablised(" + _delayShifting + ")"))); - } - } - - } - - return false; - } - - /** - * Sleep normally takes micro-seconds this allows the use of a nano-second value. - * - * @param delay nanoseconds to sleep for. - */ - private void sleepLong(long delay) - { - sleep(delay / 1000000, (int) (delay % 1000000)); - } - - /** - * Sleep for the specified micro-seconds. - * @param sleep microseconds to sleep for. - */ - private void sleep(long sleep) - { - sleep(sleep, 0); - } - - /** - * Perform the sleep , swallowing any InteruptException. - * - * NOTE: If a sleep request is > 10s then reset only sleep for 5s - * - * @param milli to sleep for - * @param nano sub miliseconds to sleep for - */ - private void sleep(long milli, int nano) - { - try - { - log.debug("Sleep:" + milli + ":" + nano); - if (milli > 10000) - { - - if (_delay == milli) - { - _totalDuration = _totalReceived / _batchSize * EXPECTED_TIME_PER_BATCH; - log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) - + "s. Reset _totalDuration:" + _totalDuration); - } - else - { - log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + "s"); - } - - milli = 5000; - } - - Thread.sleep(milli, nano); - } - catch (InterruptedException e) - { - // - } - } - - public void setClient(SustainedClientTestCase client) - { - _client = client; - } - } - -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java deleted file mode 100644 index 36f9b4eaf1..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * - * 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.sustained; - -import org.apache.log4j.Logger; - -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.test.framework.distributedtesting.DistributedTestCase; -import org.apache.qpid.test.framework.DropInTest; - -import javax.jms.JMSException; -import javax.jms.Message; - -import java.util.Properties; - -/** - * SustainedTestCase is a {@link org.apache.qpid.test.framework.distributedtesting.DistributedTestCase} that runs the "Perf_SustainedPubSub" test case. This consists of one - * test client sending, and several receiving, and attempts to find the highest rate at which messages can be broadcast - * to the receivers. It is also a {@link DropInTest} to which more test clients may be added during a test run. - * - *

- *
CRC Card
Responsibilities Collaborations - *
- *
- */ -public class SustainedTestCase extends DistributedTestCase implements DropInTest -{ - /** Used for debugging. */ - Logger log = Logger.getLogger(SustainedTestCase.class); - - /** Holds the root name of the topic on which to send the test messages. */ - private static final String SUSTAINED_KEY = "Perf_SustainedPubSub"; - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public SustainedTestCase(String name) - { - super(name); - } - - /** - * Performs a single test run of the sustained test. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testBasicPubSub() throws Exception - { - log.debug("public void testSinglePubSubCycle(): called"); - - Properties testConfig = new Properties(); - testConfig.put("TEST_NAME", "Perf_SustainedPubSub"); - testConfig.put("SUSTAINED_KEY", SUSTAINED_KEY); - testConfig.put("SUSTAINED_NUM_RECEIVERS", Integer.getInteger("numReceives", 2)); - testConfig.put("SUSTAINED_UPDATE_INTERVAL", Integer.getInteger("batchSize", 1000)); - testConfig.put("SUSTAINED_UPDATE_KEY", SUSTAINED_KEY + ".UPDATE"); - testConfig.put("ACKNOWLEDGE_MODE", Integer.getInteger("ackMode", AMQSession.AUTO_ACKNOWLEDGE)); - - log.info("Created Config: " + testConfig.entrySet().toArray()); - - getTestSequencer().sequenceTest(null, null, testConfig); - } - - /** - * Accepts a late joining client into this test case. The client will be enlisted with a control message - * with the 'CONTROL_TYPE' field set to the value 'LATEJOIN'. It should also provide values for the fields: - * - *

- *
CLIENT_NAME A unique name for the new client. - *
CLIENT_PRIVATE_CONTROL_KEY The key for the route on which the client receives its control messages. - *
- * - * @param message The late joiners join message. - * - * @throws JMSException Any JMS Exception are allowed to fall through, indicating that the join failed. - */ - public void lateJoin(Message message) throws JMSException - { - throw new RuntimeException("Not implemented."); - /* - // Extract the joining clients details from its join request message. - TestClientDetails clientDetails = new TestClientDetails(); - clientDetails.clientName = message.getStringProperty("CLIENT_NAME"); - clientDetails.privateControlKey = message.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); - - // Register the joining client, but do block for confirmation as cannot do a synchronous receivers during this - // method call, as it may have been called from an 'onMessage' method. - assignReceiverRole(clientDetails, new Properties(), false); - */ - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as known to the test - * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test - * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case - * name "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "Perf_SustainedPubSub"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestClient.java index 79707bafa5..6f2089290a 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestClient.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestClient.java @@ -425,7 +425,7 @@ public class SustainedTestClient extends TestCase3BasicPubSub implements Excepti class SustainedRateAdapter implements MessageListener, Runnable { private SustainedTestClient _client; - private long _batchVariance = Integer.getInteger("batchVariance", 3); //no. batches to allow drifting + private long _batchVariance = 3; //no. batches to allow drifting private long _timeVariance = TEN_MILLI_SEC * 5; // no. nanos between send and report delay (10ms) private volatile long _delay; //in nanos private long _sent; @@ -434,11 +434,9 @@ public class SustainedTestClient extends TestCase3BasicPubSub implements Excepti private static final long NO_CLIENT_SLEEP = 1000; // 1s private volatile boolean NO_CLIENTS = true; private int _delayShifting; - private final int REPORTS_WITHOUT_CHANGE = Integer.getInteger("stableReportCount", 5); + private static final int REPORTS_WITHOUT_CHANGE = 5; private boolean _warmedup = false; private static final long EXPECTED_TIME_PER_BATCH = 100000L; - private int _warmUpBatches = Integer.getInteger("warmUpBatches", 10); - SustainedRateAdapter(SustainedTestClient client) { @@ -495,6 +493,8 @@ public class SustainedTestClient extends TestCase3BasicPubSub implements Excepti CountDownLatch _warmup = new CountDownLatch(1); + int _warmUpBatches = Integer.getInteger("warmUpBatches", 10); + int _numBatches = 10000; // long[] _timings = new long[_numBatches]; diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCoordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCoordinator.java new file mode 100644 index 0000000000..0075e45a8c --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCoordinator.java @@ -0,0 +1,222 @@ +/* + * 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.sustained; + +import org.apache.log4j.Logger; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.interop.coordinator.ListeningCoordinatorTest; +import org.apache.qpid.interop.coordinator.TestClientDetails; +import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase3BasicPubSub; +import org.apache.qpid.util.ConversationFactory; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class SustainedTestCoordinator extends CoordinatingTestCase3BasicPubSub implements ListeningCoordinatorTest +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(SustainedTestCoordinator.class); + private List _receivers; + private static final String SUSTAINED_KEY = "Perf_SustainedPubSub"; + Map _testProperties; + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public SustainedTestCoordinator(String name) + { + super(name); + _receivers = new LinkedList(); + } + + /** + * Adds a receiver to this test. + * + * @param receiver The contact details of the sending client in the test. + */ + public void setReceiver(TestClientDetails receiver) + { + _receivers.add(receiver); + } + + + /** + * Performs the a single test run + * + * @throws Exception if there was a problem running the test. + */ + public void testBasicPubSub() throws Exception + { + log.debug("public void testSinglePubSubCycle(): called"); + + Map testConfig = new HashMap(); + testConfig.put("TEST_NAME", "Perf_SustainedPubSub"); + testConfig.put("SUSTAINED_KEY", SUSTAINED_KEY); + testConfig.put("SUSTAINED_NUM_RECEIVERS", Integer.getInteger("numReceives", 2)); + testConfig.put("SUSTAINED_UPDATE_INTERVAL", Integer.getInteger("batchSize", 1000)); + testConfig.put("SUSTAINED_UPDATE_KEY", SUSTAINED_KEY + ".UPDATE"); + testConfig.put("ACKNOWLEDGE_MODE", Integer.getInteger("ackMode", AMQSession.AUTO_ACKNOWLEDGE)); + + log.info("Created Config: " + testConfig.entrySet().toArray()); + + sequenceTest(testConfig); + } + + /** + * 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 assigning the test roles, begining the test and gathering the test reports from the + * participants. + * + * @param testProperties The test case definition. + * + * @return The test results from the senders and receivers. + * + * @throws javax.jms.JMSException All underlying JMSExceptions are allowed to fall through. + */ + protected Message[] sequenceTest(Map testProperties) throws JMSException + { + log.debug("protected Message[] sequenceTest(Object... testProperties = " + testProperties + "): called"); + + Session session = conversationFactory.getSession(); + Destination senderControlTopic = session.createTopic(sender.privateControlKey); + + ConversationFactory.Conversation senderConversation = conversationFactory.startConversation(); + + // Assign the sender role to the sending test client. + Message assignSender = conversationFactory.getSession().createMessage(); + setPropertiesOnMessage(assignSender, testProperties); + assignSender.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); + assignSender.setStringProperty("ROLE", "SENDER"); + assignSender.setStringProperty("CLIENT_NAME", "Sustained_SENDER"); + + senderConversation.send(senderControlTopic, assignSender); + + //Assign and wait for the receiver ckuebts to be ready. + _testProperties = testProperties; + + // Wait for the senders to confirm their roles. + senderConversation.receive(); + + assignReceivers(); + + // Start the test. + Message start = session.createMessage(); + start.setStringProperty("CONTROL_TYPE", "START"); + + senderConversation.send(senderControlTopic, start); + + // Wait for the test sender to return its report. + Message senderReport = senderConversation.receive(); + + try + { + Thread.sleep(500); + } + catch (InterruptedException e) + { + } + + // Ask the receiver for its report. + Message statusRequest = session.createMessage(); + statusRequest.setStringProperty("CONTROL_TYPE", "STATUS_REQUEST"); + + + return new Message[]{senderReport}; + } + + private void assignReceivers() + { + for (TestClientDetails receiver : _receivers) + { + registerReceiver(receiver); + } + } + + private void registerReceiver(TestClientDetails receiver) + { + log.info("registerReceiver called for receiver:" + receiver); + try + { + Session session = conversationFactory.getSession(); + Destination receiverControlTopic = session.createTopic(receiver.privateControlKey); + ConversationFactory.Conversation receiverConversation = conversationFactory.startConversation(); + // Assign the receiver role the receiving client. + Message assignReceiver = session.createMessage(); + setPropertiesOnMessage(assignReceiver, _testProperties); + assignReceiver.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); + assignReceiver.setStringProperty("ROLE", "RECEIVER"); + assignReceiver.setStringProperty("CLIENT_NAME", "Sustained_RECEIVER_" + receiver.clientName); + + receiverConversation.send(receiverControlTopic, assignReceiver); + + //Don't wait for receiver to be ready.... we can't this is being done in + // the dispatcher thread, and most likely the acceptance message we + // want is sitting in the Dispatcher._queue waiting its turn for being + // dispatched so if we block here we won't can't get the message. + // So assume consumer is ready for action. + //receiverConversation.receive(); + } + catch (JMSException e) + { + log.warn("Unable to assign receiver:" + receiver + ". Due to:" + e.getMessage()); + } + } + + public void latejoin(Message message) + { + try + { + + TestClientDetails clientDetails = new TestClientDetails(); + clientDetails.clientName = message.getStringProperty("CLIENT_NAME"); + clientDetails.privateControlKey = message.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); + + + registerReceiver(clientDetails); + } + catch (JMSException e) + { + //swallow + } + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the interop + * testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "Perf_SustainedPubSub"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestClient.java new file mode 100644 index 0000000000..44fc090410 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestClient.java @@ -0,0 +1,157 @@ +/* + * 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.sustained; + +import org.apache.log4j.Logger; +import org.apache.qpid.interop.testclient.InteropClientTestCase; +import org.apache.qpid.util.CommandLineParser; + +import javax.jms.JMSException; +import javax.jms.Message; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Properties; + +public class TestClient extends org.apache.qpid.interop.testclient.TestClient +{ + private static Logger log = Logger.getLogger(TestClient.class); + + /** + * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client + * identifying name. + * + * @param brokerUrl The url of the broker to connect to. + * @param virtualHost The virtual host to conect to. + * @param clientName The client name to use. + */ + public TestClient(String brokerUrl, String virtualHost, String clientName) + { + super(brokerUrl, virtualHost, clientName); + } + + /** + * The entry point for the interop test coordinator. This client accepts the following command line arguments: + * + *

-b The broker URL. Optional.
-h The virtual + * host. Optional.
-n The test client name. Optional.
name=value + * Trailing argument define name/value pairs. Added to system properties. Optional.
+ * + * @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", "false"}, + {"h", "The virtual host to use.", "virtual host", "false"}, + {"n", "The test client name.", "name", "false"}, + {"j", "Join this test client to running test.", "join", ""} + }); + + // 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"); + String clientName = options.getProperty("n"); + String join = options.getProperty("j"); + + // 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, (clientName == null) ? CLIENT_NAME : clientName); + + // Use a class path scanner to find all the interop test case implementations. + Collection> testCaseClasses = + new ArrayList>(); + // ClasspathScanner.getMatches(InteropClientTestCase.class, "^TestCase.*", true); + // Hard code the test classes till the classpath scanner is fixed. + Collections.addAll(testCaseClasses, + SustainedTestClient.class); + + + try + { + client.start(testCaseClasses, join); + } + catch (Exception e) + { + log.error("The test client was unable to start.", e); + System.exit(1); + } + } + + protected void start(Collection> testCaseClasses, String join) throws JMSException, ClassNotFoundException + { + super.start(testCaseClasses); + log.debug("private void start(): called"); + + if (join != null && !join.equals("")) + { + Message latejoin = session.createMessage(); + + try + { + Object test = Class.forName(join).newInstance(); + if (test instanceof InteropClientTestCase) + { + currentTestCase = (InteropClientTestCase) test; + } + else + { + throw new RuntimeException("Requested to join class '" + join + "' but this is not a InteropClientTestCase."); + } + + latejoin.setStringProperty("CONTROL_TYPE", "LATEJOIN"); + latejoin.setStringProperty("CLIENT_NAME", clientName); + latejoin.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); + producer.send(session.createTopic("iop.control.test." + currentTestCase.getName()), latejoin); + } + catch (InstantiationException e) + { + log.warn("Unable to request latejoining of test:" + currentTestCase); + } + catch (IllegalAccessException e) + { + log.warn("Unable to request latejoining of test:" + currentTestCase); + } + } + } + +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestCoordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestCoordinator.java new file mode 100644 index 0000000000..7e12fe39fb --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestCoordinator.java @@ -0,0 +1,117 @@ +/* + * 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.sustained; + +import org.apache.qpid.interop.coordinator.Coordinator; +import org.apache.qpid.interop.coordinator.ListeningTestDecorator; +import org.apache.qpid.interop.coordinator.TestClientDetails; +import org.apache.qpid.util.CommandLineParser; +import org.apache.qpid.util.ConversationFactory; +import org.apache.log4j.Logger; + +import java.util.Properties; +import java.util.Set; + +import junit.framework.TestResult; +import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; + +import javax.jms.Connection; + +public class TestCoordinator extends Coordinator +{ + + private static final Logger log = Logger.getLogger(TestCoordinator.class); + + /** + * 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 null. + */ + TestCoordinator(String brokerUrl, String virtualHost) + { + super(brokerUrl, virtualHost); + } + + protected WrappedSuiteTestDecorator newTestDecorator(WrappedSuiteTestDecorator targetTest, Set enlistedClients, ConversationFactory conversationFactory, Connection connection) + { + return new ListeningTestDecorator(targetTest, enlistedClients, conversationFactory, connection); + } + + + /** + * The entry point for the interop test coordinator. This client accepts the following command line arguments: + * + *

-b The broker URL. Mandatory.
-h The virtual host. + * Optional.
name=value Trailing argument define name/value pairs. Added to system properties. + * Optional.
+ * + * @param args The command line arguments. + */ + public static void main(String[] args) + { + try + { + // Use the command line parser to evaluate the command line with standard handling behaviour (print errors + // and usage then exist if there are errors). + Properties options = + CommandLineParser.processCommandLine(args, + new CommandLineParser( + new String[][] + { + {"b", "The broker URL.", "broker", "false"}, + {"h", "The virtual host to use.", "virtual host", "false"}, + {"o", "The name of the directory to output test timings to.", "dir", "false"} + })); + + // Extract the command line options. + String brokerUrl = options.getProperty("b"); + String virtualHost = options.getProperty("h"); + String reportDir = options.getProperty("o"); + reportDir = (reportDir == null) ? "." : reportDir; + + + String[] testClassNames = {SustainedTestCoordinator.class.getName()}; + + // Create a coordinator and begin its test procedure. + Coordinator coordinator = new TestCoordinator(brokerUrl, virtualHost); + + coordinator.setReportDir(reportDir); + + TestResult testResult = coordinator.start(testClassNames); + + if (testResult.failureCount() > 0) + { + System.exit(FAILURE_EXIT); + } + else + { + System.exit(SUCCESS_EXIT); + } + } + catch (Exception e) + { + System.err.println(e.getMessage()); + log.error("Top level handler caught execption.", e); + System.exit(EXCEPTION_EXIT); + } + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropClientTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropClientTestCase.java deleted file mode 100644 index 5e6d61a9e0..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropClientTestCase.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * - * 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.test.framework.distributedtesting; - -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. - * - *

- *
CRC Card
Responsibilities - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Perform test case actions. - *
Generate test reports. - *
- */ -public interface InteropClientTestCase extends MessageListener -{ - /** Defines the possible test case roles that an interop test case can take on. */ - public enum Roles - { - /** Specifies the sender role. */ - SENDER, - - /** Specifies the receivers role. */ - 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 true to accept the invitation, false 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 receivers. - * @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. Returning from here, indicates that the sending role has completed its test. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void start() throws JMSException; - - /** - * 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/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java deleted file mode 100644 index 12c0d0aa69..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java +++ /dev/null @@ -1,377 +0,0 @@ -/* - * - * 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.test.framework.distributedtesting; - -import org.apache.log4j.Logger; - -import org.apache.qpid.interop.clienttestcases.TestCase1DummyRun; -import org.apache.qpid.interop.clienttestcases.TestCase2BasicP2P; -import org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub; -import org.apache.qpid.sustained.SustainedClientTestCase; -import org.apache.qpid.test.framework.MessagingTestConfigProperties; -import org.apache.qpid.test.framework.TestUtils; - -import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; -import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; - -import javax.jms.*; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * 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.test.framework.distributedtesting.Coordinator}. - * - *

- *
Messages Handled by SustainedTestClient
Message Action - *
Invite(compulsory) Reply with Enlist. - *
Invite(test case) Reply with Enlist if test case available. - *
AssignRole(test case) Reply with Accept Role if matches an enlisted test. Keep test parameters. - *
Start Send test messages defined by test parameters. Send report on messages sent. - *
Status Request Send report on messages received. - *
Terminate Terminate the test client. - *
- * - *

- *
CRC Card
Responsibilities Collaborations - *
Handle all incoming control messages. {@link InteropClientTestCase} - *
Configure and look up test cases by name. {@link InteropClientTestCase} - *
- */ -public class TestClient implements MessageListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestClient.class); - - /** Used for reporting to the console. */ - private static final Logger console = Logger.getLogger("CONSOLE"); - - /** Holds the default identifying name of the test client. */ - public static final String CLIENT_NAME = "java"; - - /** Holds the URL of the broker to run the tests on. */ - public static String brokerUrl; - - /** Holds the virtual host to run the tests on. If null, then the default virtual host is used. */ - public static String virtualHost; - - /** - * Holds the test context properties that provides the default test parameters, plus command line overrides. - * This is initialized with the default test parameters, to which command line overrides may be applied. - */ - public static ParsedProperties testContextProperties = - TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); - - /** Holds all the test cases loaded from the classpath. */ - Map testCases = new HashMap(); - - /** Holds the test case currently being run by this client. */ - protected InteropClientTestCase currentTestCase; - - /** Holds the connection to the broker that the test is being coordinated on. */ - protected Connection connection; - - /** Holds the message producer to hold the test coordination over. */ - protected MessageProducer producer; - - /** Holds the JMS session for the test coordination. */ - protected Session session; - - /** Holds the name of this client, with a default value. */ - protected String clientName = CLIENT_NAME; - - /** This flag indicates that the test client should attempt to join the currently running test case on start up. */ - protected boolean join; - - /** - * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client - * identifying name. - * - * @param brokerUrl The url of the broker to connect to. - * @param virtualHost The virtual host to conect to. - * @param clientName The client name to use. - */ - public TestClient(String brokerUrl, String virtualHost, String clientName, boolean join) - { - log.debug("public SustainedTestClient(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost - + ", String clientName = " + clientName + "): called"); - - // Retain the connection parameters. - this.brokerUrl = brokerUrl; - this.virtualHost = virtualHost; - this.clientName = clientName; - this.join = join; - } - - /** - * The entry point for the interop test coordinator. This client accepts the following command line arguments: - * - *

- *
-b The broker URL. Optional. - *
-h The virtual host. Optional. - *
-n The test client name. Optional. - *
name=value Trailing argument define name/value pairs. Added to system properties. Optional. - *
- * - * @param args The command line arguments. - */ - public static void main(String[] args) - { - // Override the default broker url to be localhost:5672. - testContextProperties.setProperty(MessagingTestConfigProperties.BROKER_PROPNAME, "tcp://localhost:5672"); - - // Use the command line parser to evaluate the command line with standard handling behaviour (print errors - // and usage then exist if there are errors). - // Any options and trailing name=value pairs are also injected into the test context properties object, - // to override any defaults that may have been set up. - ParsedProperties options = - new ParsedProperties(uk.co.thebadgerset.junit.extensions.util.CommandLineParser.processCommandLine(args, - new uk.co.thebadgerset.junit.extensions.util.CommandLineParser( - new String[][] - { - { "b", "The broker URL.", "broker", "false" }, - { "h", "The virtual host to use.", "virtual host", "false" }, - { "o", "The name of the directory to output test timings to.", "dir", "false" }, - { "n", "The name of the test client.", "name", "false" }, - { "j", "Join this test client to running test.", "false" } - }), testContextProperties)); - - // Extract the command line options. - String brokerUrl = options.getProperty("b"); - String virtualHost = options.getProperty("h"); - String clientName = options.getProperty("n"); - boolean join = options.getPropertyAsBoolean("j"); - - // Create a test client and start it running. - TestClient client = new TestClient(brokerUrl, virtualHost, (clientName == null) ? CLIENT_NAME : clientName, join); - - // Use a class path scanner to find all the interop test case implementations. - // Hard code the test classes till the classpath scanner is fixed. - Collection> testCaseClasses = - new ArrayList>(); - // ClasspathScanner.getMatches(InteropClientTestCase.class, "^TestCase.*", true); - Collections.addAll(testCaseClasses, TestCase1DummyRun.class, TestCase2BasicP2P.class, TestCase3BasicPubSub.class, - SustainedClientTestCase.class); - - try - { - client.start(testCaseClasses); - } - catch (Exception e) - { - log.error("The test client was unable to start.", e); - System.exit(1); - } - } - - /** - * Starts the interop test client running. This causes it to start listening for incoming test invites. - * - * @param testCaseClasses The classes of the available test cases. The test case names from these are used to - * matchin incoming test invites against. - * - * @throws JMSException Any underlying JMSExceptions are allowed to fall through. - */ - protected void start(Collection> testCaseClasses) throws JMSException - { - log.debug("private void start(): called"); - - // Create all the test case implementations and index them by the test names. - for (Class nextClass : testCaseClasses) - { - try - { - InteropClientTestCase testCase = nextClass.newInstance(); - testCases.put(testCase.getName(), testCase); - } - catch (InstantiationException e) - { - log.warn("Could not instantiate test case class: " + nextClass.getName(), e); - // Ignored. - } - catch (IllegalAccessException e) - { - log.warn("Could not instantiate test case class due to illegal access: " + nextClass.getName(), e); - // Ignored. - } - } - - // Open a connection to communicate with the coordinator on. - connection = TestUtils.createConnection(testContextProperties); - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Set this up to listen for control messages. - Topic privateControlTopic = session.createTopic("iop.control." + clientName); - MessageConsumer consumer = session.createConsumer(privateControlTopic); - consumer.setMessageListener(this); - - Topic controlTopic = session.createTopic("iop.control"); - MessageConsumer consumer2 = session.createConsumer(controlTopic); - consumer2.setMessageListener(this); - - // Create a producer to send replies with. - producer = session.createProducer(null); - - // If the join flag was set, then broadcast a join message to notify the coordinator that a new test client - // is available to join the current test case, if it supports it. This message may be ignored, or it may result - // in this test client receiving a test invite. - if (join) - { - Message joinMessage = session.createMessage(); - - joinMessage.setStringProperty("CONTROL_TYPE", "JOIN"); - joinMessage.setStringProperty("CLIENT_NAME", clientName); - joinMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); - producer.send(controlTopic, joinMessage); - } - - // Start listening for incoming control messages. - connection.start(); - } - - /** - * Handles all incoming control messages. - * - * @param message The incoming message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - try - { - String controlType = message.getStringProperty("CONTROL_TYPE"); - String testName = message.getStringProperty("TEST_NAME"); - - log.info("onMessage(Message message = " + message + "): for '" + controlType + "' to '" + testName + "'"); - - // Check if the message is a test invite. - if ("INVITE".equals(controlType)) - { - // Flag used to indicate that an enlist should be sent. Only enlist to compulsory invites or invites - // for which test cases exist. - boolean enlist = false; - - if (testName != null) - { - log.debug("Got an invite to test: " + testName); - - // Check if the requested test case is available. - InteropClientTestCase testCase = testCases.get(testName); - - if (testCase != null) - { - // Make the requested test case the current test case. - currentTestCase = testCase; - enlist = true; - } - else - { - log.debug("Received an invite to the test '" + testName + "' but this test is not known."); - } - } - else - { - log.debug("Got a compulsory invite."); - - enlist = true; - } - - if (enlist) - { - // Reply with the client name in an Enlist message. - Message enlistMessage = session.createMessage(); - enlistMessage.setStringProperty("CONTROL_TYPE", "ENLIST"); - enlistMessage.setStringProperty("CLIENT_NAME", clientName); - enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); - enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - log.info("Sending Message '" + enlistMessage + "'. to " + message.getJMSReplyTo()); - - producer.send(message.getJMSReplyTo(), enlistMessage); - } - } - else if ("ASSIGN_ROLE".equals(controlType)) - { - // Assign the role to the current test case. - String roleName = message.getStringProperty("ROLE"); - - log.debug("Got a role assignment to role: " + roleName); - - InteropClientTestCase.Roles role = Enum.valueOf(InteropClientTestCase.Roles.class, roleName); - - currentTestCase.assignRole(role, message); - - // Reply by accepting the role in an Accept Role message. - Message acceptRoleMessage = session.createMessage(); - acceptRoleMessage.setStringProperty("CONTROL_TYPE", "ACCEPT_ROLE"); - acceptRoleMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - producer.send(message.getJMSReplyTo(), acceptRoleMessage); - } - else if ("START".equals(controlType) || "STATUS_REQUEST".equals(controlType)) - { - if ("START".equals(controlType)) - { - log.debug("Got a start notification."); - - // Start the current test case. - currentTestCase.start(); - } - else - { - log.debug("Got a status request."); - } - - // Generate the report from the test case and reply with it as a Report message. - Message reportMessage = currentTestCase.getReport(session); - reportMessage.setStringProperty("CONTROL_TYPE", "REPORT"); - reportMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - producer.send(message.getJMSReplyTo(), reportMessage); - } - else if ("TERMINATE".equals(controlType)) - { - log.info("Received termination instruction from coordinator."); - - // Is a cleaner shutdown needed? - connection.close(); - System.exit(0); - } - else - { - // Log a warning about this but otherwise ignore it. - log.warn("Got an unknown control message, controlType = " + controlType + ", message = " + message); - } - } - catch (JMSException e) - { - // Log a warning about this, but otherwise ignore it. - log.warn("A JMSException occurred whilst handling a message."); - log.debug("Got JMSException whilst handling message: " + message, e); - } - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java new file mode 100644 index 0000000000..bad49060ca --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java @@ -0,0 +1,234 @@ +/* + * + * 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.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.log4j.Logger; + +/** + * An ClasspathScanner scans the classpath for classes that implement an interface or extend a base class and have names + * that match a regular expression. + * + *

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. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Find all classes matching type and name pattern on the classpath. + *
+ * + * @todo Add logic to scan jars as well as directories. + */ +public class ClasspathScanner +{ + private static final Logger log = Logger.getLogger(ClasspathScanner.class); + + /** + * 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 regular 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> getMatches(Class matchingClass, String matchingRegexp, + boolean beanOnly) + { + log.debug("public static Collection> getMatches(Class matchingClass = " + matchingClass + + ", String matchingRegexp = " + matchingRegexp + ", boolean beanOnly = " + beanOnly + "): called"); + + // Build a compiled regular expression from the pattern to match. + Pattern matchPattern = Pattern.compile(matchingRegexp); + + String classPath = System.getProperty("java.class.path"); + Map> result = new HashMap>(); + + log.debug("classPath = " + classPath); + + // Find matching classes starting from all roots in the classpath. + for (String path : splitClassPath(classPath)) + { + gatherFiles(new File(path), "", result, matchPattern, matchingClass); + } + + return result.values(); + } + + /** + * Finds all matching classes rooted at a given location in the file system. If location is a directory it + * is recursively examined. + * + * @param classRoot The root of the current point in the file system being examined. + * @param classFileName The name of the current file or directory to examine. + * @param result The accumulated mapping from class names to classes that match the scan. + * + * @todo Recursion ok as file system depth is not likely to exhaust the stack. Might be better to replace with + * iteration. + */ + private static void gatherFiles(File classRoot, String classFileName, Map> result, + Pattern matchPattern, Class matchClass) + { + log.debug("private static void gatherFiles(File classRoot = " + classRoot + ", String classFileName = " + + classFileName + ", Map> result, Pattern matchPattern = " + matchPattern + + ", Class matchClass = " + matchClass + "): called"); + + File thisRoot = new File(classRoot, classFileName); + + // If the current location is a file, check if it is a matching class. + if (thisRoot.isFile()) + { + // Check that the file has a matching name. + if (matchesName(thisRoot.getName(), matchPattern)) + { + String className = classNameFromFile(thisRoot.getName()); + + // Check that the class has matching type. + try + { + Class candidateClass = Class.forName(className); + + Class matchedClass = matchesClass(candidateClass, matchClass); + + if (matchedClass != null) + { + result.put(className, matchedClass); + } + } + catch (ClassNotFoundException e) + { + // Ignore this. The matching class could not be loaded. + log.debug("Got ClassNotFoundException, ignoring.", e); + } + } + + return; + } + // Otherwise the current location is a directory, so examine all of its contents. + else + { + String[] contents = thisRoot.list(); + + if (contents != null) + { + for (String content : contents) + { + gatherFiles(classRoot, classFileName + File.separatorChar + content, result, matchPattern, matchClass); + } + } + } + } + + /** + * Checks if the specified class file name corresponds to a class with name matching the specified regular expression. + * + * @param classFileName The class file name. + * @param matchPattern The regular expression pattern to match. + * + * @return true if the class name matches, false otherwise. + */ + private static boolean matchesName(String classFileName, Pattern matchPattern) + { + String className = classNameFromFile(classFileName); + Matcher matcher = matchPattern.matcher(className); + + return matcher.matches(); + } + + /** + * Checks if the specified class to compare extends the base class being scanned for. + * + * @param matchingClass The base class to match against. + * @param toMatch The class to match against the base class. + * + * @return The class to check, cast as an instance of the class to match if the class extends the base class, or + * null otherwise. + */ + private static Class matchesClass(Class matchingClass, Class toMatch) + { + try + { + return matchingClass.asSubclass(toMatch); + } + catch (ClassCastException e) + { + return null; + } + } + + /** + * 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 splitClassPath(String classPath) + { + List result = new LinkedList(); + String separator = System.getProperty("path.separator"); + StringTokenizer tokenizer = new StringTokenizer(classPath, separator); + + while (tokenizer.hasMoreTokens()) + { + result.add(tokenizer.nextToken()); + } + + return result; + } + + /** + * Translates from the filename of a class to its fully qualified classname. Files are named using forward slash + * seperators and end in ".class", whereas fully qualified class names use "." sperators and no ".class" ending. + * + * @param classFileName The filename of the class to translate to a class name. + * + * @return The fully qualified class name. + */ + private static String classNameFromFile(String classFileName) + { + log.debug("private static String classNameFromFile(String classFileName = " + classFileName + "): called"); + + // Remove the .class ending. + String s = classFileName.substring(0, classFileName.length() - ".class".length()); + + // Turn / seperators in . seperators. + String s2 = s.replace(File.separatorChar, '.'); + + // Knock off any leading . caused by a leading /. + if (s2.startsWith(".")) + { + return s2.substring(1); + } + + return s2; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationFactory.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationFactory.java new file mode 100644 index 0000000000..0090bec3d0 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationFactory.java @@ -0,0 +1,479 @@ +/* + * + * 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 org.apache.log4j.Logger; + +import javax.jms.*; + +import java.util.*; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +/** + * 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. + * + *

One use of this is 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 conversation + * 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): + * + *

+ * class Initiator
+ * {
+ * ConversationHelper conversation = new ConversationHelper(connection, null,
+ *                                                          java.util.concurrent.LinkedBlockingQueue.class);
+ *
+ * initiateConversation()
+ * {
+ *  try {
+ *   // Exchange greetings.
+ *   conversation.send(sendDestination, conversation.getSession().createTextMessage("Hello."));
+ *   Message greeting = conversation.receive();
+ *
+ *   // Exchange goodbyes.
+ *   conversation.send(conversation.getSession().createTextMessage("Goodbye."));
+ *   Message goodbye = conversation.receive();
+ *  } finally {
+ *   conversation.end();
+ *  }
+ * }
+ * }
+ *
+ * class Responder
+ * {
+ * ConversationHelper conversation = new ConversationHelper(connection, receiveDestination,
+ *                                                          java.util.concurrent.LinkedBlockingQueue.class);
+ *
+ * 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();
+ *  }
+ * }
+ * }
+ * 
+ * + *

Conversation correlation id's are generated on a per thread basis. + * + *

The same session is shared amongst all conversations. Calls to send are therefore synchronized because JMS + * sessions are not multi-threaded. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Associate messages to an ongoing conversation using correlation ids. + *
Auto manage sessions for conversations. + *
Store messages not in a conversation in dead letter box. + *
+ */ +public class ConversationFactory +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(ConversationFactory.class); + + /** Holds a map from correlation id's to queues. */ + private Map> idsToQueues = new HashMap>(); + + /** Holds the connection over which the conversation is conducted. */ + private Connection connection; + + /** Holds the session over which the conversation is conduxted. */ + private Session session; + + /** The message consumer for incoming messages. */ + MessageConsumer consumer; + + /** The message producer for outgoing messages. */ + MessageProducer producer; + + /** The well-known or temporary destination to receive replies on. */ + Destination receiveDestination; + + /** Holds the queue implementation class for the reply queue. */ + Class queueClass; + + /** Used to hold any replies that are received outside of the context of a conversation. */ + BlockingQueue deadLetterBox = new LinkedBlockingQueue(); + + /* Used to hold conversation state on a per thread basis. */ + /* + ThreadLocal threadLocals = + new ThreadLocal() + { + protected Conversation initialValue() + { + Conversation settings = new Conversation(); + settings.conversationId = conversationIdGenerator.getAndIncrement(); + + return settings; + } + }; + */ + + /** Generates new coversation id's as needed. */ + AtomicLong conversationIdGenerator = new AtomicLong(); + + /** + * 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 receiveDestination The destination to listen to for incoming messages. This may be null to use a temporary + * queue. + * @param queueClass The queue implementation class. + * + * @throws JMSException All underlying JMSExceptions are allowed to fall through. + */ + public ConversationFactory(Connection connection, Destination receiveDestination, + Class queueClass) throws JMSException + { + log.debug("public ConversationFactory(Connection connection, Destination receiveDestination = " + receiveDestination + + ", Class queueClass = " + queueClass + "): called"); + + this.connection = connection; + this.queueClass = queueClass; + + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Check if a well-known receive destination has been provided, or use a temporary queue if not. + this.receiveDestination = (receiveDestination != null) ? receiveDestination : session.createTemporaryQueue(); + + consumer = session.createConsumer(receiveDestination); + producer = session.createProducer(null); + + consumer.setMessageListener(new Receiver()); + } + + /** + * Creates a new conversation context. + * + * @return A new conversation context. + */ + public Conversation startConversation() + { + log.debug("public Conversation startConversation(): called"); + + Conversation conversation = new Conversation(); + conversation.conversationId = conversationIdGenerator.getAndIncrement(); + + return conversation; + } + + /** + * Ensures that the reply queue for a conversation exists. + * + * @param conversationId The conversation correlation id. + */ + private void initQueueForId(long conversationId) + { + if (!idsToQueues.containsKey(conversationId)) + { + idsToQueues.put(conversationId, ReflectionUtils.newInstance(queueClass)); + } + } + + /** + * Clears the dead letter box, returning all messages that were in it. + * + * @return All messages in the dead letter box. + */ + public Collection emptyDeadLetterBox() + { + log.debug("public Collection emptyDeadLetterBox(): called"); + + Collection result = new ArrayList(); + deadLetterBox.drainTo(result); + + return result; + } + + /** + * Gets the session over which the conversation is conducted. + * + * @return The session over which the conversation is conducted. + */ + public Session getSession() + { + // Conversation settings = threadLocals.get(); + + return session; + } + + /** + * Used to hold a conversation context. This consists of a correlating id for the conversation, and a reply + * destination automatically updated to the last received reply-to destination. + */ + public class Conversation + { + /** Holds the correlation id for the context. */ + long conversationId; + + /** + * Holds the send destination for the context. This will automatically be updated to the most recently received + * reply-to destination. + */ + Destination sendDestination; + + /** + * 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 sendDestination The destination to send to. This may be null to use the last received reply-to + * destination. + * @param message The message to send. + * + * @throws JMSException All undelying JMSExceptions are allowed to fall through. This will also be thrown if no + * send destination is specified and there is no most recent reply-to destination available + * to use. + */ + public void send(Destination sendDestination, Message message) throws JMSException + { + log.debug("public void send(Destination sendDestination = " + sendDestination + ", Message message = " + message + + "): called"); + + // Conversation settings = threadLocals.get(); + // long conversationId = conversationId; + message.setJMSCorrelationID(Long.toString(conversationId)); + message.setJMSReplyTo(receiveDestination); + + // Ensure that the reply queue for this conversation exists. + initQueueForId(conversationId); + + // Check if an overriding send to destination has been set or use the last reply-to if not. + Destination sendTo = null; + + if (sendDestination != null) + { + sendTo = sendDestination; + } + else if (sendDestination != null) + { + sendTo = sendDestination; + } + else + { + throw new JMSException("The send destination was specified, and no most recent reply-to available to use."); + } + + // Send the message. + synchronized (this) + { + producer.send(sendTo, 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. + * + * @throws JMSException All undelying JMSExceptions are allowed to fall through. Thrown if the received message + * did not have its reply-to destination set up. + */ + public Message receive() throws JMSException + { + log.debug("public Message receive(): called"); + + // Conversation settings = threadLocals.get(); + // long conversationId = settings.conversationId; + + // Ensure that the reply queue for this conversation exists. + initQueueForId(conversationId); + + BlockingQueue queue = idsToQueues.get(conversationId); + + try + { + Message result = queue.take(); + + // Keep the reply-to destination to send replies to. + sendDestination = result.getJMSReplyTo(); + + return result; + } + catch (InterruptedException e) + { + return null; + } + } + + /** + * Gets many messages in an ongoing conversation. If a limit is specified, then once that many messages are + * received they will be returned. If a timeout is specified, then all messages up to the limit, received within + * that timespan will be returned. At least one of the message count or timeout should be set to a value of + * 1 or greater. + * + * @param num The number of messages to receive, or all if this is less than 1. + * @param timeout The timeout in milliseconds to receive the messages in, or forever if this is less than 1. + * + * @return All messages received within the count limit and the timeout. + * + * @throws JMSException All undelying JMSExceptions are allowed to fall through. + */ + public Collection receiveAll(int num, long timeout) throws JMSException + { + log.debug("public Collection receiveAll(int num = " + num + ", long timeout = " + timeout + + "): called"); + + // Check that a timeout or message count was set. + if ((num < 1) && (timeout < 1)) + { + throw new IllegalArgumentException("At least one of message count (num) or timeout must be set."); + } + + // Ensure that the reply queue for this conversation exists. + initQueueForId(conversationId); + BlockingQueue queue = idsToQueues.get(conversationId); + + // Used to collect the received messages in. + Collection result = new ArrayList(); + + // Used to indicate when the timeout or message count has expired. + boolean receiveMore = true; + + int messageCount = 0; + + // Receive messages until the timeout or message count expires. + do + { + try + { + Message next = null; + + // Try to receive the message with a timeout if one has been set. + if (timeout > 0) + { + next = queue.poll(timeout, TimeUnit.MILLISECONDS); + + // Check if the timeout expired, and stop receiving if so. + if (next == null) + { + receiveMore = false; + } + } + // Receive the message without a timeout. + else + { + next = queue.take(); + } + + // Increment the message count if a message was received. + messageCount += (next != null) ? 1 : 0; + + // Check if all the requested messages were received, and stop receiving if so. + if ((num > 0) && (messageCount >= num)) + { + receiveMore = false; + } + + // Keep the reply-to destination to send replies to. + sendDestination = (next != null) ? next.getJMSReplyTo() : sendDestination; + + if (next != null) + { + result.add(next); + } + } + catch (InterruptedException e) + { + // Restore the threads interrupted status. + Thread.currentThread().interrupt(); + + // Stop receiving but return the messages received so far. + receiveMore = false; + } + } + while (receiveMore); + + return result; + } + + /** + * Completes the conversation. 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() + { + log.debug("public void end(): called"); + + // Ensure that the thread local for the current thread is cleaned up. + // Conversation settings = threadLocals.get(); + // long conversationId = settings.conversationId; + // threadLocals.remove(); + + // Ensure that its queue is removed from the queue map. + BlockingQueue queue = idsToQueues.remove(conversationId); + + // Move any outstanding messages on the threads conversation id into the dead letter box. + queue.drainTo(deadLetterBox); + } + } + + /** + * 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) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + try + { + Long conversationId = Long.parseLong(message.getJMSCorrelationID()); + + // Find the converstaion queue to place the message on. If there is no conversation for the message id, + // the the dead letter box queue is used. + BlockingQueue queue = idsToQueues.get(conversationId); + queue = (queue == null) ? deadLetterBox : queue; + + queue.put(message); + } + catch (JMSException e) + { + throw new RuntimeException(e); + } + catch (InterruptedException e) + { + throw new RuntimeException(e); + } + } + } +} diff --git a/qpid/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties b/qpid/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties new file mode 100644 index 0000000000..a5fb611dfa --- /dev/null +++ b/qpid/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties @@ -0,0 +1,20 @@ +# +# 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. +# +java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory +connectionfactory.broker = amqp://guest:guest@clientid/?brokerlist='tcp://localhost:5672' -- cgit v1.2.1 From 8eab2c30ae3cc0e523954d317517aab264923286 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 3 Aug 2007 04:00:41 +0000 Subject: fixed build failures caused by sloppy checkins and SNAPSHOT dependencies, changed the junit-toolkit version from 0.6-SNAPSHOT to 0.6-20070718.144514-11 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@562323 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/pom.xml b/qpid/java/integrationtests/pom.xml index 9ccd153f54..4733a5aac5 100644 --- a/qpid/java/integrationtests/pom.xml +++ b/qpid/java/integrationtests/pom.xml @@ -46,16 +46,15 @@ qpid-client - - org.slf4j - slf4j-log4j12 + + org.slf4j + slf4j-log4j12 1.4.0 uk.co.thebadgerset junit-toolkit - 0.6-SNAPSHOT compile -- cgit v1.2.1 From 94b0978c09b14308c25b7c5b02792c192b0c5521 Mon Sep 17 00:00:00 2001 From: Martin Ritchie Date: Thu, 30 Aug 2007 12:19:31 +0000 Subject: Remerge of M2. All tests pass locally Testing done in Intelij and mvn command line via windows/cygwin. Python tests removed from auto build pending Jython-siztion. Tested running broker in intelij and python run-tests from cygwin. All tests pass. (CombinedTest still exhibts a race condition. but that has always been so.) Additional Race condition identified (around MsgReject/AutoDeleteQueues) during testing patch to follow. systests are inconsistent Some use TestableMemoryMessageStore some use MemoryMessgaeStore. Lets not roll back this change if issues are discovered. Lets work together to go forward and address any issues. I have spent a lot of time ensuring the tests work for me so I hope that they work for you. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@571129 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/pom.xml | 11 +- .../interop/clienttestcases/TestCase1DummyRun.java | 135 +++ .../interop/clienttestcases/TestCase2BasicP2P.java | 209 +++++ .../clienttestcases/TestCase3BasicPubSub.java | 239 ++++++ .../interop/coordinator/CoordinatingTestCase.java | 263 ------ .../qpid/interop/coordinator/Coordinator.java | 388 --------- .../interop/coordinator/InvitingTestDecorator.java | 220 ----- .../coordinator/ListeningCoordinatorTest.java | 28 - .../coordinator/ListeningTestDecorator.java | 200 ----- .../qpid/interop/coordinator/OptOutTestCase.java | 65 -- .../interop/coordinator/TestClientDetails.java | 87 -- .../qpid/interop/coordinator/XMLTestListener.java | 402 --------- .../testcases/CoordinatingTestCase1DummyRun.java | 85 -- .../testcases/CoordinatingTestCase2BasicP2P.java | 90 -- .../CoordinatingTestCase3BasicPubSub.java | 92 -- .../java/org/apache/qpid/interop/old/Listener.java | 291 ------- .../org/apache/qpid/interop/old/Publisher.java | 244 ------ .../testcases/InteropTestCase1DummyRun.java | 84 ++ .../testcases/InteropTestCase2BasicP2P.java | 90 ++ .../testcases/InteropTestCase3BasicPubSub.java | 88 ++ .../interop/testclient/InteropClientTestCase.java | 104 --- .../apache/qpid/interop/testclient/TestClient.java | 422 ---------- .../testclient/testcases/TestCase1DummyRun.java | 96 --- .../testclient/testcases/TestCase2BasicP2P.java | 214 ----- .../testclient/testcases/TestCase3BasicPubSub.java | 249 ------ .../qpid/sustained/SustainedClientTestCase.java | 906 ++++++++++++++++++++ .../apache/qpid/sustained/SustainedTestCase.java | 126 +++ .../apache/qpid/sustained/SustainedTestClient.java | 931 --------------------- .../qpid/sustained/SustainedTestCoordinator.java | 222 ----- .../java/org/apache/qpid/sustained/TestClient.java | 157 ---- .../org/apache/qpid/sustained/TestCoordinator.java | 117 --- .../framework/distributedtesting/TestClient.java | 464 ++++++++++ 32 files changed, 2349 insertions(+), 4970 deletions(-) create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningCoordinatorTest.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningTestDecorator.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/XMLTestListener.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase1DummyRun.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase3BasicPubSub.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestClient.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCoordinator.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestClient.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestCoordinator.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/pom.xml b/qpid/java/integrationtests/pom.xml index 4733a5aac5..89fd5ede28 100644 --- a/qpid/java/integrationtests/pom.xml +++ b/qpid/java/integrationtests/pom.xml @@ -40,21 +40,26 @@ - org.apache.qpid qpid-client - org.slf4j - slf4j-log4j12 + org.apache.qpid + qpid-systests + + + + org.slf4j + slf4j-log4j12 1.4.0 uk.co.thebadgerset junit-toolkit + 0.6-SNAPSHOT compile diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java new file mode 100644 index 0000000000..db17c7aacc --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java @@ -0,0 +1,135 @@ +/* + * + * 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.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; + +/** + * 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. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Perform test case actions. + *
Generate test reports. + *
+ */ +public class TestCase1DummyRun implements TestClientControlledTest +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase1DummyRun.class); + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC1_DummyRun"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage): called"); + + // Test parameters don't matter, accept all invites. + 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 receivers. + * @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 + { + log.debug("public void assignRole(Roles role, Message assignRoleMessage): called"); + + // Do nothing, both roles are the same. + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + */ + public void start(int numMessages) + { + log.debug("public void start(): called"); + + // Do nothing. + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession 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 + { + log.debug("public Message getReport(Session controlSession): called"); + + // Generate a dummy report, the coordinator expects a report but doesn't care what it is. + return session.createTextMessage("Dummy Run, Ok."); + } + + /** + * Handles incoming test messages. Does nothing. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Ignore any messages. + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java new file mode 100644 index 0000000000..36d3cce7f7 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java @@ -0,0 +1,209 @@ +/* + * + * 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.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * 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. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages. + *
Generate test reports. + *
+ */ +public class TestCase2BasicP2P implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase2BasicP2P.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The connection to send the test messages on. */ + private Connection connection; + + /** The controlSession to send the test messages on. */ + private Session session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC2_BasicP2P"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // 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 receivers. + * + * @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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Create a new connection to pass the test messages on. + connection = TestUtils.createConnection(TestClient.testContextProperties); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); + Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); + + log.debug("numMessages = " + numMessages); + log.debug("sendDestination = " + sendDestination); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a message producer if so. + case SENDER: + producer = session.createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages. + case RECEIVER: + MessageConsumer consumer = session.createConsumer(sendDestination); + consumer.setMessageListener(this); + break; + } + + connection.start(); + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = session.createTextMessage("test"); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession 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 + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connection. + connection.close(); + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java new file mode 100644 index 0000000000..205472716b --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java @@ -0,0 +1,239 @@ +/* + * + * 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.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the + * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of + * messages sent/received. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages using pub/sub. + *
Generate test reports. + *
+ */ +public class TestCase3BasicPubSub implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase3BasicPubSub.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC3_BasicPubSub"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // 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 receivers. + * + * @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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); + int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); + String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); + + log.debug("numMessages = " + numMessages); + log.debug("numReceivers = " + numReceivers); + log.debug("sendKey = " + sendKey); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = TestUtils.createConnection(TestClient.testContextProperties); + session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + Destination sendDestination = session[0].createTopic(sendKey); + + producer = session[0].createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number + // of receivers connections. + case RECEIVER: + // Create the required number of receivers connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = TestUtils.createConnection(TestClient.testContextProperties); + session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); + + sendDestination = session[i].createTopic(sendKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + consumer.setMessageListener(this); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (Connection conn : connection) + { + conn.start(); + } + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = session[0].createTextMessage("test"); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession 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 + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connections. + for (Connection conn : connection) + { + conn.close(); + } + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java deleted file mode 100644 index d2042be741..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * - * 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 junit.framework.TestCase; - -import org.apache.log4j.Logger; - -import org.apache.qpid.util.ConversationFactory; - -import javax.jms.*; - -import java.util.Map; - -/** - * A 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). - * - *

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: - * - *

- * 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.");
- *   }
- * }
- *
- * 
- * - *

- *
CRC Card
Responsibilities Collaborations - *
Accept notification of test case participants. {@link InvitingTestDecorator} - *
Accpet JMS Connection to carry out the coordination over. - *
Coordinate the test sequence amongst participants. {@link ConversationFactory} - *
Supply test properties - *
- */ -public abstract class CoordinatingTestCase extends TestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(CoordinatingTestCase.class); - - /** Holds the contact details for the sending test client. */ - protected TestClientDetails sender; - - /** Holds the contact details for the receving test client. */ - protected TestClientDetails receiver; - - /** Holds the conversation factory over which to coordinate the test. */ - protected ConversationFactory conversationFactory; - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public CoordinatingTestCase(String name) - { - super(name); - } - - /** - * Sets the sender test client to coordinate the test with. - * - * @param sender The contact details of the sending client in the test. - */ - public void setSender(TestClientDetails sender) - { - log.debug("public void setSender(TestClientDetails sender = " + sender + "): called"); - - this.sender = sender; - } - - /** - * Sets the receiving test client to coordinate the test with. - * - * @param receiver The contact details of the sending client in the test. - */ - public void setReceiver(TestClientDetails receiver) - { - log.debug("public void setReceiver(TestClientDetails receiver = " + receiver + "): called"); - - this.receiver = receiver; - } - - /** - * Supplies the sending test client. - * - * @return The sending test client. - */ - public TestClientDetails getSender() - { - return sender; - } - - /** - * Supplies the receiving test client. - * - * @return The receiving test client. - */ - public TestClientDetails getReceiver() - { - return receiver; - } - - /** - * Returns the name of the current test method of this test class, with the sending and receiving client names - * appended on to it, so that the resulting name unqiuely identifies the test and the clients that participated - * in it. - * - * @return The unique test and client name. - */ - public String getName() - { - if ((sender == null) || (receiver == null)) - { - return super.getName(); - } - else - { - return super.getName() + "_sender_" + sender.clientName + "_receiver_" + receiver.clientName; - } - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * - * @return The name of the corresponding interop test case. - */ - public abstract String getTestCaseNameForTestMethod(String methodName); - - /** - * Accepts the conversation factory over which to hold the test coordinating conversation. - * - * @param conversationFactory The conversation factory to coordinate the test over. - */ - public void setConversationFactory(ConversationFactory conversationFactory) - { - this.conversationFactory = conversationFactory; - } - - /** - * 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 assigning the test roles, begining the test and gathering the test reports - * from the participants. - * - * @param testProperties The test case definition. - * - * @return The test results from the senders and receivers. - * - * @throws JMSException All underlying JMSExceptions are allowed to fall through. - */ - protected Message[] sequenceTest(Map testProperties) throws JMSException - { - log.debug("protected Message[] sequenceTest(Object... testProperties = " + testProperties + "): called"); - - Session session = conversationFactory.getSession(); - Destination senderControlTopic = session.createTopic(sender.privateControlKey); - Destination receiverControlTopic = session.createTopic(receiver.privateControlKey); - - ConversationFactory.Conversation senderConversation = conversationFactory.startConversation(); - ConversationFactory.Conversation receiverConversation = conversationFactory.startConversation(); - - // Assign the sender role to the sending test client. - Message assignSender = conversationFactory.getSession().createMessage(); - setPropertiesOnMessage(assignSender, testProperties); - assignSender.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); - assignSender.setStringProperty("ROLE", "SENDER"); - - senderConversation.send(senderControlTopic, assignSender); - - // Assign the receiver role the receiving client. - Message assignReceiver = session.createMessage(); - setPropertiesOnMessage(assignReceiver, testProperties); - assignReceiver.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); - assignReceiver.setStringProperty("ROLE", "RECEIVER"); - - receiverConversation.send(receiverControlTopic, assignReceiver); - - // Wait for the senders and receivers to confirm their roles. - senderConversation.receive(); - receiverConversation.receive(); - - // Start the test. - Message start = session.createMessage(); - start.setStringProperty("CONTROL_TYPE", "START"); - - senderConversation.send(senderControlTopic, start); - - // Wait for the test sender to return its report. - Message senderReport = senderConversation.receive(); - - try - { - Thread.sleep(500); - } - catch (InterruptedException e) - { } - - // Ask the receiver for its report. - Message statusRequest = session.createMessage(); - statusRequest.setStringProperty("CONTROL_TYPE", "STATUS_REQUEST"); - - receiverConversation.send(receiverControlTopic, statusRequest); - - // Wait for the receiver to send its report. - Message receiverReport = receiverConversation.receive(); - - return new Message[] { senderReport, receiverReport }; - } - - /** - * Sets properties of different types on a JMS Message. - * - * @param message The message to set properties on. - * @param properties The property name/value pairs to set. - * - * @throws JMSException All underlying JMSExceptions are allowed to fall through. - */ - public void setPropertiesOnMessage(Message message, Map properties) throws JMSException - { - for (Map.Entry entry : properties.entrySet()) - { - String name = entry.getKey(); - Object value = entry.getValue(); - - message.setObjectProperty(name, value); - } - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java deleted file mode 100644 index 6eec20769a..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * - * 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.io.*; -import java.util.*; -import java.util.concurrent.LinkedBlockingQueue; -import javax.jms.*; -import junit.framework.Test; -import junit.framework.TestResult; -import junit.framework.TestSuite; -import org.apache.log4j.Logger; -import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase1DummyRun; -import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase2BasicP2P; -import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase3BasicPubSub; -import org.apache.qpid.interop.testclient.TestClient; -import org.apache.qpid.util.CommandLineParser; -import org.apache.qpid.util.ConversationFactory; -import org.apache.qpid.util.PrettyPrintingUtils; -import uk.co.thebadgerset.junit.extensions.TKTestResult; -import uk.co.thebadgerset.junit.extensions.TKTestRunner; -import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; -import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; - -/** - *

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. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Find out what test clients are available. {@link ConversationFactory} - *
Decorate available tests to run all available clients. {@link InvitingTestDecorator} - *
Attach XML test result logger. - *
Terminate the interop testing framework. - *
- */ -public class Coordinator extends TKTestRunner -{ - private static final Logger log = Logger.getLogger(Coordinator.class); - - public static final String DEFAULT_CONNECTION_PROPS_RESOURCE = "org/apache/qpid/interop/connection.properties"; - - /** Holds the URL of the broker to coordinate the tests on. */ - protected String brokerUrl; - - /** Holds the virtual host to coordinate the tests on. If null, then the default virtual host is used. */ - protected String virtualHost; - - /** Holds the list of all clients that enlisted, when the compulsory invite was issued. */ - protected Set enlistedClients = new HashSet(); - - /** Holds the conversation helper for the control conversation. */ - protected ConversationFactory conversationFactory; - - /** Holds the connection that the coordinating messages are sent over. */ - protected Connection connection; - - /** - * Holds the name of the class of the test currently being run. Ideally passed into the {@link #createTestResult} - * method, but as the signature is already fixed for this, the current value gets pushed here as a member variable. - */ - protected String currentTestClassName; - - /** Holds the path of the directory to output test results too, if one is defined. */ - protected static String _reportDir; - - /** - * 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 null. - */ - public Coordinator(String brokerUrl, String virtualHost) - { - log.debug("Coordinator(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost + "): called"); - - // 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: - * - *

- *
-b The broker URL. Mandatory. - *
-h The virtual host. Optional. - *
name=value Trailing argument define name/value pairs. Added to system properties. Optional. - *
- * - * @param args The command line arguments. - */ - public static void main(String[] args) - { - try - { - // Use the command line parser to evaluate the command line with standard handling behaviour (print errors - // and usage then exit if there are errors). - Properties options = - CommandLineParser.processCommandLine(args, - new CommandLineParser( - new String[][] - { - {"b", "The broker URL.", "broker", "false"}, - {"h", "The virtual host to use.", "virtual host", "false"}, - {"o", "The name of the directory to output test timings to.", "dir", "false"} - })); - - // Extract the command line options. - String brokerUrl = options.getProperty("b"); - String virtualHost = options.getProperty("h"); - _reportDir = options.getProperty("o"); - _reportDir = (_reportDir == null) ? "." : _reportDir; - - // Scan for available test cases using a classpath scanner. - Collection> testCaseClasses = - new ArrayList>(); - // ClasspathScanner.getMatches(CoordinatingTestCase.class, "^Test.*", true); - // Hard code the test classes till the classpath scanner is fixed. - Collections.addAll(testCaseClasses, - CoordinatingTestCase1DummyRun.class, - CoordinatingTestCase2BasicP2P.class, - CoordinatingTestCase3BasicPubSub.class); - - // Check that some test classes were actually found. - if (testCaseClasses.isEmpty()) - { - throw new RuntimeException( - "No test classes implementing CoordinatingTestCase were found on the class path."); - } - - int i = 0; - String[] testClassNames = new String[testCaseClasses.size()]; - - for (Class testClass : testCaseClasses) - { - testClassNames[i++] = testClass.getName(); - } - - // Create a coordinator and begin its test procedure. - Coordinator coordinator = new Coordinator(brokerUrl, virtualHost); - - boolean failure = false; - - TestResult testResult = coordinator.start(testClassNames); - - if (failure) - { - System.exit(FAILURE_EXIT); - } - else - { - System.exit(SUCCESS_EXIT); - } - } - catch (Exception e) - { - System.err.println(e.getMessage()); - log.error("Top level handler caught execption.", e); - System.exit(EXCEPTION_EXIT); - } - } - - /** - * Starts all of the test classes to be run by this coordinator running. - * - * @param testClassNames An array of all the coordinating test case implementations. - * - * @return A JUnit TestResult to run the tests with. - * - * @throws Exception Any underlying exceptions are allowed to fall through, and fail the test process. - */ - public TestResult start(String[] testClassNames) throws Exception - { - log.debug("public TestResult start(String[] testClassNames = " + PrettyPrintingUtils.printArray(testClassNames) - + ": called"); - - // Connect to the broker. - connection = TestClient.createConnection(DEFAULT_CONNECTION_PROPS_RESOURCE, brokerUrl, virtualHost); - Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - Destination controlTopic = session.createTopic("iop.control"); - Destination responseQueue = session.createQueue("coordinator"); - - conversationFactory = new ConversationFactory(connection, responseQueue, LinkedBlockingQueue.class); - ConversationFactory.Conversation conversation = conversationFactory.startConversation(); - - connection.start(); - - // Broadcast the compulsory invitation to find out what clients are available to test. - Message invite = session.createMessage(); - invite.setStringProperty("CONTROL_TYPE", "INVITE"); - invite.setJMSReplyTo(responseQueue); - - conversation.send(controlTopic, invite); - - // Wait for a short time, to give test clients an opportunity to reply to the invitation. - Collection enlists = conversation.receiveAll(0, 3000); - - enlistedClients = extractEnlists(enlists); - - // Run the test in the suite using JUnit. - TestResult result = null; - - for (String testClassName : testClassNames) - { - // Record the current test class, so that the test results can be output to a file incorporating this name. - this.currentTestClassName = testClassName; - - result = super.start(new String[]{testClassName}); - } - - // At this point in time, all tests have completed. Broadcast the shutdown message. - Message terminate = session.createMessage(); - terminate.setStringProperty("CONTROL_TYPE", "TERMINATE"); - - conversation.send(controlTopic, terminate); - - return result; - } - - /** - * For a collection of enlist messages, this method pulls out of the client details for the enlisting clients. - * - * @param enlists The enlist messages. - * - * @return A set of enlisting clients, extracted from the enlist messages. - * - * @throws JMSException Any underlying JMSException is allowed to fall through. - */ - public static Set extractEnlists(Collection enlists) throws JMSException - { - log.debug("public static Set extractEnlists(Collection enlists = " + enlists - + "): called"); - - Set enlistedClients = new HashSet(); - - // Retain the list of all available clients. - for (Message enlist : enlists) - { - TestClientDetails clientDetails = new TestClientDetails(); - clientDetails.clientName = enlist.getStringProperty("CLIENT_NAME"); - clientDetails.privateControlKey = enlist.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); - - enlistedClients.add(clientDetails); - } - - return enlistedClients; - } - - /** - * 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 coordinators ability to invite test clients to participate in - * tests. - * - * @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) - { - log.debug("public TestResult doRun(Test \"" + test + "\", boolean " + wait + "): called"); - - // Wrap all tests in the test suite with WrappedSuiteTestDecorators. This is quite ugly and a bit baffling, - // but the reason it is done is because the JUnit implementation of TestDecorator has some bugs in it. - WrappedSuiteTestDecorator targetTest = null; - - if (test instanceof TestSuite) - { - log.debug("targetTest is a TestSuite"); - - TestSuite suite = (TestSuite) test; - - int numTests = suite.countTestCases(); - log.debug("There are " + numTests + " in the suite."); - - for (int i = 0; i < numTests; i++) - { - Test nextTest = suite.testAt(i); - log.debug("suite.testAt(" + i + ") = " + nextTest); - - if (nextTest instanceof CoordinatingTestCase) - { - log.debug("nextTest is a CoordinatingTestCase"); - } - } - - targetTest = new WrappedSuiteTestDecorator(suite); - log.debug("Wrapped with a WrappedSuiteTestDecorator."); - } - // Wrap the tests in an inviting test decorator, to perform the invite/test cycle. - - targetTest = newTestDecorator(targetTest, enlistedClients, conversationFactory, connection); - - TestSuite suite = new TestSuite(); - suite.addTest(targetTest); - - // Wrap the tests in a scaled test decorator to them them as a 'batch' in one thread. - // targetTest = new ScaledTestDecorator(targetTest, new int[] { 1 }); - - return super.doRun(suite, wait); - } - - protected WrappedSuiteTestDecorator newTestDecorator(WrappedSuiteTestDecorator targetTest, Set enlistedClients, ConversationFactory conversationFactory, Connection connection) - { - return new InvitingTestDecorator(targetTest, enlistedClients, conversationFactory, connection); - } - - /** - * Creates the TestResult object to be used for test runs. - * - * @return An instance of the test result object. - */ - protected TestResult createTestResult() - { - log.debug("protected TestResult createTestResult(): called"); - - TKTestResult result = new TKTestResult(fPrinter.getWriter(), delay, verbose, testCaseName); - - // Check if a directory to output reports to has been specified and attach test listeners if so. - if (_reportDir != null) - { - // Create the report directory if it does not already exist. - File reportDirFile = new File(_reportDir); - - if (!reportDirFile.exists()) - { - reportDirFile.mkdir(); - } - - // Create the timings file (make the name of this configurable as a command line parameter). - Writer timingsWriter = null; - - try - { - File timingsFile = new File(reportDirFile, "TEST." + currentTestClassName + ".xml"); - timingsWriter = new BufferedWriter(new FileWriter(timingsFile), 20000); - } - catch (IOException e) - { - throw new RuntimeException("Unable to create the log file to write test results to: " + e, e); - } - - // Set up a CSV results listener to output the timings to the results file. - XMLTestListener listener = new XMLTestListener(timingsWriter, currentTestClassName); - result.addListener(listener); - result.addTKTestListener(listener); - - // Register the results listeners shutdown hook to flush its data if the test framework is shutdown - // prematurely. - // registerShutdownHook(listener); - - // Record the start time of the batch. - // result.notifyStartBatch(); - - // At this point in time the test class has been instantiated, giving it an opportunity to read its parameters. - // Inform any test listers of the test properties. - result.notifyTestProperties(TestContextProperties.getAccessedProps()); - } - - return result; - } - - public void setReportDir(String reportDir) - { - _reportDir = reportDir; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java deleted file mode 100644 index 8695f7f66f..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * - * 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.*; - -import javax.jms.Connection; -import javax.jms.Destination; -import javax.jms.JMSException; -import javax.jms.Message; - -import junit.framework.Test; -import junit.framework.TestResult; - -import org.apache.log4j.Logger; - -import org.apache.qpid.util.ConversationFactory; - -import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; - -/** - *

- *
CRC Card
Responsibilities Collaborations - *
Broadcast test invitations and collect enlists. {@link ConversationFactory}. - *
Output test failures for clients unwilling to run the test case. {@link Coordinator} - *
Execute coordinated test cases. {@link CoordinatingTestCase} - *
- */ -public class InvitingTestDecorator extends WrappedSuiteTestDecorator -{ - private static final Logger log = Logger.getLogger(InvitingTestDecorator.class); - - /** Holds the contact information for all test clients that are available and that may take part in the test. */ - Set allClients; - - /** Holds the conversation helper for the control level conversation for coordinating the test through. */ - ConversationFactory conversationFactory; - - /** Holds the connection that the control conversation is held over. */ - Connection connection; - - /** Holds the underlying {@link CoordinatingTestCase}s that this decorator wraps. */ - WrappedSuiteTestDecorator testSuite; - - /** - * Creates a wrapped suite test decorator from another one. - * - * @param suite The test suite. - * @param availableClients The list of all clients that responded to the compulsory invite. - * @param controlConversation The conversation helper for the control level, test coordination conversation. - * @param controlConnection The connection that the coordination messages are sent over. - */ - public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set availableClients, - ConversationFactory controlConversation, Connection controlConnection) - { - super(suite); - - log.debug("public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set allClients = " - + availableClients + ", ConversationHelper controlConversation = " + controlConversation + "): called"); - - testSuite = suite; - allClients = availableClients; - conversationFactory = controlConversation; - connection = controlConnection; - } - - /** - * Broadcasts a test invitation and accetps enlisting from participating clients. The wrapped test case is - * then repeated for every combination of test clients (provided the wrapped test case extends - * {@link CoordinatingTestCase}. - * - *

Any JMSExceptions during the invite/enlist conversation will be allowed to fall through as runtime exceptions, - * resulting in the non-completion of the test run. - * - * @todo Better error recovery for failure of the invite/enlist conversation could be added. - * - * @param testResult The the results object to monitor the test results with. - */ - public void run(TestResult testResult) - { - log.debug("public void run(TestResult testResult): called"); - - Collection tests = testSuite.getAllUnderlyingTests(); - - for (Test test : tests) - { - CoordinatingTestCase coordTest = (CoordinatingTestCase) test; - - // Broadcast the invitation to find out what clients are available to test. - Set enlists; - try - { - Message invite = conversationFactory.getSession().createMessage(); - Destination controlTopic = conversationFactory.getSession().createTopic("iop.control"); - ConversationFactory.Conversation conversation = conversationFactory.startConversation(); - - invite.setStringProperty("CONTROL_TYPE", "INVITE"); - invite.setStringProperty("TEST_NAME", coordTest.getTestCaseNameForTestMethod(coordTest.getName())); - - conversation.send(controlTopic, invite); - - // Wait for a short time, to give test clients an opportunity to reply to the invitation. - Collection replies = conversation.receiveAll(allClients.size(), 3000); - enlists = Coordinator.extractEnlists(replies); - } - catch (JMSException e) - { - throw new RuntimeException("There was a JMSException during the invite/enlist conversation.", e); - } - - // Compare the list of willing clients to the list of all available. - Set optOuts = new HashSet(allClients); - optOuts.removeAll(enlists); - - // Output test failures for clients that will not particpate in the test. - Set> failPairs = allPairs(optOuts, allClients); - - for (List failPair : failPairs) - { - CoordinatingTestCase failTest = new OptOutTestCase("testOptOut"); - failTest.setSender(failPair.get(0)); - failTest.setReceiver(failPair.get(1)); - - failTest.run(testResult); - } - - // Loop over all combinations of clients, willing to run the test. - Set> enlistedPairs = allPairs(enlists, enlists); - - for (List enlistedPair : enlistedPairs) - { - // Set the sending and receiving client details on the test case. - coordTest.setSender(enlistedPair.get(0)); - coordTest.setReceiver(enlistedPair.get(1)); - - // Pass down the connection to hold the coordination conversation over. - coordTest.setConversationFactory(conversationFactory); - - // Execute the test case. - coordTest.run(testResult); - } - } - } - - /** - * Prints a string summarizing this test decorator, mainly for debugging purposes. - * - * @return String representation for debugging purposes. - */ - public String toString() - { - return "InvitingTestDecorator: [ testSuite = " + testSuite + " ]"; - } - - /** - * Produces all pairs of combinations of elements from two sets. The ordering of the elements in the pair is - * important, that is the pair is distinct from ; both pairs are generated. For any element, i, in - * both the left and right sets, the reflexive pair is not generated. - * - * @param left The left set. - * @param right The right set. - * - * @return All pairs formed from the permutations of all elements of the left and right sets. - */ - private Set> allPairs(Set left, Set right) - { - log.debug("private Set> allPairs(Set left = " + left + ", Set right = " + right + "): called"); - - Set> results = new HashSet>(); - - // Form all pairs from left to right. - // Form all pairs from right to left. - for (E le : left) - { - for (E re : right) - { - if (!le.equals(re)) - { - results.add(new Pair(le, re)); - results.add(new Pair(re, le)); - } - } - } - - log.debug("results = " + results); - - return results; - } - - /** - * A simple implementation of a pair, using a list. - */ - private class Pair extends ArrayList - { - public Pair(T first, T second) - { - super(); - super.add(first); - super.add(second); - } - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningCoordinatorTest.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningCoordinatorTest.java deleted file mode 100644 index 1b4461f8c2..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningCoordinatorTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 javax.jms.Message; - -public interface ListeningCoordinatorTest -{ - public void latejoin(Message message); -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningTestDecorator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningTestDecorator.java deleted file mode 100644 index 4312dfbcc6..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/ListeningTestDecorator.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * - * 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 junit.framework.Test; -import junit.framework.TestResult; -import org.apache.log4j.Logger; -import org.apache.qpid.util.ConversationFactory; -import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; - -import javax.jms.Connection; -import javax.jms.Destination; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageListener; -import java.util.Collection; -import java.util.Iterator; -import java.util.Set; - -/** - *

CRC Card
Responsibilities Collaborations
Broadcast test - * invitations and collect enlists. {@link ConversationFactory}.
Output test failures for clients - * unwilling to run the test case. {@link Coordinator}
Execute coordinated test cases. {@link - * CoordinatingTestCase}
- */ -public class ListeningTestDecorator extends WrappedSuiteTestDecorator implements MessageListener -{ - private static final Logger log = Logger.getLogger(ListeningTestDecorator.class); - - /** Holds the contact information for all test clients that are available and that may take part in the test. */ - Set allClients; - - /** Holds the conversation helper for the control level conversation for coordinating the test through. */ - ConversationFactory conversationFactory; - - /** Holds the connection that the control conversation is held over. */ - Connection connection; - - /** Holds the underlying {@link CoordinatingTestCase}s that this decorator wraps. */ - WrappedSuiteTestDecorator testSuite; - - /** Hold the current running test case. */ - CoordinatingTestCase _currentTest = null; - - /** - * Creates a wrapped suite test decorator from another one. - * - * @param suite The test suite. - * @param availableClients The list of all clients that responded to the compulsory invite. - * @param controlConversation The conversation helper for the control level, test coordination conversation. - * @param controlConnection The connection that the coordination messages are sent over. - */ - public ListeningTestDecorator(WrappedSuiteTestDecorator suite, Set availableClients, - ConversationFactory controlConversation, Connection controlConnection) - { - super(suite); - - log.debug("public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set allClients = " - + availableClients + ", ConversationHelper controlConversation = " + controlConversation + "): called"); - - testSuite = suite; - allClients = availableClients; - conversationFactory = controlConversation; - connection = controlConnection; - } - - /** - * Broadcasts a test invitation and accetps enlisting from participating clients. The wrapped test case is then - * repeated for every combination of test clients (provided the wrapped test case extends {@link - * CoordinatingTestCase}. - * - *

Any JMSExceptions during the invite/enlist conversation will be allowed to fall through as runtime - * exceptions, resulting in the non-completion of the test run. - * - * @param testResult The the results object to monitor the test results with. - * - * @todo Better error recovery for failure of the invite/enlist conversation could be added. - */ - public void run(TestResult testResult) - { - log.debug("public void run(TestResult testResult): called"); - - Collection tests = testSuite.getAllUnderlyingTests(); - - for (Test test : tests) - { - CoordinatingTestCase coordTest = (CoordinatingTestCase) test; - - Set enlists = signupClients(coordTest); - - if (enlists.size() == 0) - { - throw new RuntimeException("No clients to test with"); - } - - Iterator clients = enlists.iterator(); - coordTest.setSender(clients.next()); - - while (clients.hasNext()) - { - // Set the sending and receiving client details on the test case. - coordTest.setReceiver(clients.next()); - } - - // Pass down the connection to hold the coordination conversation over. - coordTest.setConversationFactory(conversationFactory); - - - if (coordTest instanceof ListeningCoordinatorTest) - { - _currentTest = coordTest; - } - // Execute the test case. - coordTest.run(testResult); - - _currentTest = null; - } - } - - private Set signupClients(CoordinatingTestCase coordTest) - { - // Broadcast the invitation to find out what clients are available to test. - Set enlists; - try - { - Message invite = conversationFactory.getSession().createMessage(); - Destination controlTopic = conversationFactory.getSession().createTopic("iop.control"); - ConversationFactory.Conversation conversation = conversationFactory.startConversation(); - - invite.setStringProperty("CONTROL_TYPE", "INVITE"); - invite.setStringProperty("TEST_NAME", coordTest.getTestCaseNameForTestMethod(coordTest.getName())); - - conversation.send(controlTopic, invite); - - // Wait for a short time, to give test clients an opportunity to reply to the invitation. - Collection replies = conversation.receiveAll(allClients.size(), 5000); - - log.debug("Received " + replies.size() + " enlist replies"); - - enlists = Coordinator.extractEnlists(replies); - - //Create topic to listen on for latejoiners - Destination listenTopic = conversationFactory.getSession().createTopic("iop.control.test." + coordTest.getTestCaseNameForTestMethod(coordTest.getName())); - - //Listen for joiners - conversationFactory.getSession().createConsumer(listenTopic).setMessageListener(this); - log.debug("Created consumer on :" + listenTopic); - } - catch (JMSException e) - { - throw new RuntimeException("There was a JMSException during the invite/enlist conversation.", e); - } - - return enlists; - } - - /** - * Prints a string summarizing this test decorator, mainly for debugging purposes. - * - * @return String representation for debugging purposes. - */ - public String toString() - { - return "ListeningTestDecorator: [ testSuite = " + testSuite + " ]"; - } - - - public void onMessage(Message message) - { - try - { - if (message.getStringProperty("CONTROL_TYPE").equals("LATEJOIN")) - { - ((ListeningCoordinatorTest) _currentTest).latejoin(message); - } - } - catch (JMSException e) - { - log.debug("Unable to process message:" + message); - } - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java deleted file mode 100644 index 42a382a898..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * 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 junit.framework.Assert; - -/** - * An OptOutTestCase is a test case that automatically fails. It is used when a list of test clients has been generated - * from a compulsory invite, but only some of those clients have responded to a specific test case invite. The clients - * that did not respond, are automatically given a fail for the test. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Fail the test with a suitable reason. - *
- */ -public class OptOutTestCase extends CoordinatingTestCase -{ - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public OptOutTestCase(String name) - { - super(name); - } - - /** Generates an appropriate test failure assertion. */ - public void testOptOut() - { - Assert.fail("One of " + getSender() + " and " + getReceiver() + " opted out of the test."); - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "OptOutTest"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java deleted file mode 100644 index c4a9d39cd8..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * - * 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; - -/** - *

- *
CRC Card
Responsibilities Collaborations - *
- */ -public class TestClientDetails -{ - /** The test clients name. */ - public String clientName; - - /* The test clients unique sequence number. Not currently used. */ - - /** The routing key of the test clients control topic. */ - public String privateControlKey; - - /** - * Two TestClientDetails are considered to be equal, iff they have the same client name. - * - * @param o The object to compare to. - * - * @return If the object to compare to is a TestClientDetails equal to this one, false otherwise. - */ - public boolean equals(Object o) - { - if (this == o) - { - return true; - } - - if (!(o instanceof TestClientDetails)) - { - return false; - } - - final TestClientDetails testClientDetails = (TestClientDetails) o; - - if ((clientName != null) ? (!clientName.equals(testClientDetails.clientName)) - : (testClientDetails.clientName != null)) - { - return false; - } - - return true; - } - - /** - * Computes a hash code compatible with the equals method; based on the client name alone. - * - * @return A hash code for this. - */ - public int hashCode() - { - return ((clientName != null) ? clientName.hashCode() : 0); - } - - /** - * Outputs the client name and address details. Mostly used for debugging purposes. - * - * @return The client name and address. - */ - public String toString() - { - return "TestClientDetails: [ clientName = " + clientName + ", privateControlKey = " + privateControlKey + " ]"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/XMLTestListener.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/XMLTestListener.java deleted file mode 100644 index 747ba0dd0b..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/XMLTestListener.java +++ /dev/null @@ -1,402 +0,0 @@ -/* - * - * 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.io.IOException; -import java.io.PrintWriter; -import java.io.Writer; -import java.util.*; - -import junit.framework.AssertionFailedError; -import junit.framework.Test; -import junit.framework.TestCase; - -import org.apache.log4j.Logger; - -import uk.co.thebadgerset.junit.extensions.listeners.TKTestListener; - -/** - * Listens for test results for a named test and outputs these in the standard JUnit XML format to the specified - * writer. - * - *

The API for this listener accepts notifications about different aspects of a tests results through different - * methods, so some assumption needs to be made as to which test result a notification refers to. For example - * {@link #startTest} will be called, then possibly {@link #timing} will be called, even though the test instance is - * passed in both cases, it is not enough to distinguish a particular run of the test, as the test case instance may - * be being shared between multiple threads, or being run a repeated number of times, and can therfore be re-used - * between calls. The listeners make the assumption that, for every test, a unique thread will call {@link #startTest} - * and {@link #endTest} to delimit each test. All calls to set test parameters, timings, state and so on, will occur - * between the start and end and will be given with the same thread id as the start and end, so the thread id provides - * a unqiue value to identify a particular test run against. - * - *

- *
CRC Card
Responsibilities Collaborations - *
- * - * @todo Merge this class with CSV test listener, making the collection of results common to both, and only factoring - * out the results printing code into sub-classes. Provide a simple XML results formatter with the same format as - * the ant XML formatter, and a more structured one for outputing results with timings and summaries from - * performance tests. - */ -public class XMLTestListener implements TKTestListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(XMLTestListener.class); - - /** The results file writer. */ - protected Writer writer; - - /** Holds the results for individual tests. */ - // protected Map results = new LinkedHashMap(); - // protected List results = new ArrayList(); - - /** - * Map for holding results on a per thread basis as they come in. A ThreadLocal is not used as sometimes an - * explicit thread id must be used, where notifications come from different threads than the ones that called - * the test method. - */ - Map threadLocalResults = Collections.synchronizedMap(new LinkedHashMap()); - - /** - * Holds results for tests that have ended. Transferring these results here from the per-thread results map, means - * that the thread id is freed for the thread to generate more results. - */ - List results = new ArrayList(); - - /** Holds the overall error count. */ - protected int errors = 0; - - /** Holds the overall failure count. */ - protected int failures = 0; - - /** Holds the overall tests run count. */ - protected int runs = 0; - - /** Holds the name of the class that tests are being run for. */ - String testClassName; - - /** - * Creates a new XML results output listener that writes to the specified location. - * - * @param writer The location to write results to. - */ - public XMLTestListener(Writer writer, String testClassName) - { - log.debug("public XMLTestListener(Writer writer, String testClassName = " + testClassName + "): called"); - - this.writer = writer; - this.testClassName = testClassName; - } - - /** - * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed. - * - * @param test The test to resest any results for. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void reset(Test test, Long threadId) - { - log.debug("public void reset(Test test = " + test + ", Long threadId = " + threadId + "): called"); - - XMLTestListener.Result r = - (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); - - r.error = null; - r.failure = null; - - } - - /** - * A test started. - */ - public void startTest(Test test) - { - log.debug("public void startTest(Test test = " + test + "): called"); - - Result newResult = new Result(test.getClass().getName(), ((TestCase) test).getName()); - - // Initialize the thread local test results. - threadLocalResults.put(Thread.currentThread().getId(), newResult); - runs++; - } - - /** - * Should be called every time a test completes with the run time of that test. - * - * @param test The name of the test. - * @param nanos The run time of the test in nanoseconds. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void timing(Test test, long nanos, Long threadId) - { } - - /** - * Should be called every time a test completed with the amount of memory used before and after the test was run. - * - * @param test The test which memory was measured for. - * @param memStart The total JVM memory used before the test was run. - * @param memEnd The total JVM memory used after the test was run. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void memoryUsed(Test test, long memStart, long memEnd, Long threadId) - { } - - /** - * Should be called every time a parameterized test completed with the int value of its test parameter. - * - * @param test The test which memory was measured for. - * @param parameter The int parameter value. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void parameterValue(Test test, int parameter, Long threadId) - { } - - /** - * Should be called every time a test completes with the current number of test threads running. - * - * @param test The test for which the measurement is being generated. - * @param threads The number of tests being run concurrently. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void concurrencyLevel(Test test, int threads, Long threadId) - { } - - /** - * Notifies listeners of the tests read/set properties. - * - * @param properties The tests read/set properties. - */ - public void properties(Properties properties) - { } - - /** - * A test ended. - */ - public void endTest(Test test) - { - log.debug("public void endTest(Test test = " + test + "): called"); - - // Move complete test results into the completed tests list. - Result r = threadLocalResults.get(Thread.currentThread().getId()); - results.add(r); - - // Clear all the test results for the thread. - threadLocalResults.remove(Thread.currentThread().getId()); - } - - /** - * Called when a test completes. Success, failure and errors. This method should be used when registering an - * end test from a different thread than the one that started the test. - * - * @param test The test which completed. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void endTest(Test test, Long threadId) - { - log.debug("public void endTest(Test test = " + test + ", Long threadId = " + threadId + "): called"); - - // Move complete test results into the completed tests list. - Result r = - (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); - results.add(r); - - // Clear all the test results for the thread. - threadLocalResults.remove(Thread.currentThread().getId()); - } - - /** - * An error occurred. - */ - public void addError(Test test, Throwable t) - { - log.debug("public void addError(Test test = " + test + ", Throwable t = " + t + "): called"); - - Result r = threadLocalResults.get(Thread.currentThread().getId()); - r.error = t; - errors++; - } - - /** - * A failure occurred. - */ - public void addFailure(Test test, AssertionFailedError t) - { - log.debug("public void addFailure(Test test = " + test + ", AssertionFailedError t = " + t + "): called"); - - Result r = threadLocalResults.get(Thread.currentThread().getId()); - r.failure = t; - failures++; - } - - /** - * Called when a test completes to mark it as a test fail. This method should be used when registering a - * failure from a different thread than the one that started the test. - * - * @param test The test which failed. - * @param e The assertion that failed the test. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void addFailure(Test test, AssertionFailedError e, Long threadId) - { - log.debug("public void addFailure(Test test, AssertionFailedError e, Long threadId): called"); - - Result r = - (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); - r.failure = e; - failures++; - } - - /** - * Notifies listeners of the start of a complete run of tests. - */ - public void startBatch() - { - log.debug("public void startBatch(): called"); - - // Reset all results counts. - threadLocalResults = Collections.synchronizedMap(new HashMap()); - errors = 0; - failures = 0; - runs = 0; - - // Write out the file header. - try - { - writer.write("\n"); - } - catch (IOException e) - { - throw new RuntimeException("Unable to write the test results.", e); - } - } - - /** - * Notifies listeners of the end of a complete run of tests. - * - * @param parameters The optional test parameters to log out with the batch results. - */ - public void endBatch(Properties parameters) - { - log.debug("public void endBatch(Properties parameters = " + parameters + "): called"); - - // Write out the results. - try - { - // writer.write("\n"); - writer.write("\n"); - - for (Result result : results) - { - writer.write(" \n"); - - if (result.error != null) - { - writer.write(" "); - result.error.printStackTrace(new PrintWriter(writer)); - writer.write(" "); - } - else if (result.failure != null) - { - writer.write(" "); - result.failure.printStackTrace(new PrintWriter(writer)); - writer.write(" "); - } - - writer.write(" \n"); - } - - writer.write("\n"); - writer.flush(); - } - catch (IOException e) - { - throw new RuntimeException("Unable to write the test results.", e); - } - } - - /** - * Used to capture the results of a particular test run. - */ - protected static class Result - { - public Result(String testClass, String testName) - { - this.testClass = testClass; - this.testName = testName; - } - - public String testClass; - public String testName; - - /** Holds the exception that caused error in this test. */ - public Throwable error; - - /** Holds the assertion exception that caused failure in this test. */ - public AssertionFailedError failure; - - /** Holds the error count for this test. */ - // public int errors = 0; - - /** Holds the failure count for this tests. */ - // public int failures = 0; - - /** Holds the overall tests run count for this test. */ - // public int runs = 0; - - /*public boolean equals(Object o) - { - if (this == o) - { - return true; - } - - if (!(o instanceof Result)) - { - return false; - } - - final Result result = (Result) o; - - if ((testClass != null) ? (!testClass.equals(result.testClass)) : (result.testClass != null)) - { - return false; - } - - if ((testName != null) ? (!testName.equals(result.testName)) : (result.testName != null)) - { - return false; - } - - return true; - } - - public int hashCode() - { - int result; - result = ((testClass != null) ? testClass.hashCode() : 0); - result = (29 * result) + ((testName != null) ? testName.hashCode() : 0); - - return result; - }*/ - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase1DummyRun.java deleted file mode 100644 index e642ef792b..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase1DummyRun.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * 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.testcases; - -import java.util.HashMap; -import java.util.Map; - -import javax.jms.Message; - -import junit.framework.Assert; - -import org.apache.log4j.Logger; - -import org.apache.qpid.interop.coordinator.CoordinatingTestCase; - -/** - *

- *
CRC Card
Responsibilities Collaborations - *
Exercises the interop testing framework without actually sending any test messages. - * {@link org.apache.qpid.interop.coordinator.CoordinatingTestCase} - *
- */ -public class CoordinatingTestCase1DummyRun extends CoordinatingTestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(CoordinatingTestCase1DummyRun.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public CoordinatingTestCase1DummyRun(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - */ - public void testDummyRun() throws Exception - { - log.debug("public void testDummyRun(): called"); - - Map testConfig = new HashMap(); - testConfig.put("TEST_NAME", "TC1_DummyRun"); - - Message[] reports = sequenceTest(testConfig); - - // Compare sender and receiver reports. - Assert.assertEquals("Expected to get 2 dummy reports.", 2, reports.length); - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC1_DummyRun"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java deleted file mode 100644 index b1b2d9f847..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase2BasicP2P.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * - * 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.testcases; - -import java.util.HashMap; -import java.util.Map; - -import javax.jms.Message; - -import junit.framework.Assert; - -import org.apache.log4j.Logger; - -import org.apache.qpid.interop.coordinator.CoordinatingTestCase; - -/** - *

- *
CRC Card
Responsibilities Collaborations - *
Setup p2p test parameters and compare with test output. {@link CoordinatingTestCase} - *
- */ -public class CoordinatingTestCase2BasicP2P extends CoordinatingTestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(CoordinatingTestCase2BasicP2P.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public CoordinatingTestCase2BasicP2P(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - */ - public void testBasicP2P() throws Exception - { - log.debug("public void testBasicP2P(): called"); - - Map testConfig = new HashMap(); - testConfig.put("TEST_NAME", "TC2_BasicP2P"); - testConfig.put("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); - testConfig.put("P2P_NUM_MESSAGES", 50); - - Message[] reports = sequenceTest(testConfig); - - // Compare sender and receiver reports. - int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); - int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); - - Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); - Assert.assertEquals("Sender and receiver messages sent did not match up.", messagesSent, messagesReceived); - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC2_BasicP2P"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase3BasicPubSub.java deleted file mode 100644 index 702c240e9a..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/testcases/CoordinatingTestCase3BasicPubSub.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * - * 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.testcases; - -import java.util.HashMap; -import java.util.Map; - -import javax.jms.Message; - -import junit.framework.Assert; - -import org.apache.log4j.Logger; - -import org.apache.qpid.interop.coordinator.CoordinatingTestCase; - -/** - *

- *
CRC Card
Responsibilities Collaborations - *
Setup pub/sub test parameters and compare with test output. {@link CoordinatingTestCase} - *
- */ -public class CoordinatingTestCase3BasicPubSub extends CoordinatingTestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(CoordinatingTestCase3BasicPubSub.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public CoordinatingTestCase3BasicPubSub(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - */ - public void testBasicPubSub() throws Exception - { - log.debug("public void testBasicPubSub(): called"); - - Map testConfig = new HashMap(); - testConfig.put("TEST_NAME", "TC3_BasicPubSub"); - testConfig.put("PUBSUB_KEY", "tc3route"); - testConfig.put("PUBSUB_NUM_MESSAGES", 10); - testConfig.put("PUBSUB_NUM_RECEIVERS", 5); - - Message[] reports = sequenceTest(testConfig); - - // Compare sender and receiver reports. - int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); - int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); - - Assert.assertEquals("The requested number of messages were not sent.", 10, messagesSent); - Assert.assertEquals("Received messages did not match up to num sent * num receivers.", messagesSent * 5, - messagesReceived); - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC3_BasicPubSub"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java deleted file mode 100644 index 5545f8d2dc..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Listener.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * - * 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}. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Count messages received on a topic. {@link org.apache.qpid.interop.old.Publisher} - *
Send reports on messages received, when requested to. {@link org.apache.qpid.interop.old.Publisher} - *
Shutdown, when requested to. {@link org.apache.qpid.interop.old.Publisher} - *
- * - * @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 true if it is a termination request control message, false 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 true if it is a report request control message, false 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 trueIf the specified field has the specified value, fals 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); - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java deleted file mode 100644 index f3a545f580..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/old/Publisher.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * - * 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}. - * - *

- *
CRC Card
Responsibilities Collaborations - *
- * - * @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(); - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java new file mode 100644 index 0000000000..a2e4a00aa6 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java @@ -0,0 +1,84 @@ +/* + * + * 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.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Coordinates test case 1, from the interop test specification. This test connects up the sender and receivers roles, + * and gets some dummy test reports from them, in order to check that the test framework itself is operational. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Exercises the interop testing framework without actually sending any test messages. + * {@link FrameworkBaseCase} + *
+ */ +public class InteropTestCase1DummyRun extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase1DummyRun.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase1DummyRun(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testDummyRun() throws Exception + { + log.debug("public void testDummyRun(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "TC1_DummyRun"); + + /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + // Assert.assertEquals("Expected to get 2 dummy reports.", 2, reports.length); + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC1_DummyRun"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java new file mode 100644 index 0000000000..6d6515f1fd --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java @@ -0,0 +1,90 @@ +/* + * + * 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.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Implements test case 2, from the interop test specification. This test sets up the TC2_BasicP2P test for 50 + * messages. It checks that the sender and receivers reports both indicate that all the test messages were transmitted + * successfully. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Setup p2p test parameters and compare with test output. {@link FrameworkBaseCase} + *
+ */ +public class InteropTestCase2BasicP2P extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase2BasicP2P.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase2BasicP2P(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicP2P() throws Exception + { + log.debug("public void testBasicP2P(): called"); + + Properties testConfig = new Properties(); + testConfig.setProperty("TEST_NAME", "TC2_BasicP2P"); + testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); + testConfig.put("P2P_NUM_MESSAGES", 50); + + /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); + Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived);*/ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC2_BasicP2P"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java new file mode 100644 index 0000000000..2faca91e73 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java @@ -0,0 +1,88 @@ +/* + * + * 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.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + *

+ *
CRC Card
Responsibilities Collaborations + *
Setup pub/sub test parameters and compare with test output. {@link FrameworkBaseCase} + *
+ */ +public class InteropTestCase3BasicPubSub extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase3BasicPubSub.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase3BasicPubSub(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicPubSub() throws Exception + { + log.debug("public void testBasicPubSub(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "TC3_BasicPubSub"); + testConfig.put("PUBSUB_KEY", "tc3route"); + testConfig.put("PUBSUB_NUM_MESSAGES", 10); + testConfig.put("PUBSUB_NUM_RECEIVERS", 5); + + /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 10, messagesSent); + Assert.assertEquals("Received messages did not match up to num sent * num receivers.", messagesSent * 5, + messagesReceived);*/ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC3_BasicPubSub"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java deleted file mode 100644 index 37952d08c8..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/InteropClientTestCase.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * - * 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. - * - *

- *
CRC Card
Responsibilities - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Perform test case actions. - *
Generate test reports. - *
- */ -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 true to accept the invitation, false 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. - * return from here when you have finished the test.. this will signal the controller that the test has ended. - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void start() throws JMSException; - - /** - * Gives notice of termination of the test case actions. - * - * @throws JMSException Any JMSException resulting from allowed to fall through. - */ - public void terminate() throws JMSException, InterruptedException; - - /** - * 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/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java deleted file mode 100644 index a904bfa419..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java +++ /dev/null @@ -1,422 +0,0 @@ -/* - * - * 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 org.apache.log4j.Logger; -import org.apache.qpid.interop.testclient.testcases.TestCase1DummyRun; -import org.apache.qpid.interop.testclient.testcases.TestCase2BasicP2P; -import org.apache.qpid.util.CommandLineParser; -import org.apache.qpid.util.PropertiesUtils; - -import javax.jms.Connection; -import javax.jms.ConnectionFactory; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageConsumer; -import javax.jms.MessageListener; -import javax.jms.MessageProducer; -import javax.jms.Session; -import javax.naming.Context; -import javax.naming.InitialContext; -import javax.naming.NamingException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -/** - * 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}. - * - *

- *
Messages Handled by TestClient
Message Action - *
Invite(compulsory) Reply with Enlist. - *
Invite(test case) Reply with Enlist if test case available. - *
AssignRole(test case) Reply with Accept Role if matches an enlisted test. Keep test parameters. - *
Start Send test messages defined by test parameters. Send report on messages sent. - *
Status Request Send report on messages received. - *
- * - *

- *
CRC Card
Responsibilities Collaborations - *
Handle all incoming control messages. {@link InteropClientTestCase} - *
Configure and look up test cases by name. {@link InteropClientTestCase} - *
- */ -public class TestClient implements MessageListener -{ - private static Logger log = Logger.getLogger(TestClient.class); - - public static final String CONNECTION_PROPERTY = "connectionfactory.broker"; - public static final String CONNECTION_NAME = "broker"; - public static final String CLIENT_NAME = "java"; - public static final String DEFAULT_CONNECTION_PROPS_RESOURCE = "org/apache/qpid/interop/connection.properties"; - - /** Holds the URL of the broker to run the tests on. */ - public static String brokerUrl; - - /** Holds the virtual host to run the tests on. If null, then the default virtual host is used. */ - public static String virtualHost; - - /** Holds all the test cases loaded from the classpath. */ - Map testCases = new HashMap(); - - protected InteropClientTestCase currentTestCase; - - protected Connection _connection; - protected MessageProducer producer; - protected Session session; - - protected String clientName = CLIENT_NAME; - - /** - * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client - * identifying name. - * - * @param brokerUrl The url of the broker to connect to. - * @param virtualHost The virtual host to conect to. - * @param clientName The client name to use. - */ - public TestClient(String brokerUrl, String virtualHost, String clientName) - { - log.debug("public TestClient(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost - + ", String clientName = " + clientName + "): called"); - - // Retain the connection parameters. - this.brokerUrl = brokerUrl; - this.virtualHost = virtualHost; - this.clientName = clientName; - } - - /** - * The entry point for the interop test coordinator. This client accepts the following command line arguments: - * - *

- *
-b The broker URL. Optional. - *
-h The virtual host. Optional. - *
-n The test client name. Optional. - *
name=value Trailing argument define name/value pairs. Added to system properties. Optional. - *
- * - * @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", "false"}, - {"h", "The virtual host to use.", "virtual host", "false"}, - {"n", "The test client name.", "name", "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"); - String clientName = options.getProperty("n"); - - // 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, (clientName == null) ? CLIENT_NAME : clientName); - - // Use a class path scanner to find all the interop test case implementations. - Collection> testCaseClasses = - new ArrayList>(); - // ClasspathScanner.getMatches(InteropClientTestCase.class, "^TestCase.*", true); - // Hard code the test classes till the classpath scanner is fixed. - Collections.addAll(testCaseClasses, - new Class[]{TestCase1DummyRun.class, TestCase2BasicP2P.class, TestClient.class}); - - try - { - client.start(testCaseClasses); - } - catch (Exception e) - { - log.error("The test client was unable to start.", e); - System.exit(1); - } - } - - /** - * Starts the interop test client running. This causes it to start listening for incoming test invites. - * - * @throws JMSException Any underlying JMSExceptions are allowed to fall through. @param testCaseClasses - */ - protected void start(Collection> testCaseClasses) throws JMSException - { - log.debug("private void start(): called"); - - // Create all the test case implementations and index them by the test names. - for (Class nextClass : testCaseClasses) - { - try - { - InteropClientTestCase testCase = nextClass.newInstance(); - testCases.put(testCase.getName(), testCase); - } - catch (InstantiationException e) - { - log.warn("Could not instantiate test case class: " + nextClass.getName(), e); - // Ignored. - } - catch (IllegalAccessException e) - { - log.warn("Could not instantiate test case class due to illegal access: " + nextClass.getName(), e); - // Ignored. - } - } - - // Open a connection to communicate with the coordinator on. - _connection = createConnection(DEFAULT_CONNECTION_PROPS_RESOURCE, clientName, brokerUrl, virtualHost); - - session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Set this up to listen for control messages. - MessageConsumer consumer = session.createConsumer(session.createTopic("iop.control." + clientName)); - consumer.setMessageListener(this); - - MessageConsumer consumer2 = session.createConsumer(session.createTopic("iop.control")); - consumer2.setMessageListener(this); - - // Create a producer to send replies with. - producer = session.createProducer(null); - - // Start listening for incoming control messages. - _connection.start(); - } - - - public static Connection createConnection(String connectionPropsResource, String brokerUrl, String virtualHost) - { - return createConnection(connectionPropsResource, "clientID", brokerUrl, virtualHost); - } - - /** - * Establishes a JMS connection using a properties file and qpids built in JNDI implementation. This is a simple - * convenience method for code that does anticipate handling connection failures. All exceptions that indicate that - * the connection has failed, are wrapped as rutime exceptions, preumably handled by a top level failure handler. - * - * @param connectionPropsResource The name of the connection properties file. - * @param clientID - * @param brokerUrl The broker url to connect to, null to use the default from the - * properties. - * @param virtualHost The virtual host to connectio to, null to use the default. - * - * @return A JMS conneciton. - * - * @todo Make username/password configurable. Allow multiple urls for fail over. Once it feels right, move it to a - * Utils library class. - */ - public static Connection createConnection(String connectionPropsResource, String clientID, String brokerUrl, String virtualHost) - { - log.debug("public static Connection createConnection(String connectionPropsResource = " + connectionPropsResource - + ", String brokerUrl = " + brokerUrl + ", String clientID = " + clientID - + ", String virtualHost = " + virtualHost + " ): called"); - - try - { - Properties connectionProps = - PropertiesUtils.getProperties(TestClient.class.getClassLoader().getResourceAsStream( - connectionPropsResource)); - - if (brokerUrl != null) - { - String connectionString = - "amqp://guest:guest@" + clientID + "/" + ((virtualHost != null) ? virtualHost : "") + "?brokerlist='" + brokerUrl + "'"; - connectionProps.setProperty(CONNECTION_PROPERTY, connectionString); - } - - Context ctx = new InitialContext(connectionProps); - - ConnectionFactory cf = (ConnectionFactory) ctx.lookup(CONNECTION_NAME); - Connection connection = cf.createConnection(); - - return connection; - } - catch (IOException e) - { - throw new RuntimeException(e); - } - catch (NamingException e) - { - throw new RuntimeException(e); - } - catch (JMSException e) - { - throw new RuntimeException(e); - } - } - - /** - * Handles all incoming control messages. - * - * @param message The incoming message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - try - { - String controlType = message.getStringProperty("CONTROL_TYPE"); - String testName = message.getStringProperty("TEST_NAME"); - - log.info("onMessage(Message message = " + message + "): for '" + controlType + "' to '" + testName + "'"); - - // Check if the message is a test invite. - if ("INVITE".equals(controlType)) - { - // Flag used to indicate that an enlist should be sent. Only enlist to compulsory invites or invites - // for which test cases exist. - boolean enlist = false; - - if (testName != null) - { - log.debug("Got an invite to test: " + testName); - - // Check if the requested test case is available. - InteropClientTestCase testCase = testCases.get(testName); - - if (testCase != null) - { - // Make the requested test case the current test case. - currentTestCase = testCase; - enlist = true; - } - else - { - log.warn("'" + testName + "' not part of this clients tests."); - } - } - else - { - log.debug("Got a compulsory invite."); - - enlist = true; - } - - if (enlist) - { - // Reply with the client name in an Enlist message. - Message enlistMessage = session.createMessage(); - enlistMessage.setStringProperty("CONTROL_TYPE", "ENLIST"); - enlistMessage.setStringProperty("CLIENT_NAME", clientName); - enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); - enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - log.info("Sending Message '" + enlistMessage + "'. to " + message.getJMSReplyTo()); - - producer.send(message.getJMSReplyTo(), enlistMessage); - } - } - else if ("ASSIGN_ROLE".equals(controlType)) - { - // Assign the role to the current test case. - String roleName = message.getStringProperty("ROLE"); - - log.debug("Got a role assignment to role: " + roleName); - - InteropClientTestCase.Roles role = Enum.valueOf(InteropClientTestCase.Roles.class, roleName); - - currentTestCase.assignRole(role, message); - - // Reply by accepting the role in an Accept Role message. - Message acceptRoleMessage = session.createMessage(); - acceptRoleMessage.setStringProperty("CONTROL_TYPE", "ACCEPT_ROLE"); - acceptRoleMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - producer.send(message.getJMSReplyTo(), acceptRoleMessage); - } - else if ("START".equals(controlType) || "STATUS_REQUEST".equals(controlType)) - { - if ("START".equals(controlType)) - { - log.debug("Got a start notification."); - - // Start the current test case. - currentTestCase.start(); - } - else - { - log.debug("Got a status request."); - } - - // Generate the report from the test case and reply with it as a Report message. - Message reportMessage = currentTestCase.getReport(session); - reportMessage.setStringProperty("CONTROL_TYPE", "REPORT"); - reportMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - producer.send(message.getJMSReplyTo(), reportMessage); - } - else if ("TERMINATE".equals(controlType)) - { - log.info("Received termination instruction from coordinator."); - -// try -// { -// currentTestCase.terminate(); -// } -// catch (InterruptedException e) -// { -// // -// } - // Is a cleaner shutdown needed? - _connection.close(); - System.exit(0); - } - else - { - // Log a warning about this but otherwise ignore it. - log.warn("Got an unknown control message, controlType = " + controlType + ", message = " + message); - } - } - catch (JMSException e) - { - // Log a warning about this, but otherwise ignore it. - log.warn("A JMSException occurred whilst handling a message."); - log.debug("Got JMSException whilst handling message: " + message, e); - } - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java deleted file mode 100644 index 5f257c0b36..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * - * 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 org.apache.log4j.Logger; - -import org.apache.qpid.interop.testclient.InteropClientTestCase; - -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.Session; - -/** - * 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. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Perform test case actions. - *
Generate test reports. - *
- */ -public class TestCase1DummyRun implements InteropClientTestCase -{ - private static final Logger log = Logger.getLogger(TestCase1DummyRun.class); - - public String getName() - { - log.debug("public String getName(): called"); - - return "TC1_DummyRun"; - } - - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage): called"); - - // Test parameters don't matter, accept all invites. - return true; - } - - public void assignRole(Roles role, Message assignRoleMessage) throws JMSException - { - log.debug("public void assignRole(Roles role, Message assignRoleMessage): called"); - - // Do nothing, both roles are the same. - } - - public void start() - { - log.debug("public void start(): called"); - - // Do nothing. - } - - public void terminate() throws JMSException - { - //todo - } - - public Message getReport(Session session) throws JMSException - { - log.debug("public Message getReport(Session session): called"); - - // 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) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Ignore any messages. - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java deleted file mode 100644 index ff56ee9b93..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase2BasicP2P.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * - * 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.*; - -import org.apache.log4j.Logger; - -import org.apache.qpid.interop.testclient.InteropClientTestCase; -import org.apache.qpid.interop.testclient.TestClient; - -/** - * 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. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Send required number of test messages. - *
Generate test reports. - *
- */ -public class TestCase2BasicP2P implements InteropClientTestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase2BasicP2P.class); - - /** Holds the count of test messages received. */ - private int messageCount; - - /** The role to be played by the test. */ - private Roles role; - - /** The number of test messages to send. */ - private int numMessages; - - /** The routing key to send them to on the default direct exchange. */ - private Destination sendDestination; - - /** The connection to send the test messages on. */ - private Connection connection; - - /** The session to send the test messages on. */ - private Session session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "TC2_BasicP2P"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); - - // 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 - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Reset the message count for a new test. - messageCount = 0; - - // Take note of the role to be played. - this.role = role; - - // Create a new connection to pass the test messages on. - connection = - TestClient.createConnection(TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, TestClient.brokerUrl, - TestClient.virtualHost); - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Extract and retain the test parameters. - numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); - sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); - - log.debug("numMessages = " + numMessages); - log.debug("sendDestination = " + sendDestination); - log.debug("role = " + role); - - switch (role) - { - // Check if the sender role is being assigned, and set up a message producer if so. - case SENDER: - producer = session.createProducer(sendDestination); - break; - - // Otherwise the receiver role is being assigned, so set this up to listen for messages. - case RECEIVER: - MessageConsumer consumer = session.createConsumer(sendDestination); - consumer.setMessageListener(this); - break; - } - - connection.start(); - } - - /** - * Performs the test case actions. - */ - public void start() throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - if (role.equals(Roles.SENDER)) - { - Message testMessage = session.createTextMessage("test"); - - for (int i = 0; i < numMessages; i++) - { - producer.send(testMessage); - - // Increment the message count. - messageCount++; - } - } - } - - public void terminate() throws JMSException - { - //todo - } - - /** - * 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 - { - log.debug("public Message getReport(Session session): called"); - - // Close the test connection. - connection.close(); - - // Generate a report message containing the count of the number of messages passed. - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - report.setIntProperty("MESSAGE_COUNT", messageCount); - - return report; - } - - /** - * Counts incoming test messages. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Increment the message count. - messageCount++; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java deleted file mode 100644 index 7b35142c82..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase3BasicPubSub.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * - * 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.*; - -import org.apache.log4j.Logger; - -import org.apache.qpid.interop.testclient.InteropClientTestCase; - -/** - * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the - * default topic exchange, using the specified number of receiver connections. Produces reports on the actual number of - * messages sent/received. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Send required number of test messages using pub/sub. - *
Generate test reports. - *
- */ -public class TestCase3BasicPubSub implements InteropClientTestCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase3BasicPubSub.class); - - /** Holds the count of test messages received. */ - private int messageCount; - - /** The role to be played by the test. */ - private Roles role; - - /** The number of test messages to send. */ - private int numMessages; - - /** The number of receiver connection to use. */ - private int numReceivers; - - /** The routing key to send them to on the default direct exchange. */ - private Destination sendDestination; - - /** The connections to send/receive the test messages on. */ - private Connection[] connection; - - /** The sessions to send/receive the test messages on. */ - private Session[] session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "TC3_BasicPubSub"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); - - // 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 - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Reset the message count for a new test. - messageCount = 0; - - // Take note of the role to be played. - this.role = role; - - // Extract and retain the test parameters. - numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); - numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); - String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); - - log.debug("numMessages = " + numMessages); - log.debug("numReceivers = " + numReceivers); - log.debug("sendKey = " + sendKey); - log.debug("role = " + role); - - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - // Create a new connection to pass the test messages on. - connection = new Connection[1]; - session = new Session[1]; - - connection[0] = - org.apache.qpid.interop.testclient.TestClient.createConnection(org.apache.qpid.interop.testclient.TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, org.apache.qpid.interop.testclient.TestClient.brokerUrl, - org.apache.qpid.interop.testclient.TestClient.virtualHost); - session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Extract and retain the test parameters. - sendDestination = session[0].createTopic(sendKey); - - producer = session[0].createProducer(sendDestination); - break; - - // Otherwise the receiver role is being assigned, so set this up to listen for messages on the required number - // of receiver connections. - case RECEIVER: - // Create the required number of receiver connections. - connection = new Connection[numReceivers]; - session = new Session[numReceivers]; - - for (int i = 0; i < numReceivers; i++) - { - connection[i] = - org.apache.qpid.interop.testclient.TestClient.createConnection(org.apache.qpid.interop.testclient.TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, org.apache.qpid.interop.testclient.TestClient.brokerUrl, - org.apache.qpid.interop.testclient.TestClient.virtualHost); - session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); - - sendDestination = session[i].createTopic(sendKey); - - MessageConsumer consumer = session[i].createConsumer(sendDestination); - consumer.setMessageListener(this); - } - - break; - } - - // Start all the connection dispatcher threads running. - for (int i = 0; i < connection.length; i++) - { - connection[i].start(); - } - } - - /** - * Performs the test case actions. - */ - public void start() throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - if (role.equals(Roles.SENDER)) - { - Message testMessage = session[0].createTextMessage("test"); - - for (int i = 0; i < numMessages; i++) - { - producer.send(testMessage); - - // Increment the message count. - messageCount++; - } - } - } - - public void terminate() throws JMSException, InterruptedException - { - //todo - } - - /** - * 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 - { - log.debug("public Message getReport(Session session): called"); - - // Close the test connections. - for (int i = 0; i < connection.length; i++) - { - connection[i].close(); - } - - // Generate a report message containing the count of the number of messages passed. - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - report.setIntProperty("MESSAGE_COUNT", messageCount); - - return report; - } - - /** - * Counts incoming test messages. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Increment the message count. - messageCount++; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java new file mode 100644 index 0000000000..642f91b772 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java @@ -0,0 +1,906 @@ +/* + * + * 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.sustained; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQNoConsumersException; +import org.apache.qpid.client.AMQNoRouteException; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub; +import org.apache.qpid.test.framework.TestUtils; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +/** + * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the + * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of + * messages sent/received. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages using pub/sub.
Generate test reports. + *
+ */ +public class SustainedClientTestCase extends TestCase3BasicPubSub implements ExceptionListener, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(SustainedClientTestCase.class); + + /** Used to log to the console. */ + private static final Logger console = Logger.getLogger("SustainedTest"); + + /** The role to be played by the test. */ + private Roles role; + + /** The number of receivers connection to use. */ + private int numReceivers; + + /** The routing key to send them to on the default direct exchange. */ + private Destination sendDestination; + + /** The routing key to send updates to on the default direct exchange. */ + private Destination sendUpdateDestination; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** Adapter that adjusts the send rate based on the updates from clients. */ + SustainedRateAdapter _rateAdapter; + + /** */ + int _batchSize; + + private static final long TEN_MILLI_SEC = 10000000; + private static final int DEBUG_LOG_UPATE_INTERVAL = 10; + private static final int LOG_UPATE_INTERVAL = 10; + private static final boolean SLEEP_PER_MESSAGE = Boolean.getBoolean("sleepPerMessage"); + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "Perf_SustainedPubSub"; + } + + /** + * 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 receivers. + * @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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numReceivers = assignRoleMessage.getIntProperty("SUSTAINED_NUM_RECEIVERS"); + _batchSize = assignRoleMessage.getIntProperty("SUSTAINED_UPDATE_INTERVAL"); + String sendKey = assignRoleMessage.getStringProperty("SUSTAINED_KEY"); + String sendUpdateKey = assignRoleMessage.getStringProperty("SUSTAINED_UPDATE_KEY"); + int ackMode = assignRoleMessage.getIntProperty("ACKNOWLEDGE_MODE"); + String clientName = assignRoleMessage.getStringProperty("CLIENT_NAME"); + + if (log.isDebugEnabled()) + { + log.debug("numReceivers = " + numReceivers); + log.debug("_batchSize = " + _batchSize); + log.debug("ackMode = " + ackMode); + log.debug("sendKey = " + sendKey); + log.debug("sendUpdateKey = " + sendUpdateKey); + log.debug("role = " + role); + } + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + console.info("Creating Sender"); + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = TestUtils.createConnection(TestClient.testContextProperties); + session[0] = connection[0].createSession(false, ackMode); + + // Extract and retain the test parameters. + sendDestination = session[0].createTopic(sendKey); + + connection[0].setExceptionListener(this); + + producer = session[0].createProducer(sendDestination); + + sendUpdateDestination = session[0].createTopic(sendUpdateKey); + MessageConsumer updateConsumer = session[0].createConsumer(sendUpdateDestination); + + _rateAdapter = new SustainedRateAdapter(this); + updateConsumer.setMessageListener(_rateAdapter); + + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number + // of receivers connections. + case RECEIVER: + console.info("Creating Receiver"); + // Create the required number of receivers connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = TestUtils.createConnection(TestClient.testContextProperties); + session[i] = connection[i].createSession(false, ackMode); + + sendDestination = session[i].createTopic(sendKey); + + sendUpdateDestination = session[i].createTopic(sendUpdateKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + + consumer.setMessageListener(new SustainedListener(clientName + "-" + i, _batchSize, session[i], + sendUpdateDestination)); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (int i = 0; i < connection.length; i++) + { + connection[i].start(); + } + } + + /** Performs the test case actions. + * @param numMessages*/ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + _rateAdapter.run(); + break; + case RECEIVER: + + } + + // return from here when you have finished the test.. this will signal the controller and + } + + public void terminate() throws JMSException, InterruptedException + { + if (_rateAdapter != null) + { + _rateAdapter.stop(); + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession 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 + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connections. + for (int i = 0; i < connection.length; i++) + { + connection[i].close(); + } + + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + + return report; + } + + public void onException(JMSException jmsException) + { + Exception linked = jmsException.getLinkedException(); + + if (linked != null) + { + if (log.isDebugEnabled()) + { + log.debug("Linked Exception:" + linked); + } + + if ((linked instanceof AMQNoRouteException) || (linked instanceof AMQNoConsumersException)) + { + if (log.isDebugEnabled()) + { + if (linked instanceof AMQNoConsumersException) + { + log.warn("No clients currently available for message:" + + ((AMQNoConsumersException) linked).getUndeliveredMessage()); + } + else + { + log.warn("No route for message"); + } + } + + // Tell the rate adapter that there are no clients ready yet + _rateAdapter.NO_CLIENTS = true; + } + } + else + { + log.warn("Exception:" + linked); + } + } + + /** + * Inner class that listens for messages and sends a report for the time taken between receiving the 'start' and + * 'end' messages. + */ + class SustainedListener implements MessageListener + { + /** Number of messages received */ + private long _received = 0; + /** The number of messages in the batch */ + private int _batchSize = 0; + /** Record of the when the 'start' messagse was sen */ + private Long _startTime; + /** Message producer to use to send reports */ + MessageProducer _updater; + /** Session to create the report message on */ + Session _session; + /** Record of the client ID used for this SustainedListnener */ + String _client; + + /** + * Main Constructor + * + * @param clientname The _client id used to identify this connection. + * @param batchSize The number of messages that are to be sent per batch. Note: This is not used to + * control the interval between sending reports. + * @param session The controlSession used for communication. + * @param sendDestination The destination that update reports should be sent to. + * + * @throws JMSException My occur if creatingthe Producer fails + */ + public SustainedListener(String clientname, int batchSize, Session session, Destination sendDestination) + throws JMSException + { + _batchSize = batchSize; + _client = clientname; + _session = session; + _updater = session.createProducer(sendDestination); + } + + public void onMessage(Message message) + { + if (log.isTraceEnabled()) + { + log.trace("Message " + _received + "received in listener"); + } + + if (message instanceof TextMessage) + { + try + { + _received++; + if (((TextMessage) message).getText().equals("start")) + { + log.debug("Starting Batch"); + _startTime = System.nanoTime(); + } + else if (((TextMessage) message).getText().equals("end")) + { + if (_startTime != null) + { + long currentTime = System.nanoTime(); + sendStatus(currentTime - _startTime, _received, message.getIntProperty("BATCH")); + log.debug("End Batch"); + } + } + } + catch (JMSException e) + { + // ignore error + } + } + + } + + /** + * sendStatus creates and sends the report back to the publisher + * + * @param time taken for the the last batch + * @param received Total number of messages received. + * @param batchNumber the batch number + * @throws JMSException if an error occurs during the send + */ + private void sendStatus(long time, long received, int batchNumber) throws JMSException + { + Message updateMessage = _session.createTextMessage("update"); + updateMessage.setStringProperty("CLIENT_ID", ":" + _client); + updateMessage.setStringProperty("CONTROL_TYPE", "UPDATE"); + updateMessage.setLongProperty("RECEIVED", received); + updateMessage.setIntProperty("BATCH", batchNumber); + updateMessage.setLongProperty("DURATION", time); + + if (log.isInfoEnabled()) + { + log.info("**** SENDING [" + batchNumber + "]**** " + "CLIENT_ID:" + _client + " RECEIVED:" + received + + " BATCH:" + batchNumber + " DURATION:" + time); + } + + // Output on the main console.info the details of this batch + if ((batchNumber % 10) == 0) + { + console.info("Sending Report [" + batchNumber + "] " + "CLIENT_ID:" + _client + " RECEIVED:" + received + + " BATCH:" + batchNumber + " DURATION:" + time); + } + + _updater.send(updateMessage); + } + } + + /** + * This class is used here to adjust the _delay value which in turn is used to control the number of messages/second + * that are sent through the test system. + * + * By keeping a record of the messages recevied and the average time taken to process the batch size can be + * calculated and so the delay can be adjusted to maintain that rate. + * + * Given that delays of < 10ms can be rounded up the delay is only used between messages if the _delay > 10ms * no + * messages in the batch. Otherwise the delay is used at the end of the batch. + */ + class SustainedRateAdapter implements MessageListener, Runnable + { + private SustainedClientTestCase _client; + private long _batchVariance = Integer.getInteger("batchVariance", 3); // no. batches to allow drifting + private long _timeVariance = TEN_MILLI_SEC * 5; // no. nanos between send and report delay (10ms) + private volatile long _delay; // in nanos + private long _sent; + private Map _slowClients = new HashMap(); + private static final long PAUSE_SLEEP = TEN_MILLI_SEC / 1000; // 10 ms + private static final long NO_CLIENT_SLEEP = 1000; // 1s + private volatile boolean NO_CLIENTS = true; + private int _delayShifting; + private final int REPORTS_WITHOUT_CHANGE = Integer.getInteger("stableReportCount", 5); + private boolean _warmedup = false; + private static final long EXPECTED_TIME_PER_BATCH = 100000L; + private int _warmUpBatches = Integer.getInteger("warmUpBatches", 10); + + SustainedRateAdapter(SustainedClientTestCase client) + { + _client = client; + } + + public void onMessage(Message message) + { + if (log.isDebugEnabled()) + { + log.debug("SustainedRateAdapter onMessage(Message message = " + message + "): called"); + } + + try + { + String controlType = message.getStringProperty("CONTROL_TYPE"); + + // Check if the message is a test invite. + if ("UPDATE".equals(controlType)) + { + NO_CLIENTS = false; + long duration = message.getLongProperty("DURATION"); + long totalReceived = message.getLongProperty("RECEIVED"); + String client = message.getStringProperty("CLIENT_ID"); + int batchNumber = message.getIntProperty("BATCH"); + + if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + log.info("Update Report: CLIENT_ID:" + client + " RECEIVED:" + totalReceived + " Recevied BATCH:" + + batchNumber + " DURATION:" + duration); + } + + recordSlow(client, totalReceived, batchNumber); + + adjustDelay(client, batchNumber, duration); + + // Warm up completes when: + // we haven't warmed up + // and the number of batches sent to each client is at least half of the required warmup batches + if (!_warmedup && (batchNumber >= _warmUpBatches)) + { + _warmedup = true; + _warmup.countDown(); + + } + } + } + catch (JMSException e) + { + // + } + } + + CountDownLatch _warmup = new CountDownLatch(1); + + int _numBatches = 10000; + + // long[] _timings = new long[_numBatches]; + private boolean _running = true; + + public void run() + { + console.info("Warming up"); + + doBatch(_warmUpBatches); + + try + { + // wait for warmup to complete. + _warmup.await(); + + // set delay to the average length of the batches + _delay = _totalDuration / _warmUpBatches / delays.size(); + + console.info("Warmup complete delay set : " + _delay + " based on _totalDuration: " + _totalDuration + + " over no. batches: " + _warmUpBatches + " with client count: " + delays.size()); + + _totalDuration = 0L; + _totalReceived = 0L; + _sent = 0L; + } + catch (InterruptedException e) + { + // + } + + doBatch(_numBatches); + + } + + private void doBatch(int batchSize) // long[] timings, + { + TextMessage testMessage = null; + try + { + testMessage = _client.session[0].createTextMessage("start"); + + for (int batch = 0; batch <= batchSize; batch++) + // while (_running) + { + long start = System.nanoTime(); + + testMessage.setText("start"); + testMessage.setIntProperty("BATCH", batch); + + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + + testMessage.setText("test"); + // start at 2 so start and end count as part of batch + for (int m = 2; m < _batchSize; m++) + { + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + } + + testMessage.setText("end"); + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + + long end = System.nanoTime(); + + long sendtime = end - start; + + if (log.isDebugEnabled()) + { + log.info("Sent batch[" + batch + "](" + _batchSize + ") in " + sendtime); // timings[batch]); + } + + if ((batch % LOG_UPATE_INTERVAL) == 0) + { + console.info("Sent Batch[" + batch + "](" + _batchSize + ")" + status()); + } + + _rateAdapter.sleepBatch(); + + } + } + catch (JMSException e) + { + console.error("Runner ended"); + } + } + + private String status() + { + return " TotalDuration: " + _totalDuration + " for " + delays.size() + " consumers" + " Delay is " + _delay + + " resulting in " + + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch")); + } + + private void sleepBatch() + { + if (checkForSlowClients()) + { // if there werwe slow clients we have already slept so don't sleep anymore again. + return; + } + + if (!SLEEP_PER_MESSAGE) + { + // per batch sleep.. if sleep is to small to spread over the batch. + if (_delay <= (TEN_MILLI_SEC * _batchSize)) + { + sleepLong(_delay); + } + else + { + log.info("Not sleeping _delay > ten*batch is:" + _delay); + } + } + } + + public void stop() + { + _running = false; + } + + Map delays = new HashMap(); + Long _totalReceived = 0L; + Long _totalDuration = 0L; + int _skipUpdate = 0; + + /** + * Adjust the delay for sending messages based on this update from the client + * + * @param client The client that send this update + * @param duration The time taken for the last batch of messagse + * @param batchNumber The reported batchnumber from the client + */ + private void adjustDelay(String client, int batchNumber, long duration) + { + // Retrieve the current total time taken for this client. + Long currentTime = delays.get(client); + + // Add the new duration time to this client + if (currentTime == null) + { + currentTime = duration; + } + else + { + currentTime += duration; + } + + delays.put(client, currentTime); + + long batchesSent = _sent / _batchSize; + + // ensure we don't divide by zero + if (batchesSent == 0) + { + batchesSent = 1L; + } + + _totalReceived += _batchSize; + _totalDuration += duration; + + // calculate average duration accross clients per batch + long averageDuration = _totalDuration / delays.size() / batchesSent; + + // calculate the difference between current send delay and average report delay + long diff = (duration) - averageDuration; + + if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + log.info("TotalDuration:" + _totalDuration + " for " + delays.size() + " consumers." + " on batch: " + + batchesSent + " received batch: " + batchNumber + " Batch Duration: " + duration + " Average: " + + averageDuration + " so diff: " + diff + " for : " + client + " Delay is " + _delay + " resulting in " + + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch"))); + } + + // if the averageDuration differs from the current by more than the specified variane then adjust delay. + if (Math.abs(diff) > _timeVariance) + { + + // if the the _delay is larger than the required duration to send report + // speed up + if (diff > TEN_MILLI_SEC) + { + _delay -= TEN_MILLI_SEC; + + if (_delay < 0) + { + _delay = 0; + log.info("Reset _delay to 0"); + delayStable(); + } + else + { + delayChanged(); + } + + } + else if (diff < 0) // diff < 0 diff cannot be 0 as it is > _timeVariance + { + // the report took longer + _delay += TEN_MILLI_SEC; + delayChanged(); + } + } + else + { + delayStable(); + } + + // If we have a consumer that is behind with the batches. + if ((batchesSent - batchNumber) > _batchVariance) + { + log.debug("Increasing _delay as sending more than receiving"); + + _delay += 2 * TEN_MILLI_SEC; + delayChanged(); + } + + } + + /** Reset the number of iterations before we say the delay has stabilised. */ + private void delayChanged() + { + _delayShifting = REPORTS_WITHOUT_CHANGE; + } + + /** + * Record the fact that delay has stabilised If delay has stablised for REPORTS_WITHOUT_CHANGE then it will + * output Delay stabilised + */ + private void delayStable() + { + _delayShifting--; + + if (_delayShifting < 0) + { + _delayShifting = 0; + console.debug("Delay stabilised:" + _delay); + } + } + + /** + * Checks that the client has received enough messages. If the client has fallen behind then they are put in the + * _slowClients lists which will increase the delay. + * + * @param client The client identifier to check + * @param received the number of messages received by that client + * @param batchNumber + */ + private void recordSlow(String client, long received, int batchNumber) + { + if (Math.abs(batchNumber - (_sent / _batchSize)) > _batchVariance) + { + _slowClients.put(client, received); + } + else + { + _slowClients.remove(client); + } + } + + /** Incrment the number of sent messages and then sleep, if required. */ + public void sentMessage() + { + + _sent++; + + if (_delay > (TEN_MILLI_SEC * _batchSize)) + { + long batchDelay = _delay / _batchSize; + // less than 10ms sleep doesn't always work. + // _delay is in nano seconds + // if (batchDelay < (TEN_MILLI_SEC)) + // { + // sleep(0, (int) batchDelay); + // } + // else + { + // if (batchDelay < 30000000000L) + { + sleepLong(batchDelay); + } + } + } + else + { + if (SLEEP_PER_MESSAGE && (_delay > 0)) + { + sleepLong(_delay / _batchSize); + } + } + } + + /** + * Check at the end of each batch and pause sending messages to allow slow clients to catch up. + * + * @return true if there were slow clients that caught up. + */ + private boolean checkForSlowClients() + { + // This will allways be true as we are running this at the end of each batchSize + // if (_sent % _batchSize == 0) + { + // Cause test to pause when we have slow + if (!_slowClients.isEmpty() || NO_CLIENTS) + { + + while (!_slowClients.isEmpty()) + { + if (log.isInfoEnabled() && ((_sent / _batchSize % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + String clients = ""; + Iterator it = _slowClients.keySet().iterator(); + while (it.hasNext()) + { + clients += it.next(); + if (it.hasNext()) + { + clients += ", "; + } + } + + log.info("Pausing for slow clients:" + clients); + } + + if (console.isDebugEnabled() && ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0)) + { + console.debug(_slowClients.size() + " slow clients."); + } + + sleep(PAUSE_SLEEP); + } + + if (NO_CLIENTS) + { + sleep(NO_CLIENT_SLEEP); + } + + log.debug("Continuing"); + + return true; + } + else + { + if ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0) + { + console.info("Total Delay :" + _delay + " " + + ((_delayShifting == 0) ? "Stablised" : ("Not Stablised(" + _delayShifting + ")"))); + } + } + + } + + return false; + } + + /** + * Sleep normally takes micro-seconds this allows the use of a nano-second value. + * + * @param delay nanoseconds to sleep for. + */ + private void sleepLong(long delay) + { + sleep(delay / 1000000, (int) (delay % 1000000)); + } + + /** + * Sleep for the specified micro-seconds. + * @param sleep microseconds to sleep for. + */ + private void sleep(long sleep) + { + sleep(sleep, 0); + } + + /** + * Perform the sleep , swallowing any InteruptException. + * + * NOTE: If a sleep request is > 10s then reset only sleep for 5s + * + * @param milli to sleep for + * @param nano sub miliseconds to sleep for + */ + private void sleep(long milli, int nano) + { + try + { + log.debug("Sleep:" + milli + ":" + nano); + if (milli > 10000) + { + + if (_delay == milli) + { + _totalDuration = _totalReceived / _batchSize * EXPECTED_TIME_PER_BATCH; + log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + + "s. Reset _totalDuration:" + _totalDuration); + } + else + { + log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + "s"); + } + + milli = 5000; + } + + Thread.sleep(milli, nano); + } + catch (InterruptedException e) + { + // + } + } + + public void setClient(SustainedClientTestCase client) + { + _client = client; + } + } + +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java new file mode 100644 index 0000000000..0077b4727a --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java @@ -0,0 +1,126 @@ +/* + * + * 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.sustained; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.test.framework.DropInTest; +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import javax.jms.JMSException; +import javax.jms.Message; + +import java.util.Properties; + +/** + * SustainedTestCase is a {@link FrameworkBaseCase} that runs the "Perf_SustainedPubSub" test case. This consists of one + * test client sending, and several receiving, and attempts to find the highest rate at which messages can be broadcast + * to the receivers. It is also a {@link DropInTest} to which more test clients may be added during a test run. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
+ *
+ */ +public class SustainedTestCase extends FrameworkBaseCase implements DropInTest +{ + /** Used for debugging. */ + Logger log = Logger.getLogger(SustainedTestCase.class); + + /** Holds the root name of the topic on which to send the test messages. */ + private static final String SUSTAINED_KEY = "Perf_SustainedPubSub"; + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public SustainedTestCase(String name) + { + super(name); + } + + /** + * Performs a single test run of the sustained test. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicPubSub() throws Exception + { + log.debug("public void testSinglePubSubCycle(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "Perf_SustainedPubSub"); + testConfig.put("SUSTAINED_KEY", SUSTAINED_KEY); + testConfig.put("SUSTAINED_NUM_RECEIVERS", Integer.getInteger("numReceives", 2)); + testConfig.put("SUSTAINED_UPDATE_INTERVAL", Integer.getInteger("batchSize", 1000)); + testConfig.put("SUSTAINED_UPDATE_KEY", SUSTAINED_KEY + ".UPDATE"); + testConfig.put("ACKNOWLEDGE_MODE", Integer.getInteger("ackMode", AMQSession.AUTO_ACKNOWLEDGE)); + + log.info("Created Config: " + testConfig.entrySet().toArray()); + + getCircuitFactory().sequenceTest(null, null, testConfig); + } + + /** + * Accepts a late joining client into this test case. The client will be enlisted with a control message + * with the 'CONTROL_TYPE' field set to the value 'LATEJOIN'. It should also provide values for the fields: + * + *

+ *
CLIENT_NAME A unique name for the new client. + *
CLIENT_PRIVATE_CONTROL_KEY The key for the route on which the client receives its control messages. + *
+ * + * @param message The late joiners join message. + * + * @throws JMSException Any JMS Exception are allowed to fall through, indicating that the join failed. + */ + public void lateJoin(Message message) throws JMSException + { + throw new RuntimeException("Not implemented."); + /* + // Extract the joining clients details from its join request message. + TestClientDetails clientDetails = new TestClientDetails(); + clientDetails.clientName = message.getStringProperty("CLIENT_NAME"); + clientDetails.privateControlKey = message.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); + + // Register the joining client, but do block for confirmation as cannot do a synchronous receivers during this + // method call, as it may have been called from an 'onMessage' method. + assignReceiverRole(clientDetails, new Properties(), false); + */ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as known to the test + * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test + * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case + * name "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "Perf_SustainedPubSub"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestClient.java deleted file mode 100644 index 6f2089290a..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestClient.java +++ /dev/null @@ -1,931 +0,0 @@ -/* - * - * 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.sustained; - -import org.apache.log4j.Logger; -import org.apache.qpid.client.AMQNoConsumersException; -import org.apache.qpid.client.AMQNoRouteException; -import org.apache.qpid.interop.testclient.testcases.TestCase3BasicPubSub; - -import javax.jms.Connection; -import javax.jms.Destination; -import javax.jms.ExceptionListener; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageConsumer; -import javax.jms.MessageListener; -import javax.jms.MessageProducer; -import javax.jms.Session; -import javax.jms.TextMessage; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.CountDownLatch; - -/** - * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the - * default topic exchange, using the specified number of receiver connections. Produces reports on the actual number of - * messages sent/received. - * - *

CRC Card
Responsibilities Collaborations
Supply the name - * of the test case that this implements.
Accept/Reject invites based on test parameters.
Adapt to - * assigned roles.
Send required number of test messages using pub/sub.
Generate test reports. - *
- */ -public class SustainedTestClient extends TestCase3BasicPubSub implements ExceptionListener -{ - /** Used for debugging. */ - private static final Logger debugLog = Logger.getLogger(SustainedTestClient.class); - - private static final Logger log = Logger.getLogger("SustainedTest"); - - - /** The role to be played by the test. */ - private Roles role; - - /** The number of test messages to send. */ -// private int numMessages; - - /** The number of receiver connection to use. */ - private int numReceivers; - - /** The routing key to send them to on the default direct exchange. */ - private Destination sendDestination; - - /** The routing key to send updates to on the default direct exchange. */ - private Destination sendUpdateDestination; - - - /** The connections to send/receive the test messages on. */ - private Connection[] connection; - - /** The sessions to send/receive the test messages on. */ - private Session[] session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** Adapter that adjusts the send rate based on the updates from clients. */ - SustainedRateAdapter _rateAdapter; - - /** */ - int _batchSize; - - - private static final long TEN_MILLI_SEC = 10000000; - private static final int DEBUG_LOG_UPATE_INTERVAL = 10; - private static final int LOG_UPATE_INTERVAL = 10; - private static final boolean SLEEP_PER_MESSAGE = Boolean.getBoolean("sleepPerMessage"); - - /** - * 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() - { - debugLog.debug("public String getName(): called"); - - return "Perf_SustainedPubSub"; - } - - /** - * 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 - { - debugLog.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Take note of the role to be played. - this.role = role; - - // Extract and retain the test parameters. - numReceivers = assignRoleMessage.getIntProperty("SUSTAINED_NUM_RECEIVERS"); - _batchSize = assignRoleMessage.getIntProperty("SUSTAINED_UPDATE_INTERVAL"); - String sendKey = assignRoleMessage.getStringProperty("SUSTAINED_KEY"); - String sendUpdateKey = assignRoleMessage.getStringProperty("SUSTAINED_UPDATE_KEY"); - int ackMode = assignRoleMessage.getIntProperty("ACKNOWLEDGE_MODE"); - String clientName = assignRoleMessage.getStringProperty("CLIENT_NAME"); - - if (debugLog.isDebugEnabled()) - { - debugLog.debug("numReceivers = " + numReceivers); - debugLog.debug("_batchSize = " + _batchSize); - debugLog.debug("ackMode = " + ackMode); - debugLog.debug("sendKey = " + sendKey); - debugLog.debug("sendUpdateKey = " + sendUpdateKey); - debugLog.debug("role = " + role); - } - - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - log.info("Creating Sender"); - // Create a new connection to pass the test messages on. - connection = new Connection[1]; - session = new Session[1]; - - connection[0] = - org.apache.qpid.interop.testclient.TestClient.createConnection(org.apache.qpid.interop.testclient.TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, - clientName, - org.apache.qpid.interop.testclient.TestClient.brokerUrl, - org.apache.qpid.interop.testclient.TestClient.virtualHost); - session[0] = connection[0].createSession(false, ackMode); - - // Extract and retain the test parameters. - sendDestination = session[0].createTopic(sendKey); - - connection[0].setExceptionListener(this); - - producer = session[0].createProducer(sendDestination); - - sendUpdateDestination = session[0].createTopic(sendUpdateKey); - MessageConsumer updateConsumer = session[0].createConsumer(sendUpdateDestination); - - _rateAdapter = new SustainedRateAdapter(this); - updateConsumer.setMessageListener(_rateAdapter); - - - break; - - // Otherwise the receiver role is being assigned, so set this up to listen for messages on the required number - // of receiver connections. - case RECEIVER: - log.info("Creating Receiver"); - // Create the required number of receiver connections. - connection = new Connection[numReceivers]; - session = new Session[numReceivers]; - - for (int i = 0; i < numReceivers; i++) - { - connection[i] = - org.apache.qpid.interop.testclient.TestClient.createConnection(org.apache.qpid.interop.testclient.TestClient.DEFAULT_CONNECTION_PROPS_RESOURCE, - clientName, - org.apache.qpid.interop.testclient.TestClient.brokerUrl, - org.apache.qpid.interop.testclient.TestClient.virtualHost); - session[i] = connection[i].createSession(false, ackMode); - - sendDestination = session[i].createTopic(sendKey); - - sendUpdateDestination = session[i].createTopic(sendUpdateKey); - - MessageConsumer consumer = session[i].createConsumer(sendDestination); - - consumer.setMessageListener(new SustainedListener(clientName + "-" + i, _batchSize, session[i], sendUpdateDestination)); - } - - break; - } - - // Start all the connection dispatcher threads running. - for (int i = 0; i < connection.length; i++) - { - connection[i].start(); - } - } - - - /** Performs the test case actions. */ - public void start() throws JMSException - { - debugLog.debug("public void start(): called"); - - // Check that the sender role is being performed. - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - _rateAdapter.run(); - break; - case RECEIVER: - - } - - //return from here when you have finished the test.. this will signal the controller and - } - - public void terminate() throws JMSException, InterruptedException - { - if (_rateAdapter != null) - { - _rateAdapter.stop(); - } - } - - /** - * 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 - { - debugLog.debug("public Message getReport(Session session): called"); - - // Close the test connections. - for (int i = 0; i < connection.length; i++) - { - connection[i].close(); - } - - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - - return report; - } - - public void onException(JMSException jmsException) - { - Exception linked = jmsException.getLinkedException(); - - if (linked != null) - { - if (debugLog.isDebugEnabled()) - { - debugLog.debug("Linked Exception:" + linked); - } - if ((linked instanceof AMQNoRouteException) - || (linked instanceof AMQNoConsumersException)) - { - if (debugLog.isDebugEnabled()) - { - if (linked instanceof AMQNoConsumersException) - { - debugLog.warn("No clients currently available for message:" + ((AMQNoConsumersException) linked).getUndeliveredMessage()); - } - else - { - debugLog.warn("No route for message"); - } - } - - // Tell the rate adapter that there are no clients ready yet - _rateAdapter.NO_CLIENTS = true; - } - } - else - { - debugLog.warn("Exception:" + linked); - } - } - - /** - * Inner class that listens for messages and sends a report for the time taken between receiving the 'start' and - * 'end' messages. - */ - class SustainedListener implements MessageListener - { - /** Number of messages received */ - private long _received = 0; - /** The number of messages in the batch */ - private int _batchSize = 0; - /** Record of the when the 'start' messagse was sen */ - private Long _startTime; - /** Message producer to use to send reports */ - MessageProducer _updater; - /** Session to create the report message on */ - Session _session; - /** Record of the client ID used for this SustainedListnener */ - String _client; - - - /** - * Main Constructor - * - * @param clientname The _client id used to identify this connection. - * @param batchSize The number of messages that are to be sent per batch. Note: This is not used to - * control the interval between sending reports. - * @param session The session used for communication. - * @param sendDestination The destination that update reports should be sent to. - * - * @throws JMSException My occur if creatingthe Producer fails - */ - public SustainedListener(String clientname, int batchSize, Session session, Destination sendDestination) throws JMSException - { - _batchSize = batchSize; - _client = clientname; - _session = session; - _updater = session.createProducer(sendDestination); - } - - public void onMessage(Message message) - { - if (debugLog.isTraceEnabled()) - { - debugLog.trace("Message " + _received + "received in listener"); - } - - - if (message instanceof TextMessage) - { - try - { - _received++; - if (((TextMessage) message).getText().equals("start")) - { - debugLog.debug("Starting Batch"); - _startTime = System.nanoTime(); - } - else if (((TextMessage) message).getText().equals("end")) - { - if (_startTime != null) - { - long currentTime = System.nanoTime(); - sendStatus(currentTime - _startTime, _received, message.getIntProperty("BATCH")); - debugLog.debug("End Batch"); - } - } - } - catch (JMSException e) - { - //ignore error - } - } - - } - - /** - * sendStatus creates and sends the report back to the publisher - * - * @param time taken for the the last batch - * @param received Total number of messages received. - * @param batchNumber the batch number - * @throws JMSException if an error occurs during the send - */ - private void sendStatus(long time, long received, int batchNumber) throws JMSException - { - Message updateMessage = _session.createTextMessage("update"); - updateMessage.setStringProperty("CLIENT_ID", ":" + _client); - updateMessage.setStringProperty("CONTROL_TYPE", "UPDATE"); - updateMessage.setLongProperty("RECEIVED", received); - updateMessage.setIntProperty("BATCH", batchNumber); - updateMessage.setLongProperty("DURATION", time); - - if (debugLog.isInfoEnabled()) - { - debugLog.info("**** SENDING [" + batchNumber + "]**** " - + "CLIENT_ID:" + _client + " RECEIVED:" + received - + " BATCH:" + batchNumber + " DURATION:" + time); - } - - // Output on the main log.info the details of this batch - if (batchNumber % 10 == 0) - { - log.info("Sending Report [" + batchNumber + "] " - + "CLIENT_ID:" + _client + " RECEIVED:" + received - + " BATCH:" + batchNumber + " DURATION:" + time); - } - - _updater.send(updateMessage); - } - } - - - /** - * This class is used here to adjust the _delay value which in turn is used to control the number of messages/second - * that are sent through the test system. - * - * By keeping a record of the messages recevied and the average time taken to process the batch size can be - * calculated and so the delay can be adjusted to maintain that rate. - * - * Given that delays of < 10ms can be rounded up the delay is only used between messages if the _delay > 10ms * no - * messages in the batch. Otherwise the delay is used at the end of the batch. - */ - class SustainedRateAdapter implements MessageListener, Runnable - { - private SustainedTestClient _client; - private long _batchVariance = 3; //no. batches to allow drifting - private long _timeVariance = TEN_MILLI_SEC * 5; // no. nanos between send and report delay (10ms) - private volatile long _delay; //in nanos - private long _sent; - private Map _slowClients = new HashMap(); - private static final long PAUSE_SLEEP = TEN_MILLI_SEC / 1000; // 10 ms - private static final long NO_CLIENT_SLEEP = 1000; // 1s - private volatile boolean NO_CLIENTS = true; - private int _delayShifting; - private static final int REPORTS_WITHOUT_CHANGE = 5; - private boolean _warmedup = false; - private static final long EXPECTED_TIME_PER_BATCH = 100000L; - - SustainedRateAdapter(SustainedTestClient client) - { - _client = client; - } - - public void onMessage(Message message) - { - if (debugLog.isDebugEnabled()) - { - debugLog.debug("SustainedRateAdapter onMessage(Message message = " + message + "): called"); - } - - try - { - String controlType = message.getStringProperty("CONTROL_TYPE"); - - // Check if the message is a test invite. - if ("UPDATE".equals(controlType)) - { - NO_CLIENTS = false; - long duration = message.getLongProperty("DURATION"); - long totalReceived = message.getLongProperty("RECEIVED"); - String client = message.getStringProperty("CLIENT_ID"); - int batchNumber = message.getIntProperty("BATCH"); - - if (debugLog.isInfoEnabled() && batchNumber % DEBUG_LOG_UPATE_INTERVAL == 0) - { - debugLog.info("Update Report: CLIENT_ID:" + client + " RECEIVED:" + totalReceived - + " Recevied BATCH:" + batchNumber + " DURATION:" + duration); - } - - recordSlow(client, totalReceived, batchNumber); - - adjustDelay(client, batchNumber, duration); - - // Warm up completes when: - // we haven't warmed up - // and the number of batches sent to each client is at least half of the required warmup batches - if (!_warmedup - && (batchNumber >= _warmUpBatches)) - { - _warmedup = true; - _warmup.countDown(); - - } - } - } - catch (JMSException e) - { - // - } - } - - CountDownLatch _warmup = new CountDownLatch(1); - - int _warmUpBatches = Integer.getInteger("warmUpBatches", 10); - - int _numBatches = 10000; - - // long[] _timings = new long[_numBatches]; - private boolean _running = true; - - - public void run() - { - log.info("Warming up"); - - doBatch(_warmUpBatches); - - try - { - //wait for warmup to complete. - _warmup.await(); - - //set delay to the average length of the batches - _delay = _totalDuration / _warmUpBatches / delays.size(); - - log.info("Warmup complete delay set : " + _delay - + " based on _totalDuration: " + _totalDuration - + " over no. batches: " + _warmUpBatches - + " with client count: " + delays.size()); - - _totalDuration = 0L; - _totalReceived = 0L; - _sent = 0L; - } - catch (InterruptedException e) - { - // - } - - - doBatch(_numBatches); - - } - - private void doBatch(int batchSize) // long[] timings, - { - TextMessage testMessage = null; - try - { - testMessage = _client.session[0].createTextMessage("start"); - - - for (int batch = 0; batch <= batchSize; batch++) -// while (_running) - { - long start = System.nanoTime(); - - testMessage.setText("start"); - testMessage.setIntProperty("BATCH", batch); - - _client.producer.send(testMessage); - _rateAdapter.sentMessage(); - - testMessage.setText("test"); - //start at 2 so start and end count as part of batch - for (int m = 2; m < _batchSize; m++) - { - _client.producer.send(testMessage); - _rateAdapter.sentMessage(); - } - - testMessage.setText("end"); - _client.producer.send(testMessage); - _rateAdapter.sentMessage(); - - long end = System.nanoTime(); - - long sendtime = end - start; - - if (debugLog.isDebugEnabled()) - { - debugLog.info("Sent batch[" + batch + "](" + _batchSize + ") in " + sendtime);//timings[batch]); - } - - if (batch % LOG_UPATE_INTERVAL == 0) - { - log.info("Sent Batch[" + batch + "](" + _batchSize + ")" + status()); - } - - _rateAdapter.sleepBatch(); - - } - } - catch (JMSException e) - { - log.error("Runner ended"); - } - } - - private String status() - { - return " TotalDuration: " + _totalDuration + " for " + delays.size() + " consumers" - + " Delay is " + _delay + " resulting in " - + ((_delay > TEN_MILLI_SEC * _batchSize) ? (_delay / _batchSize) + "/msg" : _delay + "/batch"); - } - - private void sleepBatch() - { - if (checkForSlowClients()) - {//if there werwe slow clients we have already slept so don't sleep anymore again. - return; - } - - if (!SLEEP_PER_MESSAGE) - { - //per batch sleep.. if sleep is to small to spread over the batch. - if (_delay <= TEN_MILLI_SEC * _batchSize) - { - sleepLong(_delay); - } - else - { - debugLog.info("Not sleeping _delay > ten*batch is:" + _delay); - } - } - } - - public void stop() - { - _running = false; - } - - Map delays = new HashMap(); - Long _totalReceived = 0L; - Long _totalDuration = 0L; - int _skipUpdate = 0; - - /** - * Adjust the delay for sending messages based on this update from the client - * - * @param client The client that send this update - * @param duration The time taken for the last batch of messagse - * @param batchNumber The reported batchnumber from the client - */ - private void adjustDelay(String client, int batchNumber, long duration) - { - //Retrieve the current total time taken for this client. - Long currentTime = delays.get(client); - - // Add the new duration time to this client - if (currentTime == null) - { - currentTime = duration; - } - else - { - currentTime += duration; - } - - delays.put(client, currentTime); - - long batchesSent = _sent / _batchSize; - - // ensure we don't divide by zero - if (batchesSent == 0) - { - batchesSent = 1L; - } - - _totalReceived += _batchSize; - _totalDuration += duration; - - //calculate average duration accross clients per batch - long averageDuration = _totalDuration / delays.size() / batchesSent; - - //calculate the difference between current send delay and average report delay - long diff = (duration) - averageDuration; - - if (debugLog.isInfoEnabled() && batchNumber % DEBUG_LOG_UPATE_INTERVAL == 0) - { - debugLog.info("TotalDuration:" + _totalDuration + " for " + delays.size() + " consumers." - + " on batch: " + batchesSent - + " received batch: " + batchNumber - + " Batch Duration: " + duration - + " Average: " + averageDuration - + " so diff: " + diff + " for : " + client - + " Delay is " + _delay + " resulting in " - + ((_delay > TEN_MILLI_SEC * _batchSize) - ? (_delay / _batchSize) + "/msg" : _delay + "/batch")); - } - - //if the averageDuration differs from the current by more than the specified variane then adjust delay. - if (Math.abs(diff) > _timeVariance) - { - - // if the the _delay is larger than the required duration to send report - // speed up - if (diff > TEN_MILLI_SEC) - { - _delay -= TEN_MILLI_SEC; - - if (_delay < 0) - { - _delay = 0; - debugLog.info("Reset _delay to 0"); - delayStable(); - } - else - { - delayChanged(); - } - - } - else if (diff < 0) // diff < 0 diff cannot be 0 as it is > _timeVariance - { - // the report took longer - _delay += TEN_MILLI_SEC; - delayChanged(); - } - } - else - { - delayStable(); - } - - // If we have a consumer that is behind with the batches. - if (batchesSent - batchNumber > _batchVariance) - { - debugLog.debug("Increasing _delay as sending more than receiving"); - - _delay += 2 * TEN_MILLI_SEC; - delayChanged(); - } - - - } - - /** Reset the number of iterations before we say the delay has stabilised. */ - private void delayChanged() - { - _delayShifting = REPORTS_WITHOUT_CHANGE; - } - - /** - * Record the fact that delay has stabilised If delay has stablised for REPORTS_WITHOUT_CHANGE then it will - * output Delay stabilised - */ - private void delayStable() - { - _delayShifting--; - - if (_delayShifting < 0) - { - _delayShifting = 0; - log.debug("Delay stabilised:" + _delay); - } - } - - /** - * Checks that the client has received enough messages. If the client has fallen behind then they are put in the - * _slowClients lists which will increase the delay. - * - * @param client The client identifier to check - * @param received the number of messages received by that client - * @param batchNumber - */ - private void recordSlow(String client, long received, int batchNumber) - { - if (Math.abs(batchNumber - (_sent / _batchSize)) > _batchVariance) - { - _slowClients.put(client, received); - } - else - { - _slowClients.remove(client); - } - } - - /** Incrment the number of sent messages and then sleep, if required. */ - public void sentMessage() - { - - _sent++; - - if (_delay > TEN_MILLI_SEC * _batchSize) - { - long batchDelay = _delay / _batchSize; - // less than 10ms sleep doesn't always work. - // _delay is in nano seconds -// if (batchDelay < (TEN_MILLI_SEC)) -// { -// sleep(0, (int) batchDelay); -// } -// else - { -// if (batchDelay < 30000000000L) - { - sleepLong(batchDelay); - } - } - } - else - { - if (SLEEP_PER_MESSAGE && (_delay > 0)) - { - sleepLong(_delay / _batchSize); - } - } - } - - - /** - * Check at the end of each batch and pause sending messages to allow slow clients to catch up. - * - * @return true if there were slow clients that caught up. - */ - private boolean checkForSlowClients() - { - // This will allways be true as we are running this at the end of each batchSize -// if (_sent % _batchSize == 0) - { - // Cause test to pause when we have slow - if (!_slowClients.isEmpty() || NO_CLIENTS) - { - - - while (!_slowClients.isEmpty()) - { - if (debugLog.isInfoEnabled() - && _sent / _batchSize % DEBUG_LOG_UPATE_INTERVAL == 0) - { - String clients = ""; - Iterator it = _slowClients.keySet().iterator(); - while (it.hasNext()) - { - clients += it.next(); - if (it.hasNext()) - { - clients += ", "; - } - } - debugLog.info("Pausing for slow clients:" + clients); - } - - - if (log.isDebugEnabled() - && _sent / _batchSize % LOG_UPATE_INTERVAL == 0) - { - log.debug(_slowClients.size() + " slow clients."); - } - sleep(PAUSE_SLEEP); - } - - if (NO_CLIENTS) - { - sleep(NO_CLIENT_SLEEP); - } - - debugLog.debug("Continuing"); - return true; - } - else - { - if (_sent / _batchSize % LOG_UPATE_INTERVAL == 0) - { - log.info("Total Delay :" + _delay + " " - + (_delayShifting == 0 ? "Stablised" : "Not Stablised(" + _delayShifting + ")")); - } - } - - } - - return false; - } - - /** - * Sleep normally takes micro-seconds this allows the use of a nano-second value. - * - * @param delay nanoseconds to sleep for. - */ - private void sleepLong(long delay) - { - sleep(delay / 1000000, (int) (delay % 1000000)); - } - - /** - * Sleep for the specified micro-seconds. - * @param sleep microseconds to sleep for. - */ - private void sleep(long sleep) - { - sleep(sleep, 0); - } - - /** - * Perform the sleep , swallowing any InteruptException. - * - * NOTE: If a sleep request is > 10s then reset only sleep for 5s - * - * @param milli to sleep for - * @param nano sub miliseconds to sleep for - */ - private void sleep(long milli, int nano) - { - try - { - debugLog.debug("Sleep:" + milli + ":" + nano); - if (milli > 10000) - { - - if (_delay == milli) - { - _totalDuration = _totalReceived / _batchSize * EXPECTED_TIME_PER_BATCH; - debugLog.error("Sleeping for more than 10 seconds adjusted to 5s!:" + milli / 1000 + "s. Reset _totalDuration:" + _totalDuration); - } - else - { - debugLog.error("Sleeping for more than 10 seconds adjusted to 5s!:" + milli / 1000 + "s"); - } - - milli = 5000; - } - - Thread.sleep(milli, nano); - } - catch (InterruptedException e) - { - // - } - } - - public void setClient(SustainedTestClient client) - { - _client = client; - } - } - -} - diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCoordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCoordinator.java deleted file mode 100644 index 0075e45a8c..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCoordinator.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * 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.sustained; - -import org.apache.log4j.Logger; -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.interop.coordinator.ListeningCoordinatorTest; -import org.apache.qpid.interop.coordinator.TestClientDetails; -import org.apache.qpid.interop.coordinator.testcases.CoordinatingTestCase3BasicPubSub; -import org.apache.qpid.util.ConversationFactory; - -import javax.jms.Destination; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.Session; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -public class SustainedTestCoordinator extends CoordinatingTestCase3BasicPubSub implements ListeningCoordinatorTest -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(SustainedTestCoordinator.class); - private List _receivers; - private static final String SUSTAINED_KEY = "Perf_SustainedPubSub"; - Map _testProperties; - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public SustainedTestCoordinator(String name) - { - super(name); - _receivers = new LinkedList(); - } - - /** - * Adds a receiver to this test. - * - * @param receiver The contact details of the sending client in the test. - */ - public void setReceiver(TestClientDetails receiver) - { - _receivers.add(receiver); - } - - - /** - * Performs the a single test run - * - * @throws Exception if there was a problem running the test. - */ - public void testBasicPubSub() throws Exception - { - log.debug("public void testSinglePubSubCycle(): called"); - - Map testConfig = new HashMap(); - testConfig.put("TEST_NAME", "Perf_SustainedPubSub"); - testConfig.put("SUSTAINED_KEY", SUSTAINED_KEY); - testConfig.put("SUSTAINED_NUM_RECEIVERS", Integer.getInteger("numReceives", 2)); - testConfig.put("SUSTAINED_UPDATE_INTERVAL", Integer.getInteger("batchSize", 1000)); - testConfig.put("SUSTAINED_UPDATE_KEY", SUSTAINED_KEY + ".UPDATE"); - testConfig.put("ACKNOWLEDGE_MODE", Integer.getInteger("ackMode", AMQSession.AUTO_ACKNOWLEDGE)); - - log.info("Created Config: " + testConfig.entrySet().toArray()); - - sequenceTest(testConfig); - } - - /** - * 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 assigning the test roles, begining the test and gathering the test reports from the - * participants. - * - * @param testProperties The test case definition. - * - * @return The test results from the senders and receivers. - * - * @throws javax.jms.JMSException All underlying JMSExceptions are allowed to fall through. - */ - protected Message[] sequenceTest(Map testProperties) throws JMSException - { - log.debug("protected Message[] sequenceTest(Object... testProperties = " + testProperties + "): called"); - - Session session = conversationFactory.getSession(); - Destination senderControlTopic = session.createTopic(sender.privateControlKey); - - ConversationFactory.Conversation senderConversation = conversationFactory.startConversation(); - - // Assign the sender role to the sending test client. - Message assignSender = conversationFactory.getSession().createMessage(); - setPropertiesOnMessage(assignSender, testProperties); - assignSender.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); - assignSender.setStringProperty("ROLE", "SENDER"); - assignSender.setStringProperty("CLIENT_NAME", "Sustained_SENDER"); - - senderConversation.send(senderControlTopic, assignSender); - - //Assign and wait for the receiver ckuebts to be ready. - _testProperties = testProperties; - - // Wait for the senders to confirm their roles. - senderConversation.receive(); - - assignReceivers(); - - // Start the test. - Message start = session.createMessage(); - start.setStringProperty("CONTROL_TYPE", "START"); - - senderConversation.send(senderControlTopic, start); - - // Wait for the test sender to return its report. - Message senderReport = senderConversation.receive(); - - try - { - Thread.sleep(500); - } - catch (InterruptedException e) - { - } - - // Ask the receiver for its report. - Message statusRequest = session.createMessage(); - statusRequest.setStringProperty("CONTROL_TYPE", "STATUS_REQUEST"); - - - return new Message[]{senderReport}; - } - - private void assignReceivers() - { - for (TestClientDetails receiver : _receivers) - { - registerReceiver(receiver); - } - } - - private void registerReceiver(TestClientDetails receiver) - { - log.info("registerReceiver called for receiver:" + receiver); - try - { - Session session = conversationFactory.getSession(); - Destination receiverControlTopic = session.createTopic(receiver.privateControlKey); - ConversationFactory.Conversation receiverConversation = conversationFactory.startConversation(); - // Assign the receiver role the receiving client. - Message assignReceiver = session.createMessage(); - setPropertiesOnMessage(assignReceiver, _testProperties); - assignReceiver.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE"); - assignReceiver.setStringProperty("ROLE", "RECEIVER"); - assignReceiver.setStringProperty("CLIENT_NAME", "Sustained_RECEIVER_" + receiver.clientName); - - receiverConversation.send(receiverControlTopic, assignReceiver); - - //Don't wait for receiver to be ready.... we can't this is being done in - // the dispatcher thread, and most likely the acceptance message we - // want is sitting in the Dispatcher._queue waiting its turn for being - // dispatched so if we block here we won't can't get the message. - // So assume consumer is ready for action. - //receiverConversation.receive(); - } - catch (JMSException e) - { - log.warn("Unable to assign receiver:" + receiver + ". Due to:" + e.getMessage()); - } - } - - public void latejoin(Message message) - { - try - { - - TestClientDetails clientDetails = new TestClientDetails(); - clientDetails.clientName = message.getStringProperty("CLIENT_NAME"); - clientDetails.privateControlKey = message.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); - - - registerReceiver(clientDetails); - } - catch (JMSException e) - { - //swallow - } - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the interop - * testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "Perf_SustainedPubSub"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestClient.java deleted file mode 100644 index 44fc090410..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestClient.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * 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.sustained; - -import org.apache.log4j.Logger; -import org.apache.qpid.interop.testclient.InteropClientTestCase; -import org.apache.qpid.util.CommandLineParser; - -import javax.jms.JMSException; -import javax.jms.Message; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Properties; - -public class TestClient extends org.apache.qpid.interop.testclient.TestClient -{ - private static Logger log = Logger.getLogger(TestClient.class); - - /** - * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client - * identifying name. - * - * @param brokerUrl The url of the broker to connect to. - * @param virtualHost The virtual host to conect to. - * @param clientName The client name to use. - */ - public TestClient(String brokerUrl, String virtualHost, String clientName) - { - super(brokerUrl, virtualHost, clientName); - } - - /** - * The entry point for the interop test coordinator. This client accepts the following command line arguments: - * - *

-b The broker URL. Optional.
-h The virtual - * host. Optional.
-n The test client name. Optional.
name=value - * Trailing argument define name/value pairs. Added to system properties. Optional.
- * - * @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", "false"}, - {"h", "The virtual host to use.", "virtual host", "false"}, - {"n", "The test client name.", "name", "false"}, - {"j", "Join this test client to running test.", "join", ""} - }); - - // 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"); - String clientName = options.getProperty("n"); - String join = options.getProperty("j"); - - // 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, (clientName == null) ? CLIENT_NAME : clientName); - - // Use a class path scanner to find all the interop test case implementations. - Collection> testCaseClasses = - new ArrayList>(); - // ClasspathScanner.getMatches(InteropClientTestCase.class, "^TestCase.*", true); - // Hard code the test classes till the classpath scanner is fixed. - Collections.addAll(testCaseClasses, - SustainedTestClient.class); - - - try - { - client.start(testCaseClasses, join); - } - catch (Exception e) - { - log.error("The test client was unable to start.", e); - System.exit(1); - } - } - - protected void start(Collection> testCaseClasses, String join) throws JMSException, ClassNotFoundException - { - super.start(testCaseClasses); - log.debug("private void start(): called"); - - if (join != null && !join.equals("")) - { - Message latejoin = session.createMessage(); - - try - { - Object test = Class.forName(join).newInstance(); - if (test instanceof InteropClientTestCase) - { - currentTestCase = (InteropClientTestCase) test; - } - else - { - throw new RuntimeException("Requested to join class '" + join + "' but this is not a InteropClientTestCase."); - } - - latejoin.setStringProperty("CONTROL_TYPE", "LATEJOIN"); - latejoin.setStringProperty("CLIENT_NAME", clientName); - latejoin.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); - producer.send(session.createTopic("iop.control.test." + currentTestCase.getName()), latejoin); - } - catch (InstantiationException e) - { - log.warn("Unable to request latejoining of test:" + currentTestCase); - } - catch (IllegalAccessException e) - { - log.warn("Unable to request latejoining of test:" + currentTestCase); - } - } - } - -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestCoordinator.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestCoordinator.java deleted file mode 100644 index 7e12fe39fb..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/TestCoordinator.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.sustained; - -import org.apache.qpid.interop.coordinator.Coordinator; -import org.apache.qpid.interop.coordinator.ListeningTestDecorator; -import org.apache.qpid.interop.coordinator.TestClientDetails; -import org.apache.qpid.util.CommandLineParser; -import org.apache.qpid.util.ConversationFactory; -import org.apache.log4j.Logger; - -import java.util.Properties; -import java.util.Set; - -import junit.framework.TestResult; -import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; - -import javax.jms.Connection; - -public class TestCoordinator extends Coordinator -{ - - private static final Logger log = Logger.getLogger(TestCoordinator.class); - - /** - * 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 null. - */ - TestCoordinator(String brokerUrl, String virtualHost) - { - super(brokerUrl, virtualHost); - } - - protected WrappedSuiteTestDecorator newTestDecorator(WrappedSuiteTestDecorator targetTest, Set enlistedClients, ConversationFactory conversationFactory, Connection connection) - { - return new ListeningTestDecorator(targetTest, enlistedClients, conversationFactory, connection); - } - - - /** - * The entry point for the interop test coordinator. This client accepts the following command line arguments: - * - *

-b The broker URL. Mandatory.
-h The virtual host. - * Optional.
name=value Trailing argument define name/value pairs. Added to system properties. - * Optional.
- * - * @param args The command line arguments. - */ - public static void main(String[] args) - { - try - { - // Use the command line parser to evaluate the command line with standard handling behaviour (print errors - // and usage then exist if there are errors). - Properties options = - CommandLineParser.processCommandLine(args, - new CommandLineParser( - new String[][] - { - {"b", "The broker URL.", "broker", "false"}, - {"h", "The virtual host to use.", "virtual host", "false"}, - {"o", "The name of the directory to output test timings to.", "dir", "false"} - })); - - // Extract the command line options. - String brokerUrl = options.getProperty("b"); - String virtualHost = options.getProperty("h"); - String reportDir = options.getProperty("o"); - reportDir = (reportDir == null) ? "." : reportDir; - - - String[] testClassNames = {SustainedTestCoordinator.class.getName()}; - - // Create a coordinator and begin its test procedure. - Coordinator coordinator = new TestCoordinator(brokerUrl, virtualHost); - - coordinator.setReportDir(reportDir); - - TestResult testResult = coordinator.start(testClassNames); - - if (testResult.failureCount() > 0) - { - System.exit(FAILURE_EXIT); - } - else - { - System.exit(SUCCESS_EXIT); - } - } - catch (Exception e) - { - System.err.println(e.getMessage()); - log.error("Top level handler caught execption.", e); - System.exit(EXCEPTION_EXIT); - } - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java new file mode 100644 index 0000000000..56cc0dbd98 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java @@ -0,0 +1,464 @@ +/* + * + * 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.test.framework.distributedtesting; + +import org.apache.log4j.Logger; +import org.apache.log4j.NDC; + +import org.apache.qpid.interop.clienttestcases.TestCase1DummyRun; +import org.apache.qpid.interop.clienttestcases.TestCase2BasicP2P; +import org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub; +import org.apache.qpid.sustained.SustainedClientTestCase; +import org.apache.qpid.test.framework.MessagingTestConfigProperties; +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.clocksynch.ClockSynchThread; +import org.apache.qpid.test.framework.clocksynch.ClockSynchronizer; +import org.apache.qpid.test.framework.clocksynch.UDPClockSynchronizer; +import org.apache.qpid.test.framework.distributedcircuit.TestClientCircuitEnd; + +import uk.co.thebadgerset.junit.extensions.SleepThrottle; +import uk.co.thebadgerset.junit.extensions.Throttle; +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; +import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; + +import javax.jms.*; + +import java.util.*; + +/** + * 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 Coordinator}. + * + *

+ *
Messages Handled by SustainedTestClient
Message Action + *
Invite(compulsory) Reply with Enlist. + *
Invite(test case) Reply with Enlist if test case available. + *
AssignRole(test case) Reply with Accept Role if matches an enlisted test. Keep test parameters. + *
Start Send test messages defined by test parameters. Send report on messages sent. + *
Status Request Send report on messages received. + *
Terminate Terminate the test client. + *
ClockSynch Synch clock against the supplied UDP address. + *
+ * + *

+ *
CRC Card
Responsibilities Collaborations + *
Handle all incoming control messages. {@link TestClientControlledTest} + *
Configure and look up test cases by name. {@link TestClientControlledTest} + *
+ */ +public class TestClient implements MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestClient.class); + + /** Used for reporting to the console. */ + private static final Logger console = Logger.getLogger("CONSOLE"); + + /** Holds the default identifying name of the test client. */ + public static final String CLIENT_NAME = "java"; + + /** Holds the URL of the broker to run the tests on. */ + public static String brokerUrl; + + /** Holds the virtual host to run the tests on. If null, then the default virtual host is used. */ + public static String virtualHost; + + /** + * Holds the test context properties that provides the default test parameters, plus command line overrides. + * This is initialized with the default test parameters, to which command line overrides may be applied. + */ + public static ParsedProperties testContextProperties = + TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + /** Holds all the test cases loaded from the classpath. */ + Map testCases = new HashMap(); + + /** Holds the test case currently being run by this client. */ + protected TestClientControlledTest currentTestCase; + + /** Holds the connection to the broker that the test is being coordinated on. */ + protected Connection connection; + + /** Holds the message producer to hold the test coordination over. */ + protected MessageProducer producer; + + /** Holds the JMS controlSession for the test coordination. */ + protected Session session; + + /** Holds the name of this client, with a default value. */ + protected String clientName = CLIENT_NAME; + + /** This flag indicates that the test client should attempt to join the currently running test case on start up. */ + protected boolean join; + + /** Holds the clock synchronizer for the test node. */ + ClockSynchThread clockSynchThread; + + /** + * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client + * identifying name. + * + * @param brokerUrl The url of the broker to connect to. + * @param virtualHost The virtual host to conect to. + * @param clientName The client name to use. + * @param join Flag to indicate that this client should attempt to join running tests. + */ + public TestClient(String brokerUrl, String virtualHost, String clientName, boolean join) + { + log.debug("public TestClient(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost + + ", String clientName = " + clientName + ", boolean join = " + join + "): called"); + + // Retain the connection parameters. + this.brokerUrl = brokerUrl; + this.virtualHost = virtualHost; + this.clientName = clientName; + this.join = join; + } + + /** + * The entry point for the interop test coordinator. This client accepts the following command line arguments: + * + *

+ *
-b The broker URL. Optional. + *
-h The virtual host. Optional. + *
-n The test client name. Optional. + *
name=value Trailing argument define name/value pairs. Added to system properties. Optional. + *
+ * + * @param args The command line arguments. + */ + public static void main(String[] args) + { + log.debug("public static void main(String[] args = " + Arrays.toString(args) + "): called"); + console.info("Qpid Distributed Test Client."); + + // Override the default broker url to be localhost:5672. + testContextProperties.setProperty(MessagingTestConfigProperties.BROKER_PROPNAME, "tcp://localhost:5672"); + + // Use the command line parser to evaluate the command line with standard handling behaviour (print errors + // and usage then exist if there are errors). + // Any options and trailing name=value pairs are also injected into the test context properties object, + // to override any defaults that may have been set up. + ParsedProperties options = + new ParsedProperties(uk.co.thebadgerset.junit.extensions.util.CommandLineParser.processCommandLine(args, + new uk.co.thebadgerset.junit.extensions.util.CommandLineParser( + new String[][] + { + { "b", "The broker URL.", "broker", "false" }, + { "h", "The virtual host to use.", "virtual host", "false" }, + { "o", "The name of the directory to output test timings to.", "dir", "false" }, + { "n", "The name of the test client.", "name", "false" }, + { "j", "Join this test client to running test.", "false" } + }), testContextProperties)); + + // Extract the command line options. + String brokerUrl = options.getProperty("b"); + String virtualHost = options.getProperty("h"); + String clientName = options.getProperty("n"); + clientName = (clientName == null) ? CLIENT_NAME : clientName; + boolean join = options.getPropertyAsBoolean("j"); + + // To distinguish logging output set up an NDC on the client name. + NDC.push(clientName); + + // Create a test client and start it running. + TestClient client = new TestClient(brokerUrl, virtualHost, clientName, join); + + // Use a class path scanner to find all the interop test case implementations. + // Hard code the test classes till the classpath scanner is fixed. + Collection> testCaseClasses = + new ArrayList>(); + // ClasspathScanner.getMatches(TestClientControlledTest.class, "^TestCase.*", true); + Collections.addAll(testCaseClasses, TestCase1DummyRun.class, TestCase2BasicP2P.class, TestCase3BasicPubSub.class, + SustainedClientTestCase.class, TestClientCircuitEnd.class); + + try + { + client.start(testCaseClasses); + } + catch (Exception e) + { + log.error("The test client was unable to start.", e); + console.info(e.getMessage()); + System.exit(1); + } + } + + /** + * Starts the interop test client running. This causes it to start listening for incoming test invites. + * + * @param testCaseClasses The classes of the available test cases. The test case names from these are used to + * matchin incoming test invites against. + * + * @throws JMSException Any underlying JMSExceptions are allowed to fall through. + */ + protected void start(Collection> testCaseClasses) throws JMSException + { + log.debug("protected void start(Collection> testCaseClasses = " + + testCaseClasses + "): called"); + + // Create all the test case implementations and index them by the test names. + for (Class nextClass : testCaseClasses) + { + try + { + TestClientControlledTest testCase = nextClass.newInstance(); + testCases.put(testCase.getName(), testCase); + } + catch (InstantiationException e) + { + log.warn("Could not instantiate test case class: " + nextClass.getName(), e); + // Ignored. + } + catch (IllegalAccessException e) + { + log.warn("Could not instantiate test case class due to illegal access: " + nextClass.getName(), e); + // Ignored. + } + } + + // Open a connection to communicate with the coordinator on. + connection = TestUtils.createConnection(testContextProperties); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Set this up to listen for control messages. + Topic privateControlTopic = session.createTopic("iop.control." + clientName); + MessageConsumer consumer = session.createConsumer(privateControlTopic); + consumer.setMessageListener(this); + + Topic controlTopic = session.createTopic("iop.control"); + MessageConsumer consumer2 = session.createConsumer(controlTopic); + consumer2.setMessageListener(this); + + // Create a producer to send replies with. + producer = session.createProducer(null); + + // If the join flag was set, then broadcast a join message to notify the coordinator that a new test client + // is available to join the current test case, if it supports it. This message may be ignored, or it may result + // in this test client receiving a test invite. + if (join) + { + Message joinMessage = session.createMessage(); + + joinMessage.setStringProperty("CONTROL_TYPE", "JOIN"); + joinMessage.setStringProperty("CLIENT_NAME", clientName); + joinMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); + producer.send(controlTopic, joinMessage); + } + + // Start listening for incoming control messages. + connection.start(); + } + + /** + * Handles all incoming control messages. + * + * @param message The incoming message. + */ + public void onMessage(Message message) + { + NDC.push(clientName); + log.debug("public void onMessage(Message message = " + message + "): called"); + + try + { + String controlType = message.getStringProperty("CONTROL_TYPE"); + String testName = message.getStringProperty("TEST_NAME"); + + log.debug("Received control of type '" + controlType + "' for the test '" + testName + "'"); + + // Check if the message is a test invite. + if ("INVITE".equals(controlType)) + { + // Flag used to indicate that an enlist should be sent. Only enlist to compulsory invites or invites + // for which test cases exist. + boolean enlist = false; + + if (testName != null) + { + log.debug("Got an invite to test: " + testName); + + // Check if the requested test case is available. + TestClientControlledTest testCase = testCases.get(testName); + + if (testCase != null) + { + log.debug("Found implementing class for test '" + testName + "', enlisting for it."); + + // Check if the test case will accept the invitation. + enlist = testCase.acceptInvite(message); + + log.debug("The test case " + + (enlist ? " accepted the invite, enlisting for it." + : " did not accept the invite, not enlisting.")); + + // Make the requested test case the current test case. + currentTestCase = testCase; + } + else + { + log.debug("Received an invite to the test '" + testName + "' but this test is not known."); + } + } + else + { + log.debug("Got a compulsory invite, enlisting for it."); + + enlist = true; + } + + if (enlist) + { + // Reply with the client name in an Enlist message. + Message enlistMessage = session.createMessage(); + enlistMessage.setStringProperty("CONTROL_TYPE", "ENLIST"); + enlistMessage.setStringProperty("CLIENT_NAME", clientName); + enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); + enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.debug("Sending enlist message '" + enlistMessage + "' to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), enlistMessage); + } + else + { + // Reply with the client name in an Decline message. + Message enlistMessage = session.createMessage(); + enlistMessage.setStringProperty("CONTROL_TYPE", "DECLINE"); + enlistMessage.setStringProperty("CLIENT_NAME", clientName); + enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); + enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.debug("Sending decline message '" + enlistMessage + "' to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), enlistMessage); + } + } + else if ("ASSIGN_ROLE".equals(controlType)) + { + // Assign the role to the current test case. + String roleName = message.getStringProperty("ROLE"); + + log.debug("Got a role assignment to role: " + roleName); + + TestClientControlledTest.Roles role = Enum.valueOf(TestClientControlledTest.Roles.class, roleName); + + currentTestCase.assignRole(role, message); + + // Reply by accepting the role in an Accept Role message. + Message acceptRoleMessage = session.createMessage(); + acceptRoleMessage.setStringProperty("CLIENT_NAME", clientName); + acceptRoleMessage.setStringProperty("CONTROL_TYPE", "ACCEPT_ROLE"); + acceptRoleMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.debug("Sending accept role message '" + acceptRoleMessage + "' to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), acceptRoleMessage); + } + else if ("START".equals(controlType) || "STATUS_REQUEST".equals(controlType)) + { + if ("START".equals(controlType)) + { + log.debug("Got a start notification."); + + // Extract the number of test messages to send from the start notification. + int numMessages; + + try + { + numMessages = message.getIntProperty("MESSAGE_COUNT"); + } + catch (NumberFormatException e) + { + // If the number of messages is not specified, use the default of one. + numMessages = 1; + } + + // Start the current test case. + currentTestCase.start(numMessages); + } + else + { + log.debug("Got a status request."); + } + + // Generate the report from the test case and reply with it as a Report message. + Message reportMessage = currentTestCase.getReport(session); + reportMessage.setStringProperty("CLIENT_NAME", clientName); + reportMessage.setStringProperty("CONTROL_TYPE", "REPORT"); + reportMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.debug("Sending report message '" + reportMessage + "' to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), reportMessage); + } + else if ("TERMINATE".equals(controlType)) + { + console.info("Received termination instruction from coordinator."); + + // Is a cleaner shutdown needed? + connection.close(); + System.exit(0); + } + else if ("CLOCK_SYNCH".equals(controlType)) + { + log.debug("Received clock synch command."); + String address = message.getStringProperty("ADDRESS"); + + log.debug("address = " + address); + + // Re-create (if necessary) and start the clock synch thread to synch the clock every ten seconds. + if (clockSynchThread != null) + { + clockSynchThread.terminate(); + } + + SleepThrottle throttle = new SleepThrottle(); + throttle.setRate(0.1f); + + clockSynchThread = new ClockSynchThread(new UDPClockSynchronizer(address), throttle); + clockSynchThread.start(); + } + else + { + // Log a warning about this but otherwise ignore it. + log.warn("Got an unknown control message, controlType = " + controlType + ", message = " + message); + } + } + catch (JMSException e) + { + // Log a warning about this, but otherwise ignore it. + log.warn("Got JMSException whilst handling message: " + message, e); + } + // Log any runtimes that fall through this message handler. These are fatal errors for the test client. + catch (RuntimeException e) + { + log.error("The test client message handler got an unhandled exception: ", e); + console.info("The message handler got an unhandled exception, terminating the test client."); + System.exit(1); + } + finally + { + NDC.pop(); + } + } +} -- cgit v1.2.1 From 901073e4e0662b693219c78bc56bebeddbc0bee0 Mon Sep 17 00:00:00 2001 From: Rajith Muditha Attapattu Date: Fri, 25 Jan 2008 16:46:49 +0000 Subject: Changed version to M3 to avoid confusion with old M2 snapshots git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@615264 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/pom.xml b/qpid/java/integrationtests/pom.xml index 89fd5ede28..78a05e4f28 100644 --- a/qpid/java/integrationtests/pom.xml +++ b/qpid/java/integrationtests/pom.xml @@ -24,13 +24,13 @@ org.apache.qpid qpid-integrationtests jar - 1.0-incubating-M2-SNAPSHOT + 1.0-incubating-M3-SNAPSHOT Qpid Integration Tests org.apache.qpid qpid - 1.0-incubating-M2-SNAPSHOT + 1.0-incubating-M3-SNAPSHOT ../pom.xml -- cgit v1.2.1 From 1ea7ba4dedb90c0d697b544053bf5c617427a725 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 1 Feb 2008 01:03:46 +0000 Subject: ant build system git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@617320 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/build.xml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 qpid/java/integrationtests/build.xml (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/build.xml b/qpid/java/integrationtests/build.xml new file mode 100644 index 0000000000..c37dd4a893 --- /dev/null +++ b/qpid/java/integrationtests/build.xml @@ -0,0 +1,28 @@ + + + + + + + + + -- cgit v1.2.1 From d8988a8acd974f58545bacb52496bb9dcc9fae6d Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Wed, 23 Apr 2008 23:50:34 +0000 Subject: Delete stuff that's just going to get synced from M2.x git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@651111 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/README.txt | 13 - qpid/java/integrationtests/build.xml | 28 - .../docs/RunningSustainedTests.txt | 15 - .../integrationtests/jar-with-dependencies.xml | 47 -- qpid/java/integrationtests/pom.xml | 147 ---- .../interop/clienttestcases/TestCase1DummyRun.java | 135 --- .../interop/clienttestcases/TestCase2BasicP2P.java | 209 ----- .../clienttestcases/TestCase3BasicPubSub.java | 239 ------ .../testcases/InteropTestCase1DummyRun.java | 84 -- .../testcases/InteropTestCase2BasicP2P.java | 90 -- .../testcases/InteropTestCase3BasicPubSub.java | 88 -- .../qpid/sustained/SustainedClientTestCase.java | 906 --------------------- .../apache/qpid/sustained/SustainedTestCase.java | 126 --- .../framework/distributedtesting/TestClient.java | 464 ----------- .../org/apache/qpid/util/ClasspathScanner.java | 234 ------ .../org/apache/qpid/util/ConversationFactory.java | 479 ----------- .../org/apache/qpid/interop/connection.properties | 20 - .../src/resources/sustained-log4j.xml | 69 -- 18 files changed, 3393 deletions(-) delete mode 100644 qpid/java/integrationtests/README.txt delete mode 100644 qpid/java/integrationtests/build.xml delete mode 100644 qpid/java/integrationtests/docs/RunningSustainedTests.txt delete mode 100644 qpid/java/integrationtests/jar-with-dependencies.xml delete mode 100644 qpid/java/integrationtests/pom.xml delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java delete mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationFactory.java delete mode 100644 qpid/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties delete mode 100644 qpid/java/integrationtests/src/resources/sustained-log4j.xml (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/README.txt b/qpid/java/integrationtests/README.txt deleted file mode 100644 index 00a21883a9..0000000000 --- a/qpid/java/integrationtests/README.txt +++ /dev/null @@ -1,13 +0,0 @@ -This module contains integration tests, for testing a java client againt *any* broker -implementation or against other clients. These tests must not rely on starting the -Java broker in-vm but must depend on a broker being started independantly before running -the tests in this module. By default tests in this module will expect the broker to be -started on localhost on the default port, but this can be overridden by passing in a -sys property to maven. Interop tests are in this module. Java broker specific tests that -use an in-vm broker should go in the systests module. - -Don't set the tests in this module to run by default as part of the maven build, until -there is a script to start and stop the broker; needed to fully automate these tests. -Interop tests will always be run using a seperate script (not from maven) but it might -be worthwile to script into the maven build starting of the Java broker, and running -these tests against it. \ No newline at end of file diff --git a/qpid/java/integrationtests/build.xml b/qpid/java/integrationtests/build.xml deleted file mode 100644 index c37dd4a893..0000000000 --- a/qpid/java/integrationtests/build.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - diff --git a/qpid/java/integrationtests/docs/RunningSustainedTests.txt b/qpid/java/integrationtests/docs/RunningSustainedTests.txt deleted file mode 100644 index 2b37f4c5a7..0000000000 --- a/qpid/java/integrationtests/docs/RunningSustainedTests.txt +++ /dev/null @@ -1,15 +0,0 @@ -In addition to the integration tests the framework provided by this package also allows for -sustained tests to be run. Currently avaible tests: -- org.apache.qpid.sustained.SustainedTestClient : Pub Sub test to determine steady state throughput. - - -Running Tests. - -Run the tests as per the integration tests. - - Start a broker - - Start at least one Client [java org.apache.qpid.sustained.TestClient], ensuring unique naming - - Start Test Controller [java org.apache.qpid.sustained.TestCoordinator] - - Additional Test clients can be started: - [java org.apache.qpid.sustained.TestClient -j org.apache.qpid.sustained.SustainedTestClient] - - diff --git a/qpid/java/integrationtests/jar-with-dependencies.xml b/qpid/java/integrationtests/jar-with-dependencies.xml deleted file mode 100644 index 3e95e7ab22..0000000000 --- a/qpid/java/integrationtests/jar-with-dependencies.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - all-test-deps - - jar - - false - - - - - true - test - - - - - target/classes - - - - target/test-classes - - - - diff --git a/qpid/java/integrationtests/pom.xml b/qpid/java/integrationtests/pom.xml deleted file mode 100644 index 78a05e4f28..0000000000 --- a/qpid/java/integrationtests/pom.xml +++ /dev/null @@ -1,147 +0,0 @@ - - - - 4.0.0 - org.apache.qpid - qpid-integrationtests - jar - 1.0-incubating-M3-SNAPSHOT - Qpid Integration Tests - - - org.apache.qpid - qpid - 1.0-incubating-M3-SNAPSHOT - ../pom.xml - - - - .. - - - - - - org.apache.qpid - qpid-client - - - - org.apache.qpid - qpid-systests - - - - org.slf4j - slf4j-log4j12 - 1.4.0 - - - - uk.co.thebadgerset - junit-toolkit - 0.6-SNAPSHOT - compile - - - - - junit - junit - compile - - - - - net.sf.retrotranslator - retrotranslator-runtime - provided - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - - - org.codehaus.mojo - retrotranslator-maven-plugin - - - retro-intergration-tests - package - - translate-project - - - - ${project.build.directory}/${project.build.finalName}-java14.jar - ${retrotranslator.verify} - - ${retrotranslator.1.4-rt-path} - ${retrotranslator.1.4-jce-path} - ${retrotranslator.1.4-jsse-path} - ${retrotranslator.1.4-sasl-path} - - false - java14 - true - - - - - - - org.apache.maven.plugins - maven-assembly-plugin - 2.2-SNAPSHOT - - - jar-with-dependencies.xml - - target - target/assembly/work - - - - - - - - - - false - src/resources - - **/* - - - - - - - diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java deleted file mode 100644 index db17c7aacc..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * - * 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.clienttestcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; - -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.Session; - -/** - * 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. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Perform test case actions. - *
Generate test reports. - *
- */ -public class TestCase1DummyRun implements TestClientControlledTest -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase1DummyRun.class); - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "TC1_DummyRun"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage): called"); - - // Test parameters don't matter, accept all invites. - 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 receivers. - * @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 - { - log.debug("public void assignRole(Roles role, Message assignRoleMessage): called"); - - // Do nothing, both roles are the same. - } - - /** - * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. - * - * @param numMessages The number of test messages to send. - */ - public void start(int numMessages) - { - log.debug("public void start(): called"); - - // Do nothing. - } - - /** - * Gets a report on the actions performed by the test case in its assigned role. - * - * @param session The controlSession 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 - { - log.debug("public Message getReport(Session controlSession): called"); - - // Generate a dummy report, the coordinator expects a report but doesn't care what it is. - return session.createTextMessage("Dummy Run, Ok."); - } - - /** - * Handles incoming test messages. Does nothing. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Ignore any messages. - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java deleted file mode 100644 index 36d3cce7f7..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * - * 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.clienttestcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.TestUtils; -import org.apache.qpid.test.framework.distributedtesting.TestClient; -import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; - -import javax.jms.*; - -/** - * 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. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Send required number of test messages. - *
Generate test reports. - *
- */ -public class TestCase2BasicP2P implements TestClientControlledTest, MessageListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase2BasicP2P.class); - - /** Holds the count of test messages received. */ - private int messageCount; - - /** The role to be played by the test. */ - private Roles role; - - /** The number of test messages to send. */ - private int numMessages; - - /** The connection to send the test messages on. */ - private Connection connection; - - /** The controlSession to send the test messages on. */ - private Session session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "TC2_BasicP2P"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); - - // 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 receivers. - * - * @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 - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Reset the message count for a new test. - messageCount = 0; - - // Take note of the role to be played. - this.role = role; - - // Create a new connection to pass the test messages on. - connection = TestUtils.createConnection(TestClient.testContextProperties); - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Extract and retain the test parameters. - numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); - Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); - - log.debug("numMessages = " + numMessages); - log.debug("sendDestination = " + sendDestination); - log.debug("role = " + role); - - switch (role) - { - // Check if the sender role is being assigned, and set up a message producer if so. - case SENDER: - producer = session.createProducer(sendDestination); - break; - - // Otherwise the receivers role is being assigned, so set this up to listen for messages. - case RECEIVER: - MessageConsumer consumer = session.createConsumer(sendDestination); - consumer.setMessageListener(this); - break; - } - - connection.start(); - } - - /** - * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. - * - * @param numMessages The number of test messages to send. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void start(int numMessages) throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - if (role.equals(Roles.SENDER)) - { - Message testMessage = session.createTextMessage("test"); - - for (int i = 0; i < this.numMessages; i++) - { - producer.send(testMessage); - - // Increment the message count. - messageCount++; - } - } - } - - /** - * Gets a report on the actions performed by the test case in its assigned role. - * - * @param session The controlSession 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 - { - log.debug("public Message getReport(Session controlSession): called"); - - // Close the test connection. - connection.close(); - - // Generate a report message containing the count of the number of messages passed. - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - report.setIntProperty("MESSAGE_COUNT", messageCount); - - return report; - } - - /** - * Counts incoming test messages. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Increment the message count. - messageCount++; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java deleted file mode 100644 index 205472716b..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * - * 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.clienttestcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.TestUtils; -import org.apache.qpid.test.framework.distributedtesting.TestClient; -import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; - -import javax.jms.*; - -/** - * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the - * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of - * messages sent/received. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Send required number of test messages using pub/sub. - *
Generate test reports. - *
- */ -public class TestCase3BasicPubSub implements TestClientControlledTest, MessageListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase3BasicPubSub.class); - - /** Holds the count of test messages received. */ - private int messageCount; - - /** The role to be played by the test. */ - private Roles role; - - /** The number of test messages to send. */ - private int numMessages; - - /** The connections to send/receive the test messages on. */ - private Connection[] connection; - - /** The sessions to send/receive the test messages on. */ - private Session[] session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "TC3_BasicPubSub"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); - - // 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 receivers. - * - * @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 - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Reset the message count for a new test. - messageCount = 0; - - // Take note of the role to be played. - this.role = role; - - // Extract and retain the test parameters. - numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); - int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); - String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); - - log.debug("numMessages = " + numMessages); - log.debug("numReceivers = " + numReceivers); - log.debug("sendKey = " + sendKey); - log.debug("role = " + role); - - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - // Create a new connection to pass the test messages on. - connection = new Connection[1]; - session = new Session[1]; - - connection[0] = TestUtils.createConnection(TestClient.testContextProperties); - session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Extract and retain the test parameters. - Destination sendDestination = session[0].createTopic(sendKey); - - producer = session[0].createProducer(sendDestination); - break; - - // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number - // of receivers connections. - case RECEIVER: - // Create the required number of receivers connections. - connection = new Connection[numReceivers]; - session = new Session[numReceivers]; - - for (int i = 0; i < numReceivers; i++) - { - connection[i] = TestUtils.createConnection(TestClient.testContextProperties); - session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); - - sendDestination = session[i].createTopic(sendKey); - - MessageConsumer consumer = session[i].createConsumer(sendDestination); - consumer.setMessageListener(this); - } - - break; - } - - // Start all the connection dispatcher threads running. - for (Connection conn : connection) - { - conn.start(); - } - } - - /** - * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. - * - * @param numMessages The number of test messages to send. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void start(int numMessages) throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - if (role.equals(Roles.SENDER)) - { - Message testMessage = session[0].createTextMessage("test"); - - for (int i = 0; i < this.numMessages; i++) - { - producer.send(testMessage); - - // Increment the message count. - messageCount++; - } - } - } - - /** - * Gets a report on the actions performed by the test case in its assigned role. - * - * @param session The controlSession 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 - { - log.debug("public Message getReport(Session controlSession): called"); - - // Close the test connections. - for (Connection conn : connection) - { - conn.close(); - } - - // Generate a report message containing the count of the number of messages passed. - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - report.setIntProperty("MESSAGE_COUNT", messageCount); - - return report; - } - - /** - * Counts incoming test messages. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Increment the message count. - messageCount++; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java deleted file mode 100644 index a2e4a00aa6..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * - * 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.testcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.FrameworkBaseCase; - -import java.util.Properties; - -/** - * Coordinates test case 1, from the interop test specification. This test connects up the sender and receivers roles, - * and gets some dummy test reports from them, in order to check that the test framework itself is operational. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Exercises the interop testing framework without actually sending any test messages. - * {@link FrameworkBaseCase} - *
- */ -public class InteropTestCase1DummyRun extends FrameworkBaseCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(InteropTestCase1DummyRun.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public InteropTestCase1DummyRun(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testDummyRun() throws Exception - { - log.debug("public void testDummyRun(): called"); - - Properties testConfig = new Properties(); - testConfig.put("TEST_NAME", "TC1_DummyRun"); - - /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); - - // Compare sender and receivers reports. - // Assert.assertEquals("Expected to get 2 dummy reports.", 2, reports.length); - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC1_DummyRun"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java deleted file mode 100644 index 6d6515f1fd..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * - * 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.testcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.FrameworkBaseCase; - -import java.util.Properties; - -/** - * Implements test case 2, from the interop test specification. This test sets up the TC2_BasicP2P test for 50 - * messages. It checks that the sender and receivers reports both indicate that all the test messages were transmitted - * successfully. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Setup p2p test parameters and compare with test output. {@link FrameworkBaseCase} - *
- */ -public class InteropTestCase2BasicP2P extends FrameworkBaseCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(InteropTestCase2BasicP2P.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public InteropTestCase2BasicP2P(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testBasicP2P() throws Exception - { - log.debug("public void testBasicP2P(): called"); - - Properties testConfig = new Properties(); - testConfig.setProperty("TEST_NAME", "TC2_BasicP2P"); - testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); - testConfig.put("P2P_NUM_MESSAGES", 50); - - /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); - - // Compare sender and receivers reports. - /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); - int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); - - Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); - Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived);*/ - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC2_BasicP2P"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java deleted file mode 100644 index 2faca91e73..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * - * 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.testcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.FrameworkBaseCase; - -import java.util.Properties; - -/** - *

- *
CRC Card
Responsibilities Collaborations - *
Setup pub/sub test parameters and compare with test output. {@link FrameworkBaseCase} - *
- */ -public class InteropTestCase3BasicPubSub extends FrameworkBaseCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(InteropTestCase3BasicPubSub.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public InteropTestCase3BasicPubSub(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testBasicPubSub() throws Exception - { - log.debug("public void testBasicPubSub(): called"); - - Properties testConfig = new Properties(); - testConfig.put("TEST_NAME", "TC3_BasicPubSub"); - testConfig.put("PUBSUB_KEY", "tc3route"); - testConfig.put("PUBSUB_NUM_MESSAGES", 10); - testConfig.put("PUBSUB_NUM_RECEIVERS", 5); - - /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); - - // Compare sender and receivers reports. - /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); - int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); - - Assert.assertEquals("The requested number of messages were not sent.", 10, messagesSent); - Assert.assertEquals("Received messages did not match up to num sent * num receivers.", messagesSent * 5, - messagesReceived);*/ - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC3_BasicPubSub"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java deleted file mode 100644 index 642f91b772..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java +++ /dev/null @@ -1,906 +0,0 @@ -/* - * - * 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.sustained; - -import org.apache.log4j.Logger; - -import org.apache.qpid.client.AMQNoConsumersException; -import org.apache.qpid.client.AMQNoRouteException; -import org.apache.qpid.test.framework.distributedtesting.TestClient; -import org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub; -import org.apache.qpid.test.framework.TestUtils; - -import javax.jms.Connection; -import javax.jms.Destination; -import javax.jms.ExceptionListener; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageConsumer; -import javax.jms.MessageListener; -import javax.jms.MessageProducer; -import javax.jms.Session; -import javax.jms.TextMessage; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.CountDownLatch; - -/** - * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the - * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of - * messages sent/received. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Send required number of test messages using pub/sub.
Generate test reports. - *
- */ -public class SustainedClientTestCase extends TestCase3BasicPubSub implements ExceptionListener, MessageListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(SustainedClientTestCase.class); - - /** Used to log to the console. */ - private static final Logger console = Logger.getLogger("SustainedTest"); - - /** The role to be played by the test. */ - private Roles role; - - /** The number of receivers connection to use. */ - private int numReceivers; - - /** The routing key to send them to on the default direct exchange. */ - private Destination sendDestination; - - /** The routing key to send updates to on the default direct exchange. */ - private Destination sendUpdateDestination; - - /** The connections to send/receive the test messages on. */ - private Connection[] connection; - - /** The sessions to send/receive the test messages on. */ - private Session[] session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** Adapter that adjusts the send rate based on the updates from clients. */ - SustainedRateAdapter _rateAdapter; - - /** */ - int _batchSize; - - private static final long TEN_MILLI_SEC = 10000000; - private static final int DEBUG_LOG_UPATE_INTERVAL = 10; - private static final int LOG_UPATE_INTERVAL = 10; - private static final boolean SLEEP_PER_MESSAGE = Boolean.getBoolean("sleepPerMessage"); - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "Perf_SustainedPubSub"; - } - - /** - * 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 receivers. - * @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 - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Take note of the role to be played. - this.role = role; - - // Extract and retain the test parameters. - numReceivers = assignRoleMessage.getIntProperty("SUSTAINED_NUM_RECEIVERS"); - _batchSize = assignRoleMessage.getIntProperty("SUSTAINED_UPDATE_INTERVAL"); - String sendKey = assignRoleMessage.getStringProperty("SUSTAINED_KEY"); - String sendUpdateKey = assignRoleMessage.getStringProperty("SUSTAINED_UPDATE_KEY"); - int ackMode = assignRoleMessage.getIntProperty("ACKNOWLEDGE_MODE"); - String clientName = assignRoleMessage.getStringProperty("CLIENT_NAME"); - - if (log.isDebugEnabled()) - { - log.debug("numReceivers = " + numReceivers); - log.debug("_batchSize = " + _batchSize); - log.debug("ackMode = " + ackMode); - log.debug("sendKey = " + sendKey); - log.debug("sendUpdateKey = " + sendUpdateKey); - log.debug("role = " + role); - } - - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - console.info("Creating Sender"); - // Create a new connection to pass the test messages on. - connection = new Connection[1]; - session = new Session[1]; - - connection[0] = TestUtils.createConnection(TestClient.testContextProperties); - session[0] = connection[0].createSession(false, ackMode); - - // Extract and retain the test parameters. - sendDestination = session[0].createTopic(sendKey); - - connection[0].setExceptionListener(this); - - producer = session[0].createProducer(sendDestination); - - sendUpdateDestination = session[0].createTopic(sendUpdateKey); - MessageConsumer updateConsumer = session[0].createConsumer(sendUpdateDestination); - - _rateAdapter = new SustainedRateAdapter(this); - updateConsumer.setMessageListener(_rateAdapter); - - break; - - // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number - // of receivers connections. - case RECEIVER: - console.info("Creating Receiver"); - // Create the required number of receivers connections. - connection = new Connection[numReceivers]; - session = new Session[numReceivers]; - - for (int i = 0; i < numReceivers; i++) - { - connection[i] = TestUtils.createConnection(TestClient.testContextProperties); - session[i] = connection[i].createSession(false, ackMode); - - sendDestination = session[i].createTopic(sendKey); - - sendUpdateDestination = session[i].createTopic(sendUpdateKey); - - MessageConsumer consumer = session[i].createConsumer(sendDestination); - - consumer.setMessageListener(new SustainedListener(clientName + "-" + i, _batchSize, session[i], - sendUpdateDestination)); - } - - break; - } - - // Start all the connection dispatcher threads running. - for (int i = 0; i < connection.length; i++) - { - connection[i].start(); - } - } - - /** Performs the test case actions. - * @param numMessages*/ - public void start(int numMessages) throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - _rateAdapter.run(); - break; - case RECEIVER: - - } - - // return from here when you have finished the test.. this will signal the controller and - } - - public void terminate() throws JMSException, InterruptedException - { - if (_rateAdapter != null) - { - _rateAdapter.stop(); - } - } - - /** - * Gets a report on the actions performed by the test case in its assigned role. - * - * @param session The controlSession 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 - { - log.debug("public Message getReport(Session controlSession): called"); - - // Close the test connections. - for (int i = 0; i < connection.length; i++) - { - connection[i].close(); - } - - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - - return report; - } - - public void onException(JMSException jmsException) - { - Exception linked = jmsException.getLinkedException(); - - if (linked != null) - { - if (log.isDebugEnabled()) - { - log.debug("Linked Exception:" + linked); - } - - if ((linked instanceof AMQNoRouteException) || (linked instanceof AMQNoConsumersException)) - { - if (log.isDebugEnabled()) - { - if (linked instanceof AMQNoConsumersException) - { - log.warn("No clients currently available for message:" - + ((AMQNoConsumersException) linked).getUndeliveredMessage()); - } - else - { - log.warn("No route for message"); - } - } - - // Tell the rate adapter that there are no clients ready yet - _rateAdapter.NO_CLIENTS = true; - } - } - else - { - log.warn("Exception:" + linked); - } - } - - /** - * Inner class that listens for messages and sends a report for the time taken between receiving the 'start' and - * 'end' messages. - */ - class SustainedListener implements MessageListener - { - /** Number of messages received */ - private long _received = 0; - /** The number of messages in the batch */ - private int _batchSize = 0; - /** Record of the when the 'start' messagse was sen */ - private Long _startTime; - /** Message producer to use to send reports */ - MessageProducer _updater; - /** Session to create the report message on */ - Session _session; - /** Record of the client ID used for this SustainedListnener */ - String _client; - - /** - * Main Constructor - * - * @param clientname The _client id used to identify this connection. - * @param batchSize The number of messages that are to be sent per batch. Note: This is not used to - * control the interval between sending reports. - * @param session The controlSession used for communication. - * @param sendDestination The destination that update reports should be sent to. - * - * @throws JMSException My occur if creatingthe Producer fails - */ - public SustainedListener(String clientname, int batchSize, Session session, Destination sendDestination) - throws JMSException - { - _batchSize = batchSize; - _client = clientname; - _session = session; - _updater = session.createProducer(sendDestination); - } - - public void onMessage(Message message) - { - if (log.isTraceEnabled()) - { - log.trace("Message " + _received + "received in listener"); - } - - if (message instanceof TextMessage) - { - try - { - _received++; - if (((TextMessage) message).getText().equals("start")) - { - log.debug("Starting Batch"); - _startTime = System.nanoTime(); - } - else if (((TextMessage) message).getText().equals("end")) - { - if (_startTime != null) - { - long currentTime = System.nanoTime(); - sendStatus(currentTime - _startTime, _received, message.getIntProperty("BATCH")); - log.debug("End Batch"); - } - } - } - catch (JMSException e) - { - // ignore error - } - } - - } - - /** - * sendStatus creates and sends the report back to the publisher - * - * @param time taken for the the last batch - * @param received Total number of messages received. - * @param batchNumber the batch number - * @throws JMSException if an error occurs during the send - */ - private void sendStatus(long time, long received, int batchNumber) throws JMSException - { - Message updateMessage = _session.createTextMessage("update"); - updateMessage.setStringProperty("CLIENT_ID", ":" + _client); - updateMessage.setStringProperty("CONTROL_TYPE", "UPDATE"); - updateMessage.setLongProperty("RECEIVED", received); - updateMessage.setIntProperty("BATCH", batchNumber); - updateMessage.setLongProperty("DURATION", time); - - if (log.isInfoEnabled()) - { - log.info("**** SENDING [" + batchNumber + "]**** " + "CLIENT_ID:" + _client + " RECEIVED:" + received - + " BATCH:" + batchNumber + " DURATION:" + time); - } - - // Output on the main console.info the details of this batch - if ((batchNumber % 10) == 0) - { - console.info("Sending Report [" + batchNumber + "] " + "CLIENT_ID:" + _client + " RECEIVED:" + received - + " BATCH:" + batchNumber + " DURATION:" + time); - } - - _updater.send(updateMessage); - } - } - - /** - * This class is used here to adjust the _delay value which in turn is used to control the number of messages/second - * that are sent through the test system. - * - * By keeping a record of the messages recevied and the average time taken to process the batch size can be - * calculated and so the delay can be adjusted to maintain that rate. - * - * Given that delays of < 10ms can be rounded up the delay is only used between messages if the _delay > 10ms * no - * messages in the batch. Otherwise the delay is used at the end of the batch. - */ - class SustainedRateAdapter implements MessageListener, Runnable - { - private SustainedClientTestCase _client; - private long _batchVariance = Integer.getInteger("batchVariance", 3); // no. batches to allow drifting - private long _timeVariance = TEN_MILLI_SEC * 5; // no. nanos between send and report delay (10ms) - private volatile long _delay; // in nanos - private long _sent; - private Map _slowClients = new HashMap(); - private static final long PAUSE_SLEEP = TEN_MILLI_SEC / 1000; // 10 ms - private static final long NO_CLIENT_SLEEP = 1000; // 1s - private volatile boolean NO_CLIENTS = true; - private int _delayShifting; - private final int REPORTS_WITHOUT_CHANGE = Integer.getInteger("stableReportCount", 5); - private boolean _warmedup = false; - private static final long EXPECTED_TIME_PER_BATCH = 100000L; - private int _warmUpBatches = Integer.getInteger("warmUpBatches", 10); - - SustainedRateAdapter(SustainedClientTestCase client) - { - _client = client; - } - - public void onMessage(Message message) - { - if (log.isDebugEnabled()) - { - log.debug("SustainedRateAdapter onMessage(Message message = " + message + "): called"); - } - - try - { - String controlType = message.getStringProperty("CONTROL_TYPE"); - - // Check if the message is a test invite. - if ("UPDATE".equals(controlType)) - { - NO_CLIENTS = false; - long duration = message.getLongProperty("DURATION"); - long totalReceived = message.getLongProperty("RECEIVED"); - String client = message.getStringProperty("CLIENT_ID"); - int batchNumber = message.getIntProperty("BATCH"); - - if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) - { - log.info("Update Report: CLIENT_ID:" + client + " RECEIVED:" + totalReceived + " Recevied BATCH:" - + batchNumber + " DURATION:" + duration); - } - - recordSlow(client, totalReceived, batchNumber); - - adjustDelay(client, batchNumber, duration); - - // Warm up completes when: - // we haven't warmed up - // and the number of batches sent to each client is at least half of the required warmup batches - if (!_warmedup && (batchNumber >= _warmUpBatches)) - { - _warmedup = true; - _warmup.countDown(); - - } - } - } - catch (JMSException e) - { - // - } - } - - CountDownLatch _warmup = new CountDownLatch(1); - - int _numBatches = 10000; - - // long[] _timings = new long[_numBatches]; - private boolean _running = true; - - public void run() - { - console.info("Warming up"); - - doBatch(_warmUpBatches); - - try - { - // wait for warmup to complete. - _warmup.await(); - - // set delay to the average length of the batches - _delay = _totalDuration / _warmUpBatches / delays.size(); - - console.info("Warmup complete delay set : " + _delay + " based on _totalDuration: " + _totalDuration - + " over no. batches: " + _warmUpBatches + " with client count: " + delays.size()); - - _totalDuration = 0L; - _totalReceived = 0L; - _sent = 0L; - } - catch (InterruptedException e) - { - // - } - - doBatch(_numBatches); - - } - - private void doBatch(int batchSize) // long[] timings, - { - TextMessage testMessage = null; - try - { - testMessage = _client.session[0].createTextMessage("start"); - - for (int batch = 0; batch <= batchSize; batch++) - // while (_running) - { - long start = System.nanoTime(); - - testMessage.setText("start"); - testMessage.setIntProperty("BATCH", batch); - - _client.producer.send(testMessage); - _rateAdapter.sentMessage(); - - testMessage.setText("test"); - // start at 2 so start and end count as part of batch - for (int m = 2; m < _batchSize; m++) - { - _client.producer.send(testMessage); - _rateAdapter.sentMessage(); - } - - testMessage.setText("end"); - _client.producer.send(testMessage); - _rateAdapter.sentMessage(); - - long end = System.nanoTime(); - - long sendtime = end - start; - - if (log.isDebugEnabled()) - { - log.info("Sent batch[" + batch + "](" + _batchSize + ") in " + sendtime); // timings[batch]); - } - - if ((batch % LOG_UPATE_INTERVAL) == 0) - { - console.info("Sent Batch[" + batch + "](" + _batchSize + ")" + status()); - } - - _rateAdapter.sleepBatch(); - - } - } - catch (JMSException e) - { - console.error("Runner ended"); - } - } - - private String status() - { - return " TotalDuration: " + _totalDuration + " for " + delays.size() + " consumers" + " Delay is " + _delay - + " resulting in " - + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch")); - } - - private void sleepBatch() - { - if (checkForSlowClients()) - { // if there werwe slow clients we have already slept so don't sleep anymore again. - return; - } - - if (!SLEEP_PER_MESSAGE) - { - // per batch sleep.. if sleep is to small to spread over the batch. - if (_delay <= (TEN_MILLI_SEC * _batchSize)) - { - sleepLong(_delay); - } - else - { - log.info("Not sleeping _delay > ten*batch is:" + _delay); - } - } - } - - public void stop() - { - _running = false; - } - - Map delays = new HashMap(); - Long _totalReceived = 0L; - Long _totalDuration = 0L; - int _skipUpdate = 0; - - /** - * Adjust the delay for sending messages based on this update from the client - * - * @param client The client that send this update - * @param duration The time taken for the last batch of messagse - * @param batchNumber The reported batchnumber from the client - */ - private void adjustDelay(String client, int batchNumber, long duration) - { - // Retrieve the current total time taken for this client. - Long currentTime = delays.get(client); - - // Add the new duration time to this client - if (currentTime == null) - { - currentTime = duration; - } - else - { - currentTime += duration; - } - - delays.put(client, currentTime); - - long batchesSent = _sent / _batchSize; - - // ensure we don't divide by zero - if (batchesSent == 0) - { - batchesSent = 1L; - } - - _totalReceived += _batchSize; - _totalDuration += duration; - - // calculate average duration accross clients per batch - long averageDuration = _totalDuration / delays.size() / batchesSent; - - // calculate the difference between current send delay and average report delay - long diff = (duration) - averageDuration; - - if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) - { - log.info("TotalDuration:" + _totalDuration + " for " + delays.size() + " consumers." + " on batch: " - + batchesSent + " received batch: " + batchNumber + " Batch Duration: " + duration + " Average: " - + averageDuration + " so diff: " + diff + " for : " + client + " Delay is " + _delay + " resulting in " - + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch"))); - } - - // if the averageDuration differs from the current by more than the specified variane then adjust delay. - if (Math.abs(diff) > _timeVariance) - { - - // if the the _delay is larger than the required duration to send report - // speed up - if (diff > TEN_MILLI_SEC) - { - _delay -= TEN_MILLI_SEC; - - if (_delay < 0) - { - _delay = 0; - log.info("Reset _delay to 0"); - delayStable(); - } - else - { - delayChanged(); - } - - } - else if (diff < 0) // diff < 0 diff cannot be 0 as it is > _timeVariance - { - // the report took longer - _delay += TEN_MILLI_SEC; - delayChanged(); - } - } - else - { - delayStable(); - } - - // If we have a consumer that is behind with the batches. - if ((batchesSent - batchNumber) > _batchVariance) - { - log.debug("Increasing _delay as sending more than receiving"); - - _delay += 2 * TEN_MILLI_SEC; - delayChanged(); - } - - } - - /** Reset the number of iterations before we say the delay has stabilised. */ - private void delayChanged() - { - _delayShifting = REPORTS_WITHOUT_CHANGE; - } - - /** - * Record the fact that delay has stabilised If delay has stablised for REPORTS_WITHOUT_CHANGE then it will - * output Delay stabilised - */ - private void delayStable() - { - _delayShifting--; - - if (_delayShifting < 0) - { - _delayShifting = 0; - console.debug("Delay stabilised:" + _delay); - } - } - - /** - * Checks that the client has received enough messages. If the client has fallen behind then they are put in the - * _slowClients lists which will increase the delay. - * - * @param client The client identifier to check - * @param received the number of messages received by that client - * @param batchNumber - */ - private void recordSlow(String client, long received, int batchNumber) - { - if (Math.abs(batchNumber - (_sent / _batchSize)) > _batchVariance) - { - _slowClients.put(client, received); - } - else - { - _slowClients.remove(client); - } - } - - /** Incrment the number of sent messages and then sleep, if required. */ - public void sentMessage() - { - - _sent++; - - if (_delay > (TEN_MILLI_SEC * _batchSize)) - { - long batchDelay = _delay / _batchSize; - // less than 10ms sleep doesn't always work. - // _delay is in nano seconds - // if (batchDelay < (TEN_MILLI_SEC)) - // { - // sleep(0, (int) batchDelay); - // } - // else - { - // if (batchDelay < 30000000000L) - { - sleepLong(batchDelay); - } - } - } - else - { - if (SLEEP_PER_MESSAGE && (_delay > 0)) - { - sleepLong(_delay / _batchSize); - } - } - } - - /** - * Check at the end of each batch and pause sending messages to allow slow clients to catch up. - * - * @return true if there were slow clients that caught up. - */ - private boolean checkForSlowClients() - { - // This will allways be true as we are running this at the end of each batchSize - // if (_sent % _batchSize == 0) - { - // Cause test to pause when we have slow - if (!_slowClients.isEmpty() || NO_CLIENTS) - { - - while (!_slowClients.isEmpty()) - { - if (log.isInfoEnabled() && ((_sent / _batchSize % DEBUG_LOG_UPATE_INTERVAL) == 0)) - { - String clients = ""; - Iterator it = _slowClients.keySet().iterator(); - while (it.hasNext()) - { - clients += it.next(); - if (it.hasNext()) - { - clients += ", "; - } - } - - log.info("Pausing for slow clients:" + clients); - } - - if (console.isDebugEnabled() && ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0)) - { - console.debug(_slowClients.size() + " slow clients."); - } - - sleep(PAUSE_SLEEP); - } - - if (NO_CLIENTS) - { - sleep(NO_CLIENT_SLEEP); - } - - log.debug("Continuing"); - - return true; - } - else - { - if ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0) - { - console.info("Total Delay :" + _delay + " " - + ((_delayShifting == 0) ? "Stablised" : ("Not Stablised(" + _delayShifting + ")"))); - } - } - - } - - return false; - } - - /** - * Sleep normally takes micro-seconds this allows the use of a nano-second value. - * - * @param delay nanoseconds to sleep for. - */ - private void sleepLong(long delay) - { - sleep(delay / 1000000, (int) (delay % 1000000)); - } - - /** - * Sleep for the specified micro-seconds. - * @param sleep microseconds to sleep for. - */ - private void sleep(long sleep) - { - sleep(sleep, 0); - } - - /** - * Perform the sleep , swallowing any InteruptException. - * - * NOTE: If a sleep request is > 10s then reset only sleep for 5s - * - * @param milli to sleep for - * @param nano sub miliseconds to sleep for - */ - private void sleep(long milli, int nano) - { - try - { - log.debug("Sleep:" + milli + ":" + nano); - if (milli > 10000) - { - - if (_delay == milli) - { - _totalDuration = _totalReceived / _batchSize * EXPECTED_TIME_PER_BATCH; - log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) - + "s. Reset _totalDuration:" + _totalDuration); - } - else - { - log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + "s"); - } - - milli = 5000; - } - - Thread.sleep(milli, nano); - } - catch (InterruptedException e) - { - // - } - } - - public void setClient(SustainedClientTestCase client) - { - _client = client; - } - } - -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java deleted file mode 100644 index 0077b4727a..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * - * 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.sustained; - -import org.apache.log4j.Logger; - -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.test.framework.DropInTest; -import org.apache.qpid.test.framework.FrameworkBaseCase; - -import javax.jms.JMSException; -import javax.jms.Message; - -import java.util.Properties; - -/** - * SustainedTestCase is a {@link FrameworkBaseCase} that runs the "Perf_SustainedPubSub" test case. This consists of one - * test client sending, and several receiving, and attempts to find the highest rate at which messages can be broadcast - * to the receivers. It is also a {@link DropInTest} to which more test clients may be added during a test run. - * - *

- *
CRC Card
Responsibilities Collaborations - *
- *
- */ -public class SustainedTestCase extends FrameworkBaseCase implements DropInTest -{ - /** Used for debugging. */ - Logger log = Logger.getLogger(SustainedTestCase.class); - - /** Holds the root name of the topic on which to send the test messages. */ - private static final String SUSTAINED_KEY = "Perf_SustainedPubSub"; - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public SustainedTestCase(String name) - { - super(name); - } - - /** - * Performs a single test run of the sustained test. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testBasicPubSub() throws Exception - { - log.debug("public void testSinglePubSubCycle(): called"); - - Properties testConfig = new Properties(); - testConfig.put("TEST_NAME", "Perf_SustainedPubSub"); - testConfig.put("SUSTAINED_KEY", SUSTAINED_KEY); - testConfig.put("SUSTAINED_NUM_RECEIVERS", Integer.getInteger("numReceives", 2)); - testConfig.put("SUSTAINED_UPDATE_INTERVAL", Integer.getInteger("batchSize", 1000)); - testConfig.put("SUSTAINED_UPDATE_KEY", SUSTAINED_KEY + ".UPDATE"); - testConfig.put("ACKNOWLEDGE_MODE", Integer.getInteger("ackMode", AMQSession.AUTO_ACKNOWLEDGE)); - - log.info("Created Config: " + testConfig.entrySet().toArray()); - - getCircuitFactory().sequenceTest(null, null, testConfig); - } - - /** - * Accepts a late joining client into this test case. The client will be enlisted with a control message - * with the 'CONTROL_TYPE' field set to the value 'LATEJOIN'. It should also provide values for the fields: - * - *

- *
CLIENT_NAME A unique name for the new client. - *
CLIENT_PRIVATE_CONTROL_KEY The key for the route on which the client receives its control messages. - *
- * - * @param message The late joiners join message. - * - * @throws JMSException Any JMS Exception are allowed to fall through, indicating that the join failed. - */ - public void lateJoin(Message message) throws JMSException - { - throw new RuntimeException("Not implemented."); - /* - // Extract the joining clients details from its join request message. - TestClientDetails clientDetails = new TestClientDetails(); - clientDetails.clientName = message.getStringProperty("CLIENT_NAME"); - clientDetails.privateControlKey = message.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); - - // Register the joining client, but do block for confirmation as cannot do a synchronous receivers during this - // method call, as it may have been called from an 'onMessage' method. - assignReceiverRole(clientDetails, new Properties(), false); - */ - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as known to the test - * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test - * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case - * name "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "Perf_SustainedPubSub"; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java deleted file mode 100644 index 56cc0dbd98..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java +++ /dev/null @@ -1,464 +0,0 @@ -/* - * - * 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.test.framework.distributedtesting; - -import org.apache.log4j.Logger; -import org.apache.log4j.NDC; - -import org.apache.qpid.interop.clienttestcases.TestCase1DummyRun; -import org.apache.qpid.interop.clienttestcases.TestCase2BasicP2P; -import org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub; -import org.apache.qpid.sustained.SustainedClientTestCase; -import org.apache.qpid.test.framework.MessagingTestConfigProperties; -import org.apache.qpid.test.framework.TestUtils; -import org.apache.qpid.test.framework.clocksynch.ClockSynchThread; -import org.apache.qpid.test.framework.clocksynch.ClockSynchronizer; -import org.apache.qpid.test.framework.clocksynch.UDPClockSynchronizer; -import org.apache.qpid.test.framework.distributedcircuit.TestClientCircuitEnd; - -import uk.co.thebadgerset.junit.extensions.SleepThrottle; -import uk.co.thebadgerset.junit.extensions.Throttle; -import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; -import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; - -import javax.jms.*; - -import java.util.*; - -/** - * 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 Coordinator}. - * - *

- *
Messages Handled by SustainedTestClient
Message Action - *
Invite(compulsory) Reply with Enlist. - *
Invite(test case) Reply with Enlist if test case available. - *
AssignRole(test case) Reply with Accept Role if matches an enlisted test. Keep test parameters. - *
Start Send test messages defined by test parameters. Send report on messages sent. - *
Status Request Send report on messages received. - *
Terminate Terminate the test client. - *
ClockSynch Synch clock against the supplied UDP address. - *
- * - *

- *
CRC Card
Responsibilities Collaborations - *
Handle all incoming control messages. {@link TestClientControlledTest} - *
Configure and look up test cases by name. {@link TestClientControlledTest} - *
- */ -public class TestClient implements MessageListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestClient.class); - - /** Used for reporting to the console. */ - private static final Logger console = Logger.getLogger("CONSOLE"); - - /** Holds the default identifying name of the test client. */ - public static final String CLIENT_NAME = "java"; - - /** Holds the URL of the broker to run the tests on. */ - public static String brokerUrl; - - /** Holds the virtual host to run the tests on. If null, then the default virtual host is used. */ - public static String virtualHost; - - /** - * Holds the test context properties that provides the default test parameters, plus command line overrides. - * This is initialized with the default test parameters, to which command line overrides may be applied. - */ - public static ParsedProperties testContextProperties = - TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); - - /** Holds all the test cases loaded from the classpath. */ - Map testCases = new HashMap(); - - /** Holds the test case currently being run by this client. */ - protected TestClientControlledTest currentTestCase; - - /** Holds the connection to the broker that the test is being coordinated on. */ - protected Connection connection; - - /** Holds the message producer to hold the test coordination over. */ - protected MessageProducer producer; - - /** Holds the JMS controlSession for the test coordination. */ - protected Session session; - - /** Holds the name of this client, with a default value. */ - protected String clientName = CLIENT_NAME; - - /** This flag indicates that the test client should attempt to join the currently running test case on start up. */ - protected boolean join; - - /** Holds the clock synchronizer for the test node. */ - ClockSynchThread clockSynchThread; - - /** - * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client - * identifying name. - * - * @param brokerUrl The url of the broker to connect to. - * @param virtualHost The virtual host to conect to. - * @param clientName The client name to use. - * @param join Flag to indicate that this client should attempt to join running tests. - */ - public TestClient(String brokerUrl, String virtualHost, String clientName, boolean join) - { - log.debug("public TestClient(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost - + ", String clientName = " + clientName + ", boolean join = " + join + "): called"); - - // Retain the connection parameters. - this.brokerUrl = brokerUrl; - this.virtualHost = virtualHost; - this.clientName = clientName; - this.join = join; - } - - /** - * The entry point for the interop test coordinator. This client accepts the following command line arguments: - * - *

- *
-b The broker URL. Optional. - *
-h The virtual host. Optional. - *
-n The test client name. Optional. - *
name=value Trailing argument define name/value pairs. Added to system properties. Optional. - *
- * - * @param args The command line arguments. - */ - public static void main(String[] args) - { - log.debug("public static void main(String[] args = " + Arrays.toString(args) + "): called"); - console.info("Qpid Distributed Test Client."); - - // Override the default broker url to be localhost:5672. - testContextProperties.setProperty(MessagingTestConfigProperties.BROKER_PROPNAME, "tcp://localhost:5672"); - - // Use the command line parser to evaluate the command line with standard handling behaviour (print errors - // and usage then exist if there are errors). - // Any options and trailing name=value pairs are also injected into the test context properties object, - // to override any defaults that may have been set up. - ParsedProperties options = - new ParsedProperties(uk.co.thebadgerset.junit.extensions.util.CommandLineParser.processCommandLine(args, - new uk.co.thebadgerset.junit.extensions.util.CommandLineParser( - new String[][] - { - { "b", "The broker URL.", "broker", "false" }, - { "h", "The virtual host to use.", "virtual host", "false" }, - { "o", "The name of the directory to output test timings to.", "dir", "false" }, - { "n", "The name of the test client.", "name", "false" }, - { "j", "Join this test client to running test.", "false" } - }), testContextProperties)); - - // Extract the command line options. - String brokerUrl = options.getProperty("b"); - String virtualHost = options.getProperty("h"); - String clientName = options.getProperty("n"); - clientName = (clientName == null) ? CLIENT_NAME : clientName; - boolean join = options.getPropertyAsBoolean("j"); - - // To distinguish logging output set up an NDC on the client name. - NDC.push(clientName); - - // Create a test client and start it running. - TestClient client = new TestClient(brokerUrl, virtualHost, clientName, join); - - // Use a class path scanner to find all the interop test case implementations. - // Hard code the test classes till the classpath scanner is fixed. - Collection> testCaseClasses = - new ArrayList>(); - // ClasspathScanner.getMatches(TestClientControlledTest.class, "^TestCase.*", true); - Collections.addAll(testCaseClasses, TestCase1DummyRun.class, TestCase2BasicP2P.class, TestCase3BasicPubSub.class, - SustainedClientTestCase.class, TestClientCircuitEnd.class); - - try - { - client.start(testCaseClasses); - } - catch (Exception e) - { - log.error("The test client was unable to start.", e); - console.info(e.getMessage()); - System.exit(1); - } - } - - /** - * Starts the interop test client running. This causes it to start listening for incoming test invites. - * - * @param testCaseClasses The classes of the available test cases. The test case names from these are used to - * matchin incoming test invites against. - * - * @throws JMSException Any underlying JMSExceptions are allowed to fall through. - */ - protected void start(Collection> testCaseClasses) throws JMSException - { - log.debug("protected void start(Collection> testCaseClasses = " - + testCaseClasses + "): called"); - - // Create all the test case implementations and index them by the test names. - for (Class nextClass : testCaseClasses) - { - try - { - TestClientControlledTest testCase = nextClass.newInstance(); - testCases.put(testCase.getName(), testCase); - } - catch (InstantiationException e) - { - log.warn("Could not instantiate test case class: " + nextClass.getName(), e); - // Ignored. - } - catch (IllegalAccessException e) - { - log.warn("Could not instantiate test case class due to illegal access: " + nextClass.getName(), e); - // Ignored. - } - } - - // Open a connection to communicate with the coordinator on. - connection = TestUtils.createConnection(testContextProperties); - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Set this up to listen for control messages. - Topic privateControlTopic = session.createTopic("iop.control." + clientName); - MessageConsumer consumer = session.createConsumer(privateControlTopic); - consumer.setMessageListener(this); - - Topic controlTopic = session.createTopic("iop.control"); - MessageConsumer consumer2 = session.createConsumer(controlTopic); - consumer2.setMessageListener(this); - - // Create a producer to send replies with. - producer = session.createProducer(null); - - // If the join flag was set, then broadcast a join message to notify the coordinator that a new test client - // is available to join the current test case, if it supports it. This message may be ignored, or it may result - // in this test client receiving a test invite. - if (join) - { - Message joinMessage = session.createMessage(); - - joinMessage.setStringProperty("CONTROL_TYPE", "JOIN"); - joinMessage.setStringProperty("CLIENT_NAME", clientName); - joinMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); - producer.send(controlTopic, joinMessage); - } - - // Start listening for incoming control messages. - connection.start(); - } - - /** - * Handles all incoming control messages. - * - * @param message The incoming message. - */ - public void onMessage(Message message) - { - NDC.push(clientName); - log.debug("public void onMessage(Message message = " + message + "): called"); - - try - { - String controlType = message.getStringProperty("CONTROL_TYPE"); - String testName = message.getStringProperty("TEST_NAME"); - - log.debug("Received control of type '" + controlType + "' for the test '" + testName + "'"); - - // Check if the message is a test invite. - if ("INVITE".equals(controlType)) - { - // Flag used to indicate that an enlist should be sent. Only enlist to compulsory invites or invites - // for which test cases exist. - boolean enlist = false; - - if (testName != null) - { - log.debug("Got an invite to test: " + testName); - - // Check if the requested test case is available. - TestClientControlledTest testCase = testCases.get(testName); - - if (testCase != null) - { - log.debug("Found implementing class for test '" + testName + "', enlisting for it."); - - // Check if the test case will accept the invitation. - enlist = testCase.acceptInvite(message); - - log.debug("The test case " - + (enlist ? " accepted the invite, enlisting for it." - : " did not accept the invite, not enlisting.")); - - // Make the requested test case the current test case. - currentTestCase = testCase; - } - else - { - log.debug("Received an invite to the test '" + testName + "' but this test is not known."); - } - } - else - { - log.debug("Got a compulsory invite, enlisting for it."); - - enlist = true; - } - - if (enlist) - { - // Reply with the client name in an Enlist message. - Message enlistMessage = session.createMessage(); - enlistMessage.setStringProperty("CONTROL_TYPE", "ENLIST"); - enlistMessage.setStringProperty("CLIENT_NAME", clientName); - enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); - enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - log.debug("Sending enlist message '" + enlistMessage + "' to " + message.getJMSReplyTo()); - - producer.send(message.getJMSReplyTo(), enlistMessage); - } - else - { - // Reply with the client name in an Decline message. - Message enlistMessage = session.createMessage(); - enlistMessage.setStringProperty("CONTROL_TYPE", "DECLINE"); - enlistMessage.setStringProperty("CLIENT_NAME", clientName); - enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); - enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - log.debug("Sending decline message '" + enlistMessage + "' to " + message.getJMSReplyTo()); - - producer.send(message.getJMSReplyTo(), enlistMessage); - } - } - else if ("ASSIGN_ROLE".equals(controlType)) - { - // Assign the role to the current test case. - String roleName = message.getStringProperty("ROLE"); - - log.debug("Got a role assignment to role: " + roleName); - - TestClientControlledTest.Roles role = Enum.valueOf(TestClientControlledTest.Roles.class, roleName); - - currentTestCase.assignRole(role, message); - - // Reply by accepting the role in an Accept Role message. - Message acceptRoleMessage = session.createMessage(); - acceptRoleMessage.setStringProperty("CLIENT_NAME", clientName); - acceptRoleMessage.setStringProperty("CONTROL_TYPE", "ACCEPT_ROLE"); - acceptRoleMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - log.debug("Sending accept role message '" + acceptRoleMessage + "' to " + message.getJMSReplyTo()); - - producer.send(message.getJMSReplyTo(), acceptRoleMessage); - } - else if ("START".equals(controlType) || "STATUS_REQUEST".equals(controlType)) - { - if ("START".equals(controlType)) - { - log.debug("Got a start notification."); - - // Extract the number of test messages to send from the start notification. - int numMessages; - - try - { - numMessages = message.getIntProperty("MESSAGE_COUNT"); - } - catch (NumberFormatException e) - { - // If the number of messages is not specified, use the default of one. - numMessages = 1; - } - - // Start the current test case. - currentTestCase.start(numMessages); - } - else - { - log.debug("Got a status request."); - } - - // Generate the report from the test case and reply with it as a Report message. - Message reportMessage = currentTestCase.getReport(session); - reportMessage.setStringProperty("CLIENT_NAME", clientName); - reportMessage.setStringProperty("CONTROL_TYPE", "REPORT"); - reportMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - log.debug("Sending report message '" + reportMessage + "' to " + message.getJMSReplyTo()); - - producer.send(message.getJMSReplyTo(), reportMessage); - } - else if ("TERMINATE".equals(controlType)) - { - console.info("Received termination instruction from coordinator."); - - // Is a cleaner shutdown needed? - connection.close(); - System.exit(0); - } - else if ("CLOCK_SYNCH".equals(controlType)) - { - log.debug("Received clock synch command."); - String address = message.getStringProperty("ADDRESS"); - - log.debug("address = " + address); - - // Re-create (if necessary) and start the clock synch thread to synch the clock every ten seconds. - if (clockSynchThread != null) - { - clockSynchThread.terminate(); - } - - SleepThrottle throttle = new SleepThrottle(); - throttle.setRate(0.1f); - - clockSynchThread = new ClockSynchThread(new UDPClockSynchronizer(address), throttle); - clockSynchThread.start(); - } - else - { - // Log a warning about this but otherwise ignore it. - log.warn("Got an unknown control message, controlType = " + controlType + ", message = " + message); - } - } - catch (JMSException e) - { - // Log a warning about this, but otherwise ignore it. - log.warn("Got JMSException whilst handling message: " + message, e); - } - // Log any runtimes that fall through this message handler. These are fatal errors for the test client. - catch (RuntimeException e) - { - log.error("The test client message handler got an unhandled exception: ", e); - console.info("The message handler got an unhandled exception, terminating the test client."); - System.exit(1); - } - finally - { - NDC.pop(); - } - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java deleted file mode 100644 index bad49060ca..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * - * 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.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.log4j.Logger; - -/** - * An ClasspathScanner scans the classpath for classes that implement an interface or extend a base class and have names - * that match a regular expression. - * - *

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. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Find all classes matching type and name pattern on the classpath. - *
- * - * @todo Add logic to scan jars as well as directories. - */ -public class ClasspathScanner -{ - private static final Logger log = Logger.getLogger(ClasspathScanner.class); - - /** - * 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 regular 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> getMatches(Class matchingClass, String matchingRegexp, - boolean beanOnly) - { - log.debug("public static Collection> getMatches(Class matchingClass = " + matchingClass - + ", String matchingRegexp = " + matchingRegexp + ", boolean beanOnly = " + beanOnly + "): called"); - - // Build a compiled regular expression from the pattern to match. - Pattern matchPattern = Pattern.compile(matchingRegexp); - - String classPath = System.getProperty("java.class.path"); - Map> result = new HashMap>(); - - log.debug("classPath = " + classPath); - - // Find matching classes starting from all roots in the classpath. - for (String path : splitClassPath(classPath)) - { - gatherFiles(new File(path), "", result, matchPattern, matchingClass); - } - - return result.values(); - } - - /** - * Finds all matching classes rooted at a given location in the file system. If location is a directory it - * is recursively examined. - * - * @param classRoot The root of the current point in the file system being examined. - * @param classFileName The name of the current file or directory to examine. - * @param result The accumulated mapping from class names to classes that match the scan. - * - * @todo Recursion ok as file system depth is not likely to exhaust the stack. Might be better to replace with - * iteration. - */ - private static void gatherFiles(File classRoot, String classFileName, Map> result, - Pattern matchPattern, Class matchClass) - { - log.debug("private static void gatherFiles(File classRoot = " + classRoot + ", String classFileName = " - + classFileName + ", Map> result, Pattern matchPattern = " + matchPattern - + ", Class matchClass = " + matchClass + "): called"); - - File thisRoot = new File(classRoot, classFileName); - - // If the current location is a file, check if it is a matching class. - if (thisRoot.isFile()) - { - // Check that the file has a matching name. - if (matchesName(thisRoot.getName(), matchPattern)) - { - String className = classNameFromFile(thisRoot.getName()); - - // Check that the class has matching type. - try - { - Class candidateClass = Class.forName(className); - - Class matchedClass = matchesClass(candidateClass, matchClass); - - if (matchedClass != null) - { - result.put(className, matchedClass); - } - } - catch (ClassNotFoundException e) - { - // Ignore this. The matching class could not be loaded. - log.debug("Got ClassNotFoundException, ignoring.", e); - } - } - - return; - } - // Otherwise the current location is a directory, so examine all of its contents. - else - { - String[] contents = thisRoot.list(); - - if (contents != null) - { - for (String content : contents) - { - gatherFiles(classRoot, classFileName + File.separatorChar + content, result, matchPattern, matchClass); - } - } - } - } - - /** - * Checks if the specified class file name corresponds to a class with name matching the specified regular expression. - * - * @param classFileName The class file name. - * @param matchPattern The regular expression pattern to match. - * - * @return true if the class name matches, false otherwise. - */ - private static boolean matchesName(String classFileName, Pattern matchPattern) - { - String className = classNameFromFile(classFileName); - Matcher matcher = matchPattern.matcher(className); - - return matcher.matches(); - } - - /** - * Checks if the specified class to compare extends the base class being scanned for. - * - * @param matchingClass The base class to match against. - * @param toMatch The class to match against the base class. - * - * @return The class to check, cast as an instance of the class to match if the class extends the base class, or - * null otherwise. - */ - private static Class matchesClass(Class matchingClass, Class toMatch) - { - try - { - return matchingClass.asSubclass(toMatch); - } - catch (ClassCastException e) - { - return null; - } - } - - /** - * 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 splitClassPath(String classPath) - { - List result = new LinkedList(); - String separator = System.getProperty("path.separator"); - StringTokenizer tokenizer = new StringTokenizer(classPath, separator); - - while (tokenizer.hasMoreTokens()) - { - result.add(tokenizer.nextToken()); - } - - return result; - } - - /** - * Translates from the filename of a class to its fully qualified classname. Files are named using forward slash - * seperators and end in ".class", whereas fully qualified class names use "." sperators and no ".class" ending. - * - * @param classFileName The filename of the class to translate to a class name. - * - * @return The fully qualified class name. - */ - private static String classNameFromFile(String classFileName) - { - log.debug("private static String classNameFromFile(String classFileName = " + classFileName + "): called"); - - // Remove the .class ending. - String s = classFileName.substring(0, classFileName.length() - ".class".length()); - - // Turn / seperators in . seperators. - String s2 = s.replace(File.separatorChar, '.'); - - // Knock off any leading . caused by a leading /. - if (s2.startsWith(".")) - { - return s2.substring(1); - } - - return s2; - } -} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationFactory.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationFactory.java deleted file mode 100644 index 0090bec3d0..0000000000 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationFactory.java +++ /dev/null @@ -1,479 +0,0 @@ -/* - * - * 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 org.apache.log4j.Logger; - -import javax.jms.*; - -import java.util.*; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -/** - * 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. - * - *

One use of this is 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 conversation - * 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): - * - *

- * class Initiator
- * {
- * ConversationHelper conversation = new ConversationHelper(connection, null,
- *                                                          java.util.concurrent.LinkedBlockingQueue.class);
- *
- * initiateConversation()
- * {
- *  try {
- *   // Exchange greetings.
- *   conversation.send(sendDestination, conversation.getSession().createTextMessage("Hello."));
- *   Message greeting = conversation.receive();
- *
- *   // Exchange goodbyes.
- *   conversation.send(conversation.getSession().createTextMessage("Goodbye."));
- *   Message goodbye = conversation.receive();
- *  } finally {
- *   conversation.end();
- *  }
- * }
- * }
- *
- * class Responder
- * {
- * ConversationHelper conversation = new ConversationHelper(connection, receiveDestination,
- *                                                          java.util.concurrent.LinkedBlockingQueue.class);
- *
- * 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();
- *  }
- * }
- * }
- * 
- * - *

Conversation correlation id's are generated on a per thread basis. - * - *

The same session is shared amongst all conversations. Calls to send are therefore synchronized because JMS - * sessions are not multi-threaded. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Associate messages to an ongoing conversation using correlation ids. - *
Auto manage sessions for conversations. - *
Store messages not in a conversation in dead letter box. - *
- */ -public class ConversationFactory -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(ConversationFactory.class); - - /** Holds a map from correlation id's to queues. */ - private Map> idsToQueues = new HashMap>(); - - /** Holds the connection over which the conversation is conducted. */ - private Connection connection; - - /** Holds the session over which the conversation is conduxted. */ - private Session session; - - /** The message consumer for incoming messages. */ - MessageConsumer consumer; - - /** The message producer for outgoing messages. */ - MessageProducer producer; - - /** The well-known or temporary destination to receive replies on. */ - Destination receiveDestination; - - /** Holds the queue implementation class for the reply queue. */ - Class queueClass; - - /** Used to hold any replies that are received outside of the context of a conversation. */ - BlockingQueue deadLetterBox = new LinkedBlockingQueue(); - - /* Used to hold conversation state on a per thread basis. */ - /* - ThreadLocal threadLocals = - new ThreadLocal() - { - protected Conversation initialValue() - { - Conversation settings = new Conversation(); - settings.conversationId = conversationIdGenerator.getAndIncrement(); - - return settings; - } - }; - */ - - /** Generates new coversation id's as needed. */ - AtomicLong conversationIdGenerator = new AtomicLong(); - - /** - * 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 receiveDestination The destination to listen to for incoming messages. This may be null to use a temporary - * queue. - * @param queueClass The queue implementation class. - * - * @throws JMSException All underlying JMSExceptions are allowed to fall through. - */ - public ConversationFactory(Connection connection, Destination receiveDestination, - Class queueClass) throws JMSException - { - log.debug("public ConversationFactory(Connection connection, Destination receiveDestination = " + receiveDestination - + ", Class queueClass = " + queueClass + "): called"); - - this.connection = connection; - this.queueClass = queueClass; - - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Check if a well-known receive destination has been provided, or use a temporary queue if not. - this.receiveDestination = (receiveDestination != null) ? receiveDestination : session.createTemporaryQueue(); - - consumer = session.createConsumer(receiveDestination); - producer = session.createProducer(null); - - consumer.setMessageListener(new Receiver()); - } - - /** - * Creates a new conversation context. - * - * @return A new conversation context. - */ - public Conversation startConversation() - { - log.debug("public Conversation startConversation(): called"); - - Conversation conversation = new Conversation(); - conversation.conversationId = conversationIdGenerator.getAndIncrement(); - - return conversation; - } - - /** - * Ensures that the reply queue for a conversation exists. - * - * @param conversationId The conversation correlation id. - */ - private void initQueueForId(long conversationId) - { - if (!idsToQueues.containsKey(conversationId)) - { - idsToQueues.put(conversationId, ReflectionUtils.newInstance(queueClass)); - } - } - - /** - * Clears the dead letter box, returning all messages that were in it. - * - * @return All messages in the dead letter box. - */ - public Collection emptyDeadLetterBox() - { - log.debug("public Collection emptyDeadLetterBox(): called"); - - Collection result = new ArrayList(); - deadLetterBox.drainTo(result); - - return result; - } - - /** - * Gets the session over which the conversation is conducted. - * - * @return The session over which the conversation is conducted. - */ - public Session getSession() - { - // Conversation settings = threadLocals.get(); - - return session; - } - - /** - * Used to hold a conversation context. This consists of a correlating id for the conversation, and a reply - * destination automatically updated to the last received reply-to destination. - */ - public class Conversation - { - /** Holds the correlation id for the context. */ - long conversationId; - - /** - * Holds the send destination for the context. This will automatically be updated to the most recently received - * reply-to destination. - */ - Destination sendDestination; - - /** - * 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 sendDestination The destination to send to. This may be null to use the last received reply-to - * destination. - * @param message The message to send. - * - * @throws JMSException All undelying JMSExceptions are allowed to fall through. This will also be thrown if no - * send destination is specified and there is no most recent reply-to destination available - * to use. - */ - public void send(Destination sendDestination, Message message) throws JMSException - { - log.debug("public void send(Destination sendDestination = " + sendDestination + ", Message message = " + message - + "): called"); - - // Conversation settings = threadLocals.get(); - // long conversationId = conversationId; - message.setJMSCorrelationID(Long.toString(conversationId)); - message.setJMSReplyTo(receiveDestination); - - // Ensure that the reply queue for this conversation exists. - initQueueForId(conversationId); - - // Check if an overriding send to destination has been set or use the last reply-to if not. - Destination sendTo = null; - - if (sendDestination != null) - { - sendTo = sendDestination; - } - else if (sendDestination != null) - { - sendTo = sendDestination; - } - else - { - throw new JMSException("The send destination was specified, and no most recent reply-to available to use."); - } - - // Send the message. - synchronized (this) - { - producer.send(sendTo, 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. - * - * @throws JMSException All undelying JMSExceptions are allowed to fall through. Thrown if the received message - * did not have its reply-to destination set up. - */ - public Message receive() throws JMSException - { - log.debug("public Message receive(): called"); - - // Conversation settings = threadLocals.get(); - // long conversationId = settings.conversationId; - - // Ensure that the reply queue for this conversation exists. - initQueueForId(conversationId); - - BlockingQueue queue = idsToQueues.get(conversationId); - - try - { - Message result = queue.take(); - - // Keep the reply-to destination to send replies to. - sendDestination = result.getJMSReplyTo(); - - return result; - } - catch (InterruptedException e) - { - return null; - } - } - - /** - * Gets many messages in an ongoing conversation. If a limit is specified, then once that many messages are - * received they will be returned. If a timeout is specified, then all messages up to the limit, received within - * that timespan will be returned. At least one of the message count or timeout should be set to a value of - * 1 or greater. - * - * @param num The number of messages to receive, or all if this is less than 1. - * @param timeout The timeout in milliseconds to receive the messages in, or forever if this is less than 1. - * - * @return All messages received within the count limit and the timeout. - * - * @throws JMSException All undelying JMSExceptions are allowed to fall through. - */ - public Collection receiveAll(int num, long timeout) throws JMSException - { - log.debug("public Collection receiveAll(int num = " + num + ", long timeout = " + timeout - + "): called"); - - // Check that a timeout or message count was set. - if ((num < 1) && (timeout < 1)) - { - throw new IllegalArgumentException("At least one of message count (num) or timeout must be set."); - } - - // Ensure that the reply queue for this conversation exists. - initQueueForId(conversationId); - BlockingQueue queue = idsToQueues.get(conversationId); - - // Used to collect the received messages in. - Collection result = new ArrayList(); - - // Used to indicate when the timeout or message count has expired. - boolean receiveMore = true; - - int messageCount = 0; - - // Receive messages until the timeout or message count expires. - do - { - try - { - Message next = null; - - // Try to receive the message with a timeout if one has been set. - if (timeout > 0) - { - next = queue.poll(timeout, TimeUnit.MILLISECONDS); - - // Check if the timeout expired, and stop receiving if so. - if (next == null) - { - receiveMore = false; - } - } - // Receive the message without a timeout. - else - { - next = queue.take(); - } - - // Increment the message count if a message was received. - messageCount += (next != null) ? 1 : 0; - - // Check if all the requested messages were received, and stop receiving if so. - if ((num > 0) && (messageCount >= num)) - { - receiveMore = false; - } - - // Keep the reply-to destination to send replies to. - sendDestination = (next != null) ? next.getJMSReplyTo() : sendDestination; - - if (next != null) - { - result.add(next); - } - } - catch (InterruptedException e) - { - // Restore the threads interrupted status. - Thread.currentThread().interrupt(); - - // Stop receiving but return the messages received so far. - receiveMore = false; - } - } - while (receiveMore); - - return result; - } - - /** - * Completes the conversation. 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() - { - log.debug("public void end(): called"); - - // Ensure that the thread local for the current thread is cleaned up. - // Conversation settings = threadLocals.get(); - // long conversationId = settings.conversationId; - // threadLocals.remove(); - - // Ensure that its queue is removed from the queue map. - BlockingQueue queue = idsToQueues.remove(conversationId); - - // Move any outstanding messages on the threads conversation id into the dead letter box. - queue.drainTo(deadLetterBox); - } - } - - /** - * 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) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - try - { - Long conversationId = Long.parseLong(message.getJMSCorrelationID()); - - // Find the converstaion queue to place the message on. If there is no conversation for the message id, - // the the dead letter box queue is used. - BlockingQueue queue = idsToQueues.get(conversationId); - queue = (queue == null) ? deadLetterBox : queue; - - queue.put(message); - } - catch (JMSException e) - { - throw new RuntimeException(e); - } - catch (InterruptedException e) - { - throw new RuntimeException(e); - } - } - } -} diff --git a/qpid/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties b/qpid/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties deleted file mode 100644 index a5fb611dfa..0000000000 --- a/qpid/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# 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. -# -java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory -connectionfactory.broker = amqp://guest:guest@clientid/?brokerlist='tcp://localhost:5672' diff --git a/qpid/java/integrationtests/src/resources/sustained-log4j.xml b/qpid/java/integrationtests/src/resources/sustained-log4j.xml deleted file mode 100644 index c5ab3137bf..0000000000 --- a/qpid/java/integrationtests/src/resources/sustained-log4j.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- cgit v1.2.1 From d52c60d0d7f5c763a6975dffcdd589ee705da0b7 Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Thu, 24 Apr 2008 00:01:02 +0000 Subject: QPID-832 copy from M2.x git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@651115 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/README.txt | 13 + .../docs/RunningSustainedTests.txt | 17 + .../integrationtests/jar-with-dependencies.xml | 47 ++ qpid/java/integrationtests/pom.xml | 146 ++++ .../interop/clienttestcases/TestCase1DummyRun.java | 135 +++ .../interop/clienttestcases/TestCase2BasicP2P.java | 209 +++++ .../clienttestcases/TestCase3BasicPubSub.java | 239 ++++++ .../clienttestcases/TestCase4P2PMessageSize.java | 214 +++++ .../TestCase5PubSubMessageSize.java | 243 ++++++ .../testcases/InteropTestCase1DummyRun.java | 84 ++ .../testcases/InteropTestCase2BasicP2P.java | 90 ++ .../testcases/InteropTestCase3BasicPubSub.java | 88 ++ .../testcases/InteropTestCase4P2PMessageSize.java | 193 +++++ .../InteropTestCase5PubSubMessageSize.java | 193 +++++ .../qpid/sustained/SustainedClientTestCase.java | 906 +++++++++++++++++++++ .../apache/qpid/sustained/SustainedTestCase.java | 126 +++ .../src/resources/sustained-log4j.xml | 69 ++ 17 files changed, 3012 insertions(+) create mode 100644 qpid/java/integrationtests/README.txt create mode 100644 qpid/java/integrationtests/docs/RunningSustainedTests.txt create mode 100644 qpid/java/integrationtests/jar-with-dependencies.xml create mode 100644 qpid/java/integrationtests/pom.xml create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java create mode 100644 qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java create mode 100644 qpid/java/integrationtests/src/resources/sustained-log4j.xml (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/README.txt b/qpid/java/integrationtests/README.txt new file mode 100644 index 0000000000..00a21883a9 --- /dev/null +++ b/qpid/java/integrationtests/README.txt @@ -0,0 +1,13 @@ +This module contains integration tests, for testing a java client againt *any* broker +implementation or against other clients. These tests must not rely on starting the +Java broker in-vm but must depend on a broker being started independantly before running +the tests in this module. By default tests in this module will expect the broker to be +started on localhost on the default port, but this can be overridden by passing in a +sys property to maven. Interop tests are in this module. Java broker specific tests that +use an in-vm broker should go in the systests module. + +Don't set the tests in this module to run by default as part of the maven build, until +there is a script to start and stop the broker; needed to fully automate these tests. +Interop tests will always be run using a seperate script (not from maven) but it might +be worthwile to script into the maven build starting of the Java broker, and running +these tests against it. \ No newline at end of file diff --git a/qpid/java/integrationtests/docs/RunningSustainedTests.txt b/qpid/java/integrationtests/docs/RunningSustainedTests.txt new file mode 100644 index 0000000000..db4405a32d --- /dev/null +++ b/qpid/java/integrationtests/docs/RunningSustainedTests.txt @@ -0,0 +1,17 @@ +In addition to the integration tests the framework provided by this package also allows for +sustained tests to be run. Currently avaible tests: +- org.apache.qpid.sustained.SustainedClientTestCase : Pub Sub test to determine steady state throughput. + + +Running Tests. + +Run the tests as per the integration tests. +- Start a broker +- Start at least one test client [java org.apache.qpid.interop.TestClient], ensuring unique naming. + +- Start the test coordinator with the 'fanout' engine, on the sustained test case [java org.apache.qpid.test.framework.distributedtesting.Coordinator] + +- Additional Test clients can be started and joined into the running test: [java org.apache.qpid.interop.TestClient -j] + + + diff --git a/qpid/java/integrationtests/jar-with-dependencies.xml b/qpid/java/integrationtests/jar-with-dependencies.xml new file mode 100644 index 0000000000..3e95e7ab22 --- /dev/null +++ b/qpid/java/integrationtests/jar-with-dependencies.xml @@ -0,0 +1,47 @@ + + + + all-test-deps + + jar + + false + + + + + true + test + + + + + target/classes + + + + target/test-classes + + + + diff --git a/qpid/java/integrationtests/pom.xml b/qpid/java/integrationtests/pom.xml new file mode 100644 index 0000000000..851ef27377 --- /dev/null +++ b/qpid/java/integrationtests/pom.xml @@ -0,0 +1,146 @@ + + + + 4.0.0 + org.apache.qpid + qpid-integrationtests + jar + 1.0-incubating-M2.1-SNAPSHOT + Qpid Integration Tests + + + org.apache.qpid + qpid + 1.0-incubating-M2.1-SNAPSHOT + ../pom.xml + + + + .. + + + + + + org.apache.qpid + qpid-client + + + + org.apache.qpid + qpid-systests + + + + org.slf4j + slf4j-log4j12 + 1.4.0 + + + + org.apache.qpid + junit-toolkit + compile + + + + + junit + junit + compile + + + + + net.sf.retrotranslator + retrotranslator-runtime + provided + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + + org.codehaus.mojo + retrotranslator-maven-plugin + + + retro-intergration-tests + package + + translate-project + + + + ${project.build.directory}/${project.build.finalName}-java14.jar + ${retrotranslator.verify} + + ${retrotranslator.1.4-rt-path} + ${retrotranslator.1.4-jce-path} + ${retrotranslator.1.4-jsse-path} + ${retrotranslator.1.4-sasl-path} + + false + java14 + true + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.2-SNAPSHOT + + + jar-with-dependencies.xml + + target + target/assembly/work + + + + + + + + + + false + src/resources + + **/* + + + + + + + diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java new file mode 100644 index 0000000000..45bbfcd148 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java @@ -0,0 +1,135 @@ +/* + * + * 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.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; + +/** + * 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. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Perform test case actions. + *
Generate test reports. + *
+ */ +public class TestCase1DummyRun implements TestClientControlledTest +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase1DummyRun.class); + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC1_DummyRun"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage): called"); + + // Test parameters don't matter, accept all invites. + 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 receivers. + * @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 + { + log.debug("public void assignRole(Roles role, Message assignRoleMessage): called"); + + // Do nothing, both roles are the same. + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + */ + public void start(int numMessages) + { + log.debug("public void start(): called"); + + // Do nothing. + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession 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 + { + log.debug("public Message getReport(Session controlSession): called"); + + // Generate a dummy report, the coordinator expects a report but doesn't care what it is. + return session.createTextMessage("Dummy Run, Ok."); + } + + /** + * Handles incoming test messages. Does nothing. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Ignore any messages. + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java new file mode 100644 index 0000000000..1d30ff7ca6 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java @@ -0,0 +1,209 @@ +/* + * + * 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.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * 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. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages. + *
Generate test reports. + *
+ */ +public class TestCase2BasicP2P implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase2BasicP2P.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The connection to send the test messages on. */ + private Connection connection; + + /** The controlSession to send the test messages on. */ + private Session session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC2_BasicP2P"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // 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 receivers. + * + * @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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Create a new connection to pass the test messages on. + connection = TestUtils.createConnection(TestClient.testContextProperties); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); + Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); + + log.debug("numMessages = " + numMessages); + log.debug("sendDestination = " + sendDestination); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a message producer if so. + case SENDER: + producer = session.createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages. + case RECEIVER: + MessageConsumer consumer = session.createConsumer(sendDestination); + consumer.setMessageListener(this); + break; + } + + connection.start(); + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = session.createTextMessage("test"); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession 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 + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connection. + connection.close(); + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java new file mode 100644 index 0000000000..622ddc2f64 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java @@ -0,0 +1,239 @@ +/* + * + * 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.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the + * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of + * messages sent/received. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages using pub/sub. + *
Generate test reports. + *
+ */ +public class TestCase3BasicPubSub implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase3BasicPubSub.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC3_BasicPubSub"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // 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 receivers. + * + * @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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); + int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); + String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); + + log.debug("numMessages = " + numMessages); + log.debug("numReceivers = " + numReceivers); + log.debug("sendKey = " + sendKey); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = TestUtils.createConnection(TestClient.testContextProperties); + session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + Destination sendDestination = session[0].createTopic(sendKey); + + producer = session[0].createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number + // of receivers connections. + case RECEIVER: + // Create the required number of receivers connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = TestUtils.createConnection(TestClient.testContextProperties); + session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); + + sendDestination = session[i].createTopic(sendKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + consumer.setMessageListener(this); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (Connection conn : connection) + { + conn.start(); + } + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = session[0].createTextMessage("test"); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession 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 + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connections. + for (Connection conn : connection) + { + conn.close(); + } + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java new file mode 100644 index 0000000000..0388c56678 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java @@ -0,0 +1,214 @@ +/* + * + * 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.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * Implements test case 4, P2P messages with message size. Sends/received a specified number of messages to a specified + * route on the default direct exchange, of a specified size. Produces reports on the actual number of messages + * sent/received. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages. + *
Generate test reports. + *
+ */ +public class TestCase4P2PMessageSize implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase4P2PMessageSize.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The size of the test messages to send. */ + private int messageSize; + + /** The connection to send the test messages on. */ + private Connection connection; + + /** The controlSession to send the test messages on. */ + private Session session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC4_P2PMessageSize"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // 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 receivers. + * + * @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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Create a new connection to pass the test messages on. + connection = TestUtils.createConnection(TestClient.testContextProperties); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); + messageSize = assignRoleMessage.getIntProperty("messageSize"); + Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); + + log.debug("numMessages = " + numMessages); + log.debug("sendDestination = " + sendDestination); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a message producer if so. + case SENDER: + producer = session.createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages. + case RECEIVER: + MessageConsumer consumer = session.createConsumer(sendDestination); + consumer.setMessageListener(this); + break; + } + + connection.start(); + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = TestUtils.createTestMessageOfSize(session, messageSize); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession 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 + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connection. + connection.close(); + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java new file mode 100644 index 0000000000..ab59e16ab3 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java @@ -0,0 +1,243 @@ +/* + * + * 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.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * Implements test case 5, pub/sub with message size. Sends/received a specified number of messages to a specified route + * on the default topic exchange, using the specified number of receivers connections, and the specified message size. + * Produces reports on the actual number of messages sent/received. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages using pub/sub. + *
Generate test reports. + *
+ */ +public class TestCase5PubSubMessageSize implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase5PubSubMessageSize.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The size of the test messages to send. */ + private int messageSize; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC5_PubSubMessageSize"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // 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 receivers. + * + * @param assignRoleMessage The role assingment message, contains the full test parameters. + * + * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void assignRole(Roles role, Message assignRoleMessage) throws JMSException + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); + messageSize = assignRoleMessage.getIntProperty("messageSize"); + int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); + String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); + + log.debug("numMessages = " + numMessages); + log.debug("numReceivers = " + numReceivers); + log.debug("sendKey = " + sendKey); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = TestUtils.createConnection(TestClient.testContextProperties); + session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + Destination sendDestination = session[0].createTopic(sendKey); + + producer = session[0].createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number + // of receivers connections. + case RECEIVER: + // Create the required number of receivers connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = TestUtils.createConnection(TestClient.testContextProperties); + session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); + + sendDestination = session[i].createTopic(sendKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + consumer.setMessageListener(this); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (Connection conn : connection) + { + conn.start(); + } + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = TestUtils.createTestMessageOfSize(session[0], messageSize); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession to create the report message in. + * + * @return The report message. + * + * @throws javax.jms.JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. + */ + public Message getReport(Session session) throws JMSException + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connections. + for (Connection conn : connection) + { + conn.close(); + } + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java new file mode 100644 index 0000000000..60cd9f47f3 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java @@ -0,0 +1,84 @@ +/* + * + * 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.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Coordinates test case 1, from the interop test specification. This test connects up the sender and receivers roles, + * and gets some dummy test reports from them, in order to check that the test framework itself is operational. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Exercises the interop testing framework without actually sending any test messages. + * {@link FrameworkBaseCase} + *
+ */ +public class InteropTestCase1DummyRun extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase1DummyRun.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase1DummyRun(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testDummyRun() throws Exception + { + log.debug("public void testDummyRun(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "TC1_DummyRun"); + + /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + // Assert.assertEquals("Expected to get 2 dummy reports.", 2, reports.length); + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC1_DummyRun"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java new file mode 100644 index 0000000000..8983249daa --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java @@ -0,0 +1,90 @@ +/* + * + * 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.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Implements test case 2, from the interop test specification. This test sets up the TC2_BasicP2P test for 50 + * messages. It checks that the sender and receivers reports both indicate that all the test messages were transmitted + * successfully. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Setup p2p test parameters and compare with test output. {@link FrameworkBaseCase} + *
+ */ +public class InteropTestCase2BasicP2P extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase2BasicP2P.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase2BasicP2P(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicP2P() throws Exception + { + log.debug("public void testBasicP2P(): called"); + + Properties testConfig = new Properties(); + testConfig.setProperty("TEST_NAME", "TC2_BasicP2P"); + testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); + testConfig.put("P2P_NUM_MESSAGES", 50); + + /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); + Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived);*/ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC2_BasicP2P"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java new file mode 100644 index 0000000000..6e87c3e3ee --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java @@ -0,0 +1,88 @@ +/* + * + * 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.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + *

+ *
CRC Card
Responsibilities Collaborations + *
Setup pub/sub test parameters and compare with test output. {@link FrameworkBaseCase} + *
+ */ +public class InteropTestCase3BasicPubSub extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase3BasicPubSub.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase3BasicPubSub(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicPubSub() throws Exception + { + log.debug("public void testBasicPubSub(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "TC3_BasicPubSub"); + testConfig.put("PUBSUB_KEY", "tc3route"); + testConfig.put("PUBSUB_NUM_MESSAGES", 10); + testConfig.put("PUBSUB_NUM_RECEIVERS", 5); + + /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 10, messagesSent); + Assert.assertEquals("Received messages did not match up to num sent * num receivers.", messagesSent * 5, + messagesReceived);*/ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC3_BasicPubSub"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java new file mode 100644 index 0000000000..c3f450ec42 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java @@ -0,0 +1,193 @@ +/* + * + * 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.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Implements test case 4, from the interop test specification. This test sets up the TC2_P2PMessageSize test for 50 + * messages, and a variety of message sizes. It checks that the sender and receivers reports both indicate that all + * the test messages were transmitted successfully. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Setup p2p test parameters and compare with test output. {@link FrameworkBaseCase} + *
+ */ +public class InteropTestCase4P2PMessageSize extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase4P2PMessageSize.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase4P2PMessageSize(String name) + { + super(name); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 0K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize0K() throws Exception + { + runTestForMessagesOfSize(0); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 63K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize63K() throws Exception + { + runTestForMessagesOfSize(63 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 64K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize64K() throws Exception + { + runTestForMessagesOfSize(64 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 65K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize65K() throws Exception + { + runTestForMessagesOfSize(65 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 127K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize127K() throws Exception + { + runTestForMessagesOfSize(127 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 128K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize128K() throws Exception + { + runTestForMessagesOfSize(128 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 129K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize129K() throws Exception + { + runTestForMessagesOfSize(129 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 255K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize255K() throws Exception + { + runTestForMessagesOfSize(255 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 256K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize256K() throws Exception + { + runTestForMessagesOfSize(256 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 257K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize257K() throws Exception + { + runTestForMessagesOfSize(257 * 1024); + } + + /** + * Sends 50 test messages of the specified size, and asserts that all were received. + * + * @param size The size of the messages to send in bytes. + */ + private void runTestForMessagesOfSize(int size) + { + Properties testConfig = new Properties(); + testConfig.setProperty("TEST_NAME", "TC4_P2PMessageSize"); + testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); + testConfig.put("P2P_NUM_MESSAGES", 50); + testConfig.put("messageSize", size); + + /*Message[] reports =*/ + getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /* + int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); + Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived); + */ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC4_P2PMessageSize"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java new file mode 100644 index 0000000000..86a0a60ea4 --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java @@ -0,0 +1,193 @@ +/* + * + * 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.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Implements test case 5, from the interop test specification. This test sets up the TC2_PubSubMessageSize test for 10 + * messages, sent to 5 consumers, and a variety of message sizes. It checks that the sender and receivers reports both + * indicate that all the test messages were transmitted successfully. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Setup pub/sub test parameters and compare with test output. {@link FrameworkBaseCase} + *
+ */ +public class InteropTestCase5PubSubMessageSize extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase5PubSubMessageSize.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase5PubSubMessageSize(String name) + { + super(name); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 0K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize0K() throws Exception + { + runTestForMessagesOfSize(0); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 63K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize63K() throws Exception + { + runTestForMessagesOfSize(63 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 64K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize64K() throws Exception + { + runTestForMessagesOfSize(64 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 65K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize65K() throws Exception + { + runTestForMessagesOfSize(65 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 127K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize127K() throws Exception + { + runTestForMessagesOfSize(127 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 128K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize128K() throws Exception + { + runTestForMessagesOfSize(128 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 129K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize129K() throws Exception + { + runTestForMessagesOfSize(129 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 255K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize255K() throws Exception + { + runTestForMessagesOfSize(255 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 256K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize256K() throws Exception + { + runTestForMessagesOfSize(256 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 257K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize257K() throws Exception + { + runTestForMessagesOfSize(257 * 1024); + } + + /** + * Sends 50 test messages of the specified size, and asserts that all were received. + * + * @param size The size of the messages to send in bytes. + */ + private void runTestForMessagesOfSize(int size) + { + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "TC5_PubSubMessageSize"); + testConfig.put("PUBSUB_KEY", "tc3route"); + testConfig.put("PUBSUB_NUM_MESSAGES", 10); + testConfig.put("PUBSUB_NUM_RECEIVERS", 5); + testConfig.put("messageSize", size); + + /*Message[] reports =*/ + getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /* + int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); + Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived); + */ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC5_PubSubMessageSize"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java new file mode 100644 index 0000000000..5ed502287d --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java @@ -0,0 +1,906 @@ +/* + * + * 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.sustained; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQNoConsumersException; +import org.apache.qpid.client.AMQNoRouteException; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub; +import org.apache.qpid.test.framework.TestUtils; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +/** + * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the + * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of + * messages sent/received. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages using pub/sub.
Generate test reports. + *
+ */ +public class SustainedClientTestCase extends TestCase3BasicPubSub implements ExceptionListener, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(SustainedClientTestCase.class); + + /** Used to log to the console. */ + private static final Logger console = Logger.getLogger("SustainedTest"); + + /** The role to be played by the test. */ + private Roles role; + + /** The number of receivers connection to use. */ + private int numReceivers; + + /** The routing key to send them to on the default direct exchange. */ + private Destination sendDestination; + + /** The routing key to send updates to on the default direct exchange. */ + private Destination sendUpdateDestination; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** Adapter that adjusts the send rate based on the updates from clients. */ + SustainedRateAdapter _rateAdapter; + + /** */ + int _batchSize; + + private static final long TEN_MILLI_SEC = 10000000; + private static final int DEBUG_LOG_UPATE_INTERVAL = 10; + private static final int LOG_UPATE_INTERVAL = 10; + private static final boolean SLEEP_PER_MESSAGE = Boolean.getBoolean("sleepPerMessage"); + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "Perf_SustainedPubSub"; + } + + /** + * 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 receivers. + * @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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numReceivers = assignRoleMessage.getIntProperty("SUSTAINED_NUM_RECEIVERS"); + _batchSize = assignRoleMessage.getIntProperty("SUSTAINED_UPDATE_INTERVAL"); + String sendKey = assignRoleMessage.getStringProperty("SUSTAINED_KEY"); + String sendUpdateKey = assignRoleMessage.getStringProperty("SUSTAINED_UPDATE_KEY"); + int ackMode = assignRoleMessage.getIntProperty("ACKNOWLEDGE_MODE"); + String clientName = assignRoleMessage.getStringProperty("CLIENT_NAME"); + + if (log.isDebugEnabled()) + { + log.debug("numReceivers = " + numReceivers); + log.debug("_batchSize = " + _batchSize); + log.debug("ackMode = " + ackMode); + log.debug("sendKey = " + sendKey); + log.debug("sendUpdateKey = " + sendUpdateKey); + log.debug("role = " + role); + } + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + console.info("Creating Sender"); + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = TestUtils.createConnection(TestClient.testContextProperties); + session[0] = connection[0].createSession(false, ackMode); + + // Extract and retain the test parameters. + sendDestination = session[0].createTopic(sendKey); + + connection[0].setExceptionListener(this); + + producer = session[0].createProducer(sendDestination); + + sendUpdateDestination = session[0].createTopic(sendUpdateKey); + MessageConsumer updateConsumer = session[0].createConsumer(sendUpdateDestination); + + _rateAdapter = new SustainedRateAdapter(this); + updateConsumer.setMessageListener(_rateAdapter); + + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number + // of receivers connections. + case RECEIVER: + console.info("Creating Receiver"); + // Create the required number of receivers connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = TestUtils.createConnection(TestClient.testContextProperties); + session[i] = connection[i].createSession(false, ackMode); + + sendDestination = session[i].createTopic(sendKey); + + sendUpdateDestination = session[i].createTopic(sendUpdateKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + + consumer.setMessageListener(new SustainedListener(clientName + "-" + i, _batchSize, session[i], + sendUpdateDestination)); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (int i = 0; i < connection.length; i++) + { + connection[i].start(); + } + } + + /** Performs the test case actions. + * @param numMessages*/ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + _rateAdapter.run(); + break; + case RECEIVER: + + } + + // return from here when you have finished the test.. this will signal the controller and + } + + public void terminate() throws JMSException, InterruptedException + { + if (_rateAdapter != null) + { + _rateAdapter.stop(); + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession 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 + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connections. + for (int i = 0; i < connection.length; i++) + { + connection[i].close(); + } + + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + + return report; + } + + public void onException(JMSException jmsException) + { + Exception linked = jmsException.getLinkedException(); + + if (linked != null) + { + if (log.isDebugEnabled()) + { + log.debug("Linked Exception:" + linked); + } + + if ((linked instanceof AMQNoRouteException) || (linked instanceof AMQNoConsumersException)) + { + if (log.isDebugEnabled()) + { + if (linked instanceof AMQNoConsumersException) + { + log.warn("No clients currently available for message:" + + ((AMQNoConsumersException) linked).getUndeliveredMessage()); + } + else + { + log.warn("No route for message"); + } + } + + // Tell the rate adapter that there are no clients ready yet + _rateAdapter.NO_CLIENTS = true; + } + } + else + { + log.warn("Exception:" + linked); + } + } + + /** + * Inner class that listens for messages and sends a report for the time taken between receiving the 'start' and + * 'end' messages. + */ + class SustainedListener implements MessageListener + { + /** Number of messages received */ + private long _received = 0; + /** The number of messages in the batch */ + private int _batchSize = 0; + /** Record of the when the 'start' messagse was sen */ + private Long _startTime; + /** Message producer to use to send reports */ + MessageProducer _updater; + /** Session to create the report message on */ + Session _session; + /** Record of the client ID used for this SustainedListnener */ + String _client; + + /** + * Main Constructor + * + * @param clientname The _client id used to identify this connection. + * @param batchSize The number of messages that are to be sent per batch. Note: This is not used to + * control the interval between sending reports. + * @param session The controlSession used for communication. + * @param sendDestination The destination that update reports should be sent to. + * + * @throws JMSException My occur if creatingthe Producer fails + */ + public SustainedListener(String clientname, int batchSize, Session session, Destination sendDestination) + throws JMSException + { + _batchSize = batchSize; + _client = clientname; + _session = session; + _updater = session.createProducer(sendDestination); + } + + public void onMessage(Message message) + { + if (log.isDebugEnabled()) + { + log.debug("Message " + _received + "received in listener"); + } + + if (message instanceof TextMessage) + { + try + { + _received++; + if (((TextMessage) message).getText().equals("start")) + { + log.debug("Starting Batch"); + _startTime = System.nanoTime(); + } + else if (((TextMessage) message).getText().equals("end")) + { + if (_startTime != null) + { + long currentTime = System.nanoTime(); + sendStatus(currentTime - _startTime, _received, message.getIntProperty("BATCH")); + log.debug("End Batch"); + } + } + } + catch (JMSException e) + { + // ignore error + } + } + + } + + /** + * sendStatus creates and sends the report back to the publisher + * + * @param time taken for the the last batch + * @param received Total number of messages received. + * @param batchNumber the batch number + * @throws JMSException if an error occurs during the send + */ + private void sendStatus(long time, long received, int batchNumber) throws JMSException + { + Message updateMessage = _session.createTextMessage("update"); + updateMessage.setStringProperty("CLIENT_ID", ":" + _client); + updateMessage.setStringProperty("CONTROL_TYPE", "UPDATE"); + updateMessage.setLongProperty("RECEIVED", received); + updateMessage.setIntProperty("BATCH", batchNumber); + updateMessage.setLongProperty("DURATION", time); + + if (log.isInfoEnabled()) + { + log.info("**** SENDING [" + batchNumber + "]**** " + "CLIENT_ID:" + _client + " RECEIVED:" + received + + " BATCH:" + batchNumber + " DURATION:" + time); + } + + // Output on the main console.info the details of this batch + if ((batchNumber % 10) == 0) + { + console.info("Sending Report [" + batchNumber + "] " + "CLIENT_ID:" + _client + " RECEIVED:" + received + + " BATCH:" + batchNumber + " DURATION:" + time); + } + + _updater.send(updateMessage); + } + } + + /** + * This class is used here to adjust the _delay value which in turn is used to control the number of messages/second + * that are sent through the test system. + * + * By keeping a record of the messages recevied and the average time taken to process the batch size can be + * calculated and so the delay can be adjusted to maintain that rate. + * + * Given that delays of < 10ms can be rounded up the delay is only used between messages if the _delay > 10ms * no + * messages in the batch. Otherwise the delay is used at the end of the batch. + */ + class SustainedRateAdapter implements MessageListener, Runnable + { + private SustainedClientTestCase _client; + private long _batchVariance = Integer.getInteger("batchVariance", 3); // no. batches to allow drifting + private long _timeVariance = TEN_MILLI_SEC * 5; // no. nanos between send and report delay (10ms) + private volatile long _delay; // in nanos + private long _sent; + private Map _slowClients = new HashMap(); + private static final long PAUSE_SLEEP = TEN_MILLI_SEC / 1000; // 10 ms + private static final long NO_CLIENT_SLEEP = 1000; // 1s + private volatile boolean NO_CLIENTS = true; + private int _delayShifting; + private final int REPORTS_WITHOUT_CHANGE = Integer.getInteger("stableReportCount", 5); + private boolean _warmedup = false; + private static final long EXPECTED_TIME_PER_BATCH = 100000L; + private int _warmUpBatches = Integer.getInteger("warmUpBatches", 10); + + SustainedRateAdapter(SustainedClientTestCase client) + { + _client = client; + } + + public void onMessage(Message message) + { + if (log.isDebugEnabled()) + { + log.debug("SustainedRateAdapter onMessage(Message message = " + message + "): called"); + } + + try + { + String controlType = message.getStringProperty("CONTROL_TYPE"); + + // Check if the message is a test invite. + if ("UPDATE".equals(controlType)) + { + NO_CLIENTS = false; + long duration = message.getLongProperty("DURATION"); + long totalReceived = message.getLongProperty("RECEIVED"); + String client = message.getStringProperty("CLIENT_ID"); + int batchNumber = message.getIntProperty("BATCH"); + + if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + log.info("Update Report: CLIENT_ID:" + client + " RECEIVED:" + totalReceived + " Recevied BATCH:" + + batchNumber + " DURATION:" + duration); + } + + recordSlow(client, totalReceived, batchNumber); + + adjustDelay(client, batchNumber, duration); + + // Warm up completes when: + // we haven't warmed up + // and the number of batches sent to each client is at least half of the required warmup batches + if (!_warmedup && (batchNumber >= _warmUpBatches)) + { + _warmedup = true; + _warmup.countDown(); + + } + } + } + catch (JMSException e) + { + // + } + } + + CountDownLatch _warmup = new CountDownLatch(1); + + int _numBatches = 10000; + + // long[] _timings = new long[_numBatches]; + private boolean _running = true; + + public void run() + { + console.info("Warming up"); + + doBatch(_warmUpBatches); + + try + { + // wait for warmup to complete. + _warmup.await(); + + // set delay to the average length of the batches + _delay = _totalDuration / _warmUpBatches / delays.size(); + + console.info("Warmup complete delay set : " + _delay + " based on _totalDuration: " + _totalDuration + + " over no. batches: " + _warmUpBatches + " with client count: " + delays.size()); + + _totalDuration = 0L; + _totalReceived = 0L; + _sent = 0L; + } + catch (InterruptedException e) + { + // + } + + doBatch(_numBatches); + + } + + private void doBatch(int batchSize) // long[] timings, + { + TextMessage testMessage = null; + try + { + testMessage = _client.session[0].createTextMessage("start"); + + for (int batch = 0; batch <= batchSize; batch++) + // while (_running) + { + long start = System.nanoTime(); + + testMessage.setText("start"); + testMessage.setIntProperty("BATCH", batch); + + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + + testMessage.setText("test"); + // start at 2 so start and end count as part of batch + for (int m = 2; m < _batchSize; m++) + { + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + } + + testMessage.setText("end"); + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + + long end = System.nanoTime(); + + long sendtime = end - start; + + if (log.isDebugEnabled()) + { + log.info("Sent batch[" + batch + "](" + _batchSize + ") in " + sendtime); // timings[batch]); + } + + if ((batch % LOG_UPATE_INTERVAL) == 0) + { + console.info("Sent Batch[" + batch + "](" + _batchSize + ")" + status()); + } + + _rateAdapter.sleepBatch(); + + } + } + catch (JMSException e) + { + console.error("Runner ended"); + } + } + + private String status() + { + return " TotalDuration: " + _totalDuration + " for " + delays.size() + " consumers" + " Delay is " + _delay + + " resulting in " + + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch")); + } + + private void sleepBatch() + { + if (checkForSlowClients()) + { // if there werwe slow clients we have already slept so don't sleep anymore again. + return; + } + + if (!SLEEP_PER_MESSAGE) + { + // per batch sleep.. if sleep is to small to spread over the batch. + if (_delay <= (TEN_MILLI_SEC * _batchSize)) + { + sleepLong(_delay); + } + else + { + log.info("Not sleeping _delay > ten*batch is:" + _delay); + } + } + } + + public void stop() + { + _running = false; + } + + Map delays = new HashMap(); + Long _totalReceived = 0L; + Long _totalDuration = 0L; + int _skipUpdate = 0; + + /** + * Adjust the delay for sending messages based on this update from the client + * + * @param client The client that send this update + * @param duration The time taken for the last batch of messagse + * @param batchNumber The reported batchnumber from the client + */ + private void adjustDelay(String client, int batchNumber, long duration) + { + // Retrieve the current total time taken for this client. + Long currentTime = delays.get(client); + + // Add the new duration time to this client + if (currentTime == null) + { + currentTime = duration; + } + else + { + currentTime += duration; + } + + delays.put(client, currentTime); + + long batchesSent = _sent / _batchSize; + + // ensure we don't divide by zero + if (batchesSent == 0) + { + batchesSent = 1L; + } + + _totalReceived += _batchSize; + _totalDuration += duration; + + // calculate average duration accross clients per batch + long averageDuration = _totalDuration / delays.size() / batchesSent; + + // calculate the difference between current send delay and average report delay + long diff = (duration) - averageDuration; + + if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + log.info("TotalDuration:" + _totalDuration + " for " + delays.size() + " consumers." + " on batch: " + + batchesSent + " received batch: " + batchNumber + " Batch Duration: " + duration + " Average: " + + averageDuration + " so diff: " + diff + " for : " + client + " Delay is " + _delay + " resulting in " + + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch"))); + } + + // if the averageDuration differs from the current by more than the specified variane then adjust delay. + if (Math.abs(diff) > _timeVariance) + { + + // if the the _delay is larger than the required duration to send report + // speed up + if (diff > TEN_MILLI_SEC) + { + _delay -= TEN_MILLI_SEC; + + if (_delay < 0) + { + _delay = 0; + log.info("Reset _delay to 0"); + delayStable(); + } + else + { + delayChanged(); + } + + } + else if (diff < 0) // diff < 0 diff cannot be 0 as it is > _timeVariance + { + // the report took longer + _delay += TEN_MILLI_SEC; + delayChanged(); + } + } + else + { + delayStable(); + } + + // If we have a consumer that is behind with the batches. + if ((batchesSent - batchNumber) > _batchVariance) + { + log.debug("Increasing _delay as sending more than receiving"); + + _delay += 2 * TEN_MILLI_SEC; + delayChanged(); + } + + } + + /** Reset the number of iterations before we say the delay has stabilised. */ + private void delayChanged() + { + _delayShifting = REPORTS_WITHOUT_CHANGE; + } + + /** + * Record the fact that delay has stabilised If delay has stablised for REPORTS_WITHOUT_CHANGE then it will + * output Delay stabilised + */ + private void delayStable() + { + _delayShifting--; + + if (_delayShifting < 0) + { + _delayShifting = 0; + console.debug("Delay stabilised:" + _delay); + } + } + + /** + * Checks that the client has received enough messages. If the client has fallen behind then they are put in the + * _slowClients lists which will increase the delay. + * + * @param client The client identifier to check + * @param received the number of messages received by that client + * @param batchNumber + */ + private void recordSlow(String client, long received, int batchNumber) + { + if (Math.abs(batchNumber - (_sent / _batchSize)) > _batchVariance) + { + _slowClients.put(client, received); + } + else + { + _slowClients.remove(client); + } + } + + /** Incrment the number of sent messages and then sleep, if required. */ + public void sentMessage() + { + + _sent++; + + if (_delay > (TEN_MILLI_SEC * _batchSize)) + { + long batchDelay = _delay / _batchSize; + // less than 10ms sleep doesn't always work. + // _delay is in nano seconds + // if (batchDelay < (TEN_MILLI_SEC)) + // { + // sleep(0, (int) batchDelay); + // } + // else + { + // if (batchDelay < 30000000000L) + { + sleepLong(batchDelay); + } + } + } + else + { + if (SLEEP_PER_MESSAGE && (_delay > 0)) + { + sleepLong(_delay / _batchSize); + } + } + } + + /** + * Check at the end of each batch and pause sending messages to allow slow clients to catch up. + * + * @return true if there were slow clients that caught up. + */ + private boolean checkForSlowClients() + { + // This will allways be true as we are running this at the end of each batchSize + // if (_sent % _batchSize == 0) + { + // Cause test to pause when we have slow + if (!_slowClients.isEmpty() || NO_CLIENTS) + { + + while (!_slowClients.isEmpty()) + { + if (log.isInfoEnabled() && ((_sent / _batchSize % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + String clients = ""; + Iterator it = _slowClients.keySet().iterator(); + while (it.hasNext()) + { + clients += it.next(); + if (it.hasNext()) + { + clients += ", "; + } + } + + log.info("Pausing for slow clients:" + clients); + } + + if (console.isDebugEnabled() && ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0)) + { + console.debug(_slowClients.size() + " slow clients."); + } + + sleep(PAUSE_SLEEP); + } + + if (NO_CLIENTS) + { + sleep(NO_CLIENT_SLEEP); + } + + log.debug("Continuing"); + + return true; + } + else + { + if ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0) + { + console.info("Total Delay :" + _delay + " " + + ((_delayShifting == 0) ? "Stablised" : ("Not Stablised(" + _delayShifting + ")"))); + } + } + + } + + return false; + } + + /** + * Sleep normally takes micro-seconds this allows the use of a nano-second value. + * + * @param delay nanoseconds to sleep for. + */ + private void sleepLong(long delay) + { + sleep(delay / 1000000, (int) (delay % 1000000)); + } + + /** + * Sleep for the specified micro-seconds. + * @param sleep microseconds to sleep for. + */ + private void sleep(long sleep) + { + sleep(sleep, 0); + } + + /** + * Perform the sleep , swallowing any InteruptException. + * + * NOTE: If a sleep request is > 10s then reset only sleep for 5s + * + * @param milli to sleep for + * @param nano sub miliseconds to sleep for + */ + private void sleep(long milli, int nano) + { + try + { + log.debug("Sleep:" + milli + ":" + nano); + if (milli > 10000) + { + + if (_delay == milli) + { + _totalDuration = _totalReceived / _batchSize * EXPECTED_TIME_PER_BATCH; + log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + + "s. Reset _totalDuration:" + _totalDuration); + } + else + { + log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + "s"); + } + + milli = 5000; + } + + Thread.sleep(milli, nano); + } + catch (InterruptedException e) + { + // + } + } + + public void setClient(SustainedClientTestCase client) + { + _client = client; + } + } + +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java new file mode 100644 index 0000000000..5979a459ec --- /dev/null +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java @@ -0,0 +1,126 @@ +/* + * + * 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.sustained; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.test.framework.DropInTest; +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import javax.jms.JMSException; +import javax.jms.Message; + +import java.util.Properties; + +/** + * SustainedTestCase is a {@link FrameworkBaseCase} that runs the "Perf_SustainedPubSub" test case. This consists of one + * test client sending, and several receiving, and attempts to find the highest rate at which messages can be broadcast + * to the receivers. It is also a {@link DropInTest} to which more test clients may be added during a test run. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
+ *
+ */ +public class SustainedTestCase extends FrameworkBaseCase implements DropInTest +{ + /** Used for debugging. */ + Logger log = Logger.getLogger(SustainedTestCase.class); + + /** Holds the root name of the topic on which to send the test messages. */ + private static final String SUSTAINED_KEY = "Perf_SustainedPubSub"; + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public SustainedTestCase(String name) + { + super(name); + } + + /** + * Performs a single test run of the sustained test. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicPubSub() throws Exception + { + log.debug("public void testSinglePubSubCycle(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "Perf_SustainedPubSub"); + testConfig.put("SUSTAINED_KEY", SUSTAINED_KEY); + testConfig.put("SUSTAINED_NUM_RECEIVERS", Integer.getInteger("numReceives", 2)); + testConfig.put("SUSTAINED_UPDATE_INTERVAL", Integer.getInteger("batchSize", 1000)); + testConfig.put("SUSTAINED_UPDATE_KEY", SUSTAINED_KEY + ".UPDATE"); + testConfig.put("ACKNOWLEDGE_MODE", Integer.getInteger("ackMode", AMQSession.AUTO_ACKNOWLEDGE)); + + log.info("Created Config: " + testConfig.entrySet().toArray()); + + getCircuitFactory().sequenceTest(null, null, testConfig); + } + + /** + * Accepts a late joining client into this test case. The client will be enlisted with a control message + * with the 'CONTROL_TYPE' field set to the value 'LATEJOIN'. It should also provide values for the fields: + * + *

+ *
CLIENT_NAME A unique name for the new client. + *
CLIENT_PRIVATE_CONTROL_KEY The key for the route on which the client receives its control messages. + *
+ * + * @param message The late joiners join message. + * + * @throws JMSException Any JMS Exception are allowed to fall through, indicating that the join failed. + */ + public void lateJoin(Message message) throws JMSException + { + throw new RuntimeException("Not implemented."); + /* + // Extract the joining clients details from its join request message. + TestClientDetails clientDetails = new TestClientDetails(); + clientDetails.clientName = message.getStringProperty("CLIENT_NAME"); + clientDetails.privateControlKey = message.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); + + // Register the joining client, but do block for confirmation as cannot do a synchronous receivers during this + // method call, as it may have been called from an 'onMessage' method. + assignReceiverRole(clientDetails, new Properties(), false); + */ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as known to the test + * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test + * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case + * name "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "Perf_SustainedPubSub"; + } +} diff --git a/qpid/java/integrationtests/src/resources/sustained-log4j.xml b/qpid/java/integrationtests/src/resources/sustained-log4j.xml new file mode 100644 index 0000000000..c5ab3137bf --- /dev/null +++ b/qpid/java/integrationtests/src/resources/sustained-log4j.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.1 From d3d44fd5452e21c4a8fc3d66596e6ff9f8274f2f Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Thu, 24 Apr 2008 00:36:48 +0000 Subject: QPID-832 sync build stuff from thegreatmerge git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@651126 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/build.xml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 qpid/java/integrationtests/build.xml (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/build.xml b/qpid/java/integrationtests/build.xml new file mode 100644 index 0000000000..29d066edb7 --- /dev/null +++ b/qpid/java/integrationtests/build.xml @@ -0,0 +1,28 @@ + + + + + + + + + -- cgit v1.2.1 From c02e3dbaed139fae55d34c8e66f72506cbf5a86c Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Thu, 24 Apr 2008 09:53:13 +0000 Subject: QPID-832 update pom version git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@651211 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/pom.xml b/qpid/java/integrationtests/pom.xml index 851ef27377..bbc12d4d00 100644 --- a/qpid/java/integrationtests/pom.xml +++ b/qpid/java/integrationtests/pom.xml @@ -24,13 +24,13 @@ org.apache.qpid qpid-integrationtests jar - 1.0-incubating-M2.1-SNAPSHOT + 1.0-incubating-M3-SNAPSHOT Qpid Integration Tests org.apache.qpid qpid - 1.0-incubating-M2.1-SNAPSHOT + 1.0-incubating-M3-SNAPSHOT ../pom.xml -- cgit v1.2.1 From e32debe1df7d0a837e30cd937fb7a18fc5cfa203 Mon Sep 17 00:00:00 2001 From: Robert Godfrey Date: Thu, 24 Apr 2008 17:49:03 +0000 Subject: QPID-832 : Fix eol-style git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@651325 13f79535-47bb-0310-9956-ffa450edef68 --- .../interop/clienttestcases/TestCase1DummyRun.java | 270 +-- .../interop/clienttestcases/TestCase2BasicP2P.java | 418 ++--- .../clienttestcases/TestCase3BasicPubSub.java | 478 +++--- .../clienttestcases/TestCase4P2PMessageSize.java | 428 ++--- .../TestCase5PubSubMessageSize.java | 486 +++--- .../testcases/InteropTestCase1DummyRun.java | 168 +- .../testcases/InteropTestCase2BasicP2P.java | 180 +- .../testcases/InteropTestCase3BasicPubSub.java | 176 +- .../testcases/InteropTestCase4P2PMessageSize.java | 386 ++--- .../InteropTestCase5PubSubMessageSize.java | 386 ++--- .../qpid/sustained/SustainedClientTestCase.java | 1812 ++++++++++---------- .../apache/qpid/sustained/SustainedTestCase.java | 252 +-- 12 files changed, 2720 insertions(+), 2720 deletions(-) (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java index 45bbfcd148..db17c7aacc 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java @@ -1,135 +1,135 @@ -/* - * - * 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.clienttestcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; - -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.Session; - -/** - * 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. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Perform test case actions. - *
Generate test reports. - *
- */ -public class TestCase1DummyRun implements TestClientControlledTest -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase1DummyRun.class); - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "TC1_DummyRun"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage): called"); - - // Test parameters don't matter, accept all invites. - 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 receivers. - * @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 - { - log.debug("public void assignRole(Roles role, Message assignRoleMessage): called"); - - // Do nothing, both roles are the same. - } - - /** - * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. - * - * @param numMessages The number of test messages to send. - */ - public void start(int numMessages) - { - log.debug("public void start(): called"); - - // Do nothing. - } - - /** - * Gets a report on the actions performed by the test case in its assigned role. - * - * @param session The controlSession 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 - { - log.debug("public Message getReport(Session controlSession): called"); - - // Generate a dummy report, the coordinator expects a report but doesn't care what it is. - return session.createTextMessage("Dummy Run, Ok."); - } - - /** - * Handles incoming test messages. Does nothing. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Ignore any messages. - } -} +/* + * + * 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.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; + +/** + * 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. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Perform test case actions. + *
Generate test reports. + *
+ */ +public class TestCase1DummyRun implements TestClientControlledTest +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase1DummyRun.class); + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC1_DummyRun"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage): called"); + + // Test parameters don't matter, accept all invites. + 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 receivers. + * @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 + { + log.debug("public void assignRole(Roles role, Message assignRoleMessage): called"); + + // Do nothing, both roles are the same. + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + */ + public void start(int numMessages) + { + log.debug("public void start(): called"); + + // Do nothing. + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession 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 + { + log.debug("public Message getReport(Session controlSession): called"); + + // Generate a dummy report, the coordinator expects a report but doesn't care what it is. + return session.createTextMessage("Dummy Run, Ok."); + } + + /** + * Handles incoming test messages. Does nothing. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Ignore any messages. + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java index 1d30ff7ca6..36d3cce7f7 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java @@ -1,209 +1,209 @@ -/* - * - * 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.clienttestcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.TestUtils; -import org.apache.qpid.test.framework.distributedtesting.TestClient; -import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; - -import javax.jms.*; - -/** - * 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. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Send required number of test messages. - *
Generate test reports. - *
- */ -public class TestCase2BasicP2P implements TestClientControlledTest, MessageListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase2BasicP2P.class); - - /** Holds the count of test messages received. */ - private int messageCount; - - /** The role to be played by the test. */ - private Roles role; - - /** The number of test messages to send. */ - private int numMessages; - - /** The connection to send the test messages on. */ - private Connection connection; - - /** The controlSession to send the test messages on. */ - private Session session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "TC2_BasicP2P"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); - - // 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 receivers. - * - * @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 - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Reset the message count for a new test. - messageCount = 0; - - // Take note of the role to be played. - this.role = role; - - // Create a new connection to pass the test messages on. - connection = TestUtils.createConnection(TestClient.testContextProperties); - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Extract and retain the test parameters. - numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); - Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); - - log.debug("numMessages = " + numMessages); - log.debug("sendDestination = " + sendDestination); - log.debug("role = " + role); - - switch (role) - { - // Check if the sender role is being assigned, and set up a message producer if so. - case SENDER: - producer = session.createProducer(sendDestination); - break; - - // Otherwise the receivers role is being assigned, so set this up to listen for messages. - case RECEIVER: - MessageConsumer consumer = session.createConsumer(sendDestination); - consumer.setMessageListener(this); - break; - } - - connection.start(); - } - - /** - * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. - * - * @param numMessages The number of test messages to send. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void start(int numMessages) throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - if (role.equals(Roles.SENDER)) - { - Message testMessage = session.createTextMessage("test"); - - for (int i = 0; i < this.numMessages; i++) - { - producer.send(testMessage); - - // Increment the message count. - messageCount++; - } - } - } - - /** - * Gets a report on the actions performed by the test case in its assigned role. - * - * @param session The controlSession 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 - { - log.debug("public Message getReport(Session controlSession): called"); - - // Close the test connection. - connection.close(); - - // Generate a report message containing the count of the number of messages passed. - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - report.setIntProperty("MESSAGE_COUNT", messageCount); - - return report; - } - - /** - * Counts incoming test messages. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Increment the message count. - messageCount++; - } -} +/* + * + * 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.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * 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. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages. + *
Generate test reports. + *
+ */ +public class TestCase2BasicP2P implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase2BasicP2P.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The connection to send the test messages on. */ + private Connection connection; + + /** The controlSession to send the test messages on. */ + private Session session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC2_BasicP2P"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // 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 receivers. + * + * @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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Create a new connection to pass the test messages on. + connection = TestUtils.createConnection(TestClient.testContextProperties); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); + Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); + + log.debug("numMessages = " + numMessages); + log.debug("sendDestination = " + sendDestination); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a message producer if so. + case SENDER: + producer = session.createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages. + case RECEIVER: + MessageConsumer consumer = session.createConsumer(sendDestination); + consumer.setMessageListener(this); + break; + } + + connection.start(); + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = session.createTextMessage("test"); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession 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 + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connection. + connection.close(); + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java index 622ddc2f64..205472716b 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java @@ -1,239 +1,239 @@ -/* - * - * 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.clienttestcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.TestUtils; -import org.apache.qpid.test.framework.distributedtesting.TestClient; -import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; - -import javax.jms.*; - -/** - * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the - * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of - * messages sent/received. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Send required number of test messages using pub/sub. - *
Generate test reports. - *
- */ -public class TestCase3BasicPubSub implements TestClientControlledTest, MessageListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase3BasicPubSub.class); - - /** Holds the count of test messages received. */ - private int messageCount; - - /** The role to be played by the test. */ - private Roles role; - - /** The number of test messages to send. */ - private int numMessages; - - /** The connections to send/receive the test messages on. */ - private Connection[] connection; - - /** The sessions to send/receive the test messages on. */ - private Session[] session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "TC3_BasicPubSub"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); - - // 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 receivers. - * - * @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 - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Reset the message count for a new test. - messageCount = 0; - - // Take note of the role to be played. - this.role = role; - - // Extract and retain the test parameters. - numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); - int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); - String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); - - log.debug("numMessages = " + numMessages); - log.debug("numReceivers = " + numReceivers); - log.debug("sendKey = " + sendKey); - log.debug("role = " + role); - - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - // Create a new connection to pass the test messages on. - connection = new Connection[1]; - session = new Session[1]; - - connection[0] = TestUtils.createConnection(TestClient.testContextProperties); - session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Extract and retain the test parameters. - Destination sendDestination = session[0].createTopic(sendKey); - - producer = session[0].createProducer(sendDestination); - break; - - // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number - // of receivers connections. - case RECEIVER: - // Create the required number of receivers connections. - connection = new Connection[numReceivers]; - session = new Session[numReceivers]; - - for (int i = 0; i < numReceivers; i++) - { - connection[i] = TestUtils.createConnection(TestClient.testContextProperties); - session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); - - sendDestination = session[i].createTopic(sendKey); - - MessageConsumer consumer = session[i].createConsumer(sendDestination); - consumer.setMessageListener(this); - } - - break; - } - - // Start all the connection dispatcher threads running. - for (Connection conn : connection) - { - conn.start(); - } - } - - /** - * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. - * - * @param numMessages The number of test messages to send. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void start(int numMessages) throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - if (role.equals(Roles.SENDER)) - { - Message testMessage = session[0].createTextMessage("test"); - - for (int i = 0; i < this.numMessages; i++) - { - producer.send(testMessage); - - // Increment the message count. - messageCount++; - } - } - } - - /** - * Gets a report on the actions performed by the test case in its assigned role. - * - * @param session The controlSession 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 - { - log.debug("public Message getReport(Session controlSession): called"); - - // Close the test connections. - for (Connection conn : connection) - { - conn.close(); - } - - // Generate a report message containing the count of the number of messages passed. - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - report.setIntProperty("MESSAGE_COUNT", messageCount); - - return report; - } - - /** - * Counts incoming test messages. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Increment the message count. - messageCount++; - } -} +/* + * + * 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.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the + * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of + * messages sent/received. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages using pub/sub. + *
Generate test reports. + *
+ */ +public class TestCase3BasicPubSub implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase3BasicPubSub.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC3_BasicPubSub"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // 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 receivers. + * + * @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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); + int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); + String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); + + log.debug("numMessages = " + numMessages); + log.debug("numReceivers = " + numReceivers); + log.debug("sendKey = " + sendKey); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = TestUtils.createConnection(TestClient.testContextProperties); + session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + Destination sendDestination = session[0].createTopic(sendKey); + + producer = session[0].createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number + // of receivers connections. + case RECEIVER: + // Create the required number of receivers connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = TestUtils.createConnection(TestClient.testContextProperties); + session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); + + sendDestination = session[i].createTopic(sendKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + consumer.setMessageListener(this); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (Connection conn : connection) + { + conn.start(); + } + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = session[0].createTextMessage("test"); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession 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 + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connections. + for (Connection conn : connection) + { + conn.close(); + } + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java index 0388c56678..3730233264 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java @@ -1,214 +1,214 @@ -/* - * - * 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.clienttestcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.TestUtils; -import org.apache.qpid.test.framework.distributedtesting.TestClient; -import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; - -import javax.jms.*; - -/** - * Implements test case 4, P2P messages with message size. Sends/received a specified number of messages to a specified - * route on the default direct exchange, of a specified size. Produces reports on the actual number of messages - * sent/received. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Send required number of test messages. - *
Generate test reports. - *
- */ -public class TestCase4P2PMessageSize implements TestClientControlledTest, MessageListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase4P2PMessageSize.class); - - /** Holds the count of test messages received. */ - private int messageCount; - - /** The role to be played by the test. */ - private Roles role; - - /** The number of test messages to send. */ - private int numMessages; - - /** The size of the test messages to send. */ - private int messageSize; - - /** The connection to send the test messages on. */ - private Connection connection; - - /** The controlSession to send the test messages on. */ - private Session session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "TC4_P2PMessageSize"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); - - // 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 receivers. - * - * @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 - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Reset the message count for a new test. - messageCount = 0; - - // Take note of the role to be played. - this.role = role; - - // Create a new connection to pass the test messages on. - connection = TestUtils.createConnection(TestClient.testContextProperties); - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Extract and retain the test parameters. - numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); - messageSize = assignRoleMessage.getIntProperty("messageSize"); - Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); - - log.debug("numMessages = " + numMessages); - log.debug("sendDestination = " + sendDestination); - log.debug("role = " + role); - - switch (role) - { - // Check if the sender role is being assigned, and set up a message producer if so. - case SENDER: - producer = session.createProducer(sendDestination); - break; - - // Otherwise the receivers role is being assigned, so set this up to listen for messages. - case RECEIVER: - MessageConsumer consumer = session.createConsumer(sendDestination); - consumer.setMessageListener(this); - break; - } - - connection.start(); - } - - /** - * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. - * - * @param numMessages The number of test messages to send. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void start(int numMessages) throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - if (role.equals(Roles.SENDER)) - { - Message testMessage = TestUtils.createTestMessageOfSize(session, messageSize); - - for (int i = 0; i < this.numMessages; i++) - { - producer.send(testMessage); - - // Increment the message count. - messageCount++; - } - } - } - - /** - * Gets a report on the actions performed by the test case in its assigned role. - * - * @param session The controlSession 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 - { - log.debug("public Message getReport(Session controlSession): called"); - - // Close the test connection. - connection.close(); - - // Generate a report message containing the count of the number of messages passed. - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - report.setIntProperty("MESSAGE_COUNT", messageCount); - - return report; - } - - /** - * Counts incoming test messages. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Increment the message count. - messageCount++; - } -} +/* + * + * 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.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * Implements test case 4, P2P messages with message size. Sends/received a specified number of messages to a specified + * route on the default direct exchange, of a specified size. Produces reports on the actual number of messages + * sent/received. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages. + *
Generate test reports. + *
+ */ +public class TestCase4P2PMessageSize implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase4P2PMessageSize.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The size of the test messages to send. */ + private int messageSize; + + /** The connection to send the test messages on. */ + private Connection connection; + + /** The controlSession to send the test messages on. */ + private Session session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC4_P2PMessageSize"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // 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 receivers. + * + * @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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Create a new connection to pass the test messages on. + connection = TestUtils.createConnection(TestClient.testContextProperties); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); + messageSize = assignRoleMessage.getIntProperty("messageSize"); + Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); + + log.debug("numMessages = " + numMessages); + log.debug("sendDestination = " + sendDestination); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a message producer if so. + case SENDER: + producer = session.createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages. + case RECEIVER: + MessageConsumer consumer = session.createConsumer(sendDestination); + consumer.setMessageListener(this); + break; + } + + connection.start(); + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = TestUtils.createTestMessageOfSize(session, messageSize); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession 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 + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connection. + connection.close(); + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java index ab59e16ab3..f601712bc9 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java @@ -1,243 +1,243 @@ -/* - * - * 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.clienttestcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.TestUtils; -import org.apache.qpid.test.framework.distributedtesting.TestClient; -import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; - -import javax.jms.*; - -/** - * Implements test case 5, pub/sub with message size. Sends/received a specified number of messages to a specified route - * on the default topic exchange, using the specified number of receivers connections, and the specified message size. - * Produces reports on the actual number of messages sent/received. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Send required number of test messages using pub/sub. - *
Generate test reports. - *
- */ -public class TestCase5PubSubMessageSize implements TestClientControlledTest, MessageListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase5PubSubMessageSize.class); - - /** Holds the count of test messages received. */ - private int messageCount; - - /** The role to be played by the test. */ - private Roles role; - - /** The number of test messages to send. */ - private int numMessages; - - /** The size of the test messages to send. */ - private int messageSize; - - /** The connections to send/receive the test messages on. */ - private Connection[] connection; - - /** The sessions to send/receive the test messages on. */ - private Session[] session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "TC5_PubSubMessageSize"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); - - // 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 receivers. - * - * @param assignRoleMessage The role assingment message, contains the full test parameters. - * - * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void assignRole(Roles role, Message assignRoleMessage) throws JMSException - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Reset the message count for a new test. - messageCount = 0; - - // Take note of the role to be played. - this.role = role; - - // Extract and retain the test parameters. - numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); - messageSize = assignRoleMessage.getIntProperty("messageSize"); - int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); - String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); - - log.debug("numMessages = " + numMessages); - log.debug("numReceivers = " + numReceivers); - log.debug("sendKey = " + sendKey); - log.debug("role = " + role); - - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - // Create a new connection to pass the test messages on. - connection = new Connection[1]; - session = new Session[1]; - - connection[0] = TestUtils.createConnection(TestClient.testContextProperties); - session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Extract and retain the test parameters. - Destination sendDestination = session[0].createTopic(sendKey); - - producer = session[0].createProducer(sendDestination); - break; - - // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number - // of receivers connections. - case RECEIVER: - // Create the required number of receivers connections. - connection = new Connection[numReceivers]; - session = new Session[numReceivers]; - - for (int i = 0; i < numReceivers; i++) - { - connection[i] = TestUtils.createConnection(TestClient.testContextProperties); - session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); - - sendDestination = session[i].createTopic(sendKey); - - MessageConsumer consumer = session[i].createConsumer(sendDestination); - consumer.setMessageListener(this); - } - - break; - } - - // Start all the connection dispatcher threads running. - for (Connection conn : connection) - { - conn.start(); - } - } - - /** - * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. - * - * @param numMessages The number of test messages to send. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void start(int numMessages) throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - if (role.equals(Roles.SENDER)) - { - Message testMessage = TestUtils.createTestMessageOfSize(session[0], messageSize); - - for (int i = 0; i < this.numMessages; i++) - { - producer.send(testMessage); - - // Increment the message count. - messageCount++; - } - } - } - - /** - * Gets a report on the actions performed by the test case in its assigned role. - * - * @param session The controlSession to create the report message in. - * - * @return The report message. - * - * @throws javax.jms.JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. - */ - public Message getReport(Session session) throws JMSException - { - log.debug("public Message getReport(Session controlSession): called"); - - // Close the test connections. - for (Connection conn : connection) - { - conn.close(); - } - - // Generate a report message containing the count of the number of messages passed. - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - report.setIntProperty("MESSAGE_COUNT", messageCount); - - return report; - } - - /** - * Counts incoming test messages. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Increment the message count. - messageCount++; - } -} +/* + * + * 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.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * Implements test case 5, pub/sub with message size. Sends/received a specified number of messages to a specified route + * on the default topic exchange, using the specified number of receivers connections, and the specified message size. + * Produces reports on the actual number of messages sent/received. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages using pub/sub. + *
Generate test reports. + *
+ */ +public class TestCase5PubSubMessageSize implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase5PubSubMessageSize.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The size of the test messages to send. */ + private int messageSize; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "TC5_PubSubMessageSize"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // 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 receivers. + * + * @param assignRoleMessage The role assingment message, contains the full test parameters. + * + * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void assignRole(Roles role, Message assignRoleMessage) throws JMSException + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); + messageSize = assignRoleMessage.getIntProperty("messageSize"); + int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); + String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); + + log.debug("numMessages = " + numMessages); + log.debug("numReceivers = " + numReceivers); + log.debug("sendKey = " + sendKey); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = TestUtils.createConnection(TestClient.testContextProperties); + session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + Destination sendDestination = session[0].createTopic(sendKey); + + producer = session[0].createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number + // of receivers connections. + case RECEIVER: + // Create the required number of receivers connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = TestUtils.createConnection(TestClient.testContextProperties); + session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); + + sendDestination = session[i].createTopic(sendKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + consumer.setMessageListener(this); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (Connection conn : connection) + { + conn.start(); + } + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = TestUtils.createTestMessageOfSize(session[0], messageSize); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession to create the report message in. + * + * @return The report message. + * + * @throws javax.jms.JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. + */ + public Message getReport(Session session) throws JMSException + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connections. + for (Connection conn : connection) + { + conn.close(); + } + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java index 60cd9f47f3..a2e4a00aa6 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java @@ -1,84 +1,84 @@ -/* - * - * 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.testcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.FrameworkBaseCase; - -import java.util.Properties; - -/** - * Coordinates test case 1, from the interop test specification. This test connects up the sender and receivers roles, - * and gets some dummy test reports from them, in order to check that the test framework itself is operational. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Exercises the interop testing framework without actually sending any test messages. - * {@link FrameworkBaseCase} - *
- */ -public class InteropTestCase1DummyRun extends FrameworkBaseCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(InteropTestCase1DummyRun.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public InteropTestCase1DummyRun(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testDummyRun() throws Exception - { - log.debug("public void testDummyRun(): called"); - - Properties testConfig = new Properties(); - testConfig.put("TEST_NAME", "TC1_DummyRun"); - - /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); - - // Compare sender and receivers reports. - // Assert.assertEquals("Expected to get 2 dummy reports.", 2, reports.length); - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC1_DummyRun"; - } -} +/* + * + * 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.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Coordinates test case 1, from the interop test specification. This test connects up the sender and receivers roles, + * and gets some dummy test reports from them, in order to check that the test framework itself is operational. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Exercises the interop testing framework without actually sending any test messages. + * {@link FrameworkBaseCase} + *
+ */ +public class InteropTestCase1DummyRun extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase1DummyRun.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase1DummyRun(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testDummyRun() throws Exception + { + log.debug("public void testDummyRun(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "TC1_DummyRun"); + + /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + // Assert.assertEquals("Expected to get 2 dummy reports.", 2, reports.length); + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC1_DummyRun"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java index 8983249daa..6d6515f1fd 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java @@ -1,90 +1,90 @@ -/* - * - * 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.testcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.FrameworkBaseCase; - -import java.util.Properties; - -/** - * Implements test case 2, from the interop test specification. This test sets up the TC2_BasicP2P test for 50 - * messages. It checks that the sender and receivers reports both indicate that all the test messages were transmitted - * successfully. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Setup p2p test parameters and compare with test output. {@link FrameworkBaseCase} - *
- */ -public class InteropTestCase2BasicP2P extends FrameworkBaseCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(InteropTestCase2BasicP2P.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public InteropTestCase2BasicP2P(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testBasicP2P() throws Exception - { - log.debug("public void testBasicP2P(): called"); - - Properties testConfig = new Properties(); - testConfig.setProperty("TEST_NAME", "TC2_BasicP2P"); - testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); - testConfig.put("P2P_NUM_MESSAGES", 50); - - /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); - - // Compare sender and receivers reports. - /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); - int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); - - Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); - Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived);*/ - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC2_BasicP2P"; - } -} +/* + * + * 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.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Implements test case 2, from the interop test specification. This test sets up the TC2_BasicP2P test for 50 + * messages. It checks that the sender and receivers reports both indicate that all the test messages were transmitted + * successfully. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Setup p2p test parameters and compare with test output. {@link FrameworkBaseCase} + *
+ */ +public class InteropTestCase2BasicP2P extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase2BasicP2P.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase2BasicP2P(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicP2P() throws Exception + { + log.debug("public void testBasicP2P(): called"); + + Properties testConfig = new Properties(); + testConfig.setProperty("TEST_NAME", "TC2_BasicP2P"); + testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); + testConfig.put("P2P_NUM_MESSAGES", 50); + + /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); + Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived);*/ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC2_BasicP2P"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java index 6e87c3e3ee..2faca91e73 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java @@ -1,88 +1,88 @@ -/* - * - * 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.testcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.FrameworkBaseCase; - -import java.util.Properties; - -/** - *

- *
CRC Card
Responsibilities Collaborations - *
Setup pub/sub test parameters and compare with test output. {@link FrameworkBaseCase} - *
- */ -public class InteropTestCase3BasicPubSub extends FrameworkBaseCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(InteropTestCase3BasicPubSub.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public InteropTestCase3BasicPubSub(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testBasicPubSub() throws Exception - { - log.debug("public void testBasicPubSub(): called"); - - Properties testConfig = new Properties(); - testConfig.put("TEST_NAME", "TC3_BasicPubSub"); - testConfig.put("PUBSUB_KEY", "tc3route"); - testConfig.put("PUBSUB_NUM_MESSAGES", 10); - testConfig.put("PUBSUB_NUM_RECEIVERS", 5); - - /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); - - // Compare sender and receivers reports. - /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); - int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); - - Assert.assertEquals("The requested number of messages were not sent.", 10, messagesSent); - Assert.assertEquals("Received messages did not match up to num sent * num receivers.", messagesSent * 5, - messagesReceived);*/ - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC3_BasicPubSub"; - } -} +/* + * + * 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.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + *

+ *
CRC Card
Responsibilities Collaborations + *
Setup pub/sub test parameters and compare with test output. {@link FrameworkBaseCase} + *
+ */ +public class InteropTestCase3BasicPubSub extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase3BasicPubSub.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase3BasicPubSub(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicPubSub() throws Exception + { + log.debug("public void testBasicPubSub(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "TC3_BasicPubSub"); + testConfig.put("PUBSUB_KEY", "tc3route"); + testConfig.put("PUBSUB_NUM_MESSAGES", 10); + testConfig.put("PUBSUB_NUM_RECEIVERS", 5); + + /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 10, messagesSent); + Assert.assertEquals("Received messages did not match up to num sent * num receivers.", messagesSent * 5, + messagesReceived);*/ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC3_BasicPubSub"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java index c3f450ec42..2d64f2b805 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java @@ -1,193 +1,193 @@ -/* - * - * 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.testcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.FrameworkBaseCase; - -import java.util.Properties; - -/** - * Implements test case 4, from the interop test specification. This test sets up the TC2_P2PMessageSize test for 50 - * messages, and a variety of message sizes. It checks that the sender and receivers reports both indicate that all - * the test messages were transmitted successfully. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Setup p2p test parameters and compare with test output. {@link FrameworkBaseCase} - *
- */ -public class InteropTestCase4P2PMessageSize extends FrameworkBaseCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(InteropTestCase4P2PMessageSize.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public InteropTestCase4P2PMessageSize(String name) - { - super(name); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 0K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize0K() throws Exception - { - runTestForMessagesOfSize(0); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 63K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize63K() throws Exception - { - runTestForMessagesOfSize(63 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 64K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize64K() throws Exception - { - runTestForMessagesOfSize(64 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 65K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize65K() throws Exception - { - runTestForMessagesOfSize(65 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 127K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize127K() throws Exception - { - runTestForMessagesOfSize(127 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 128K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize128K() throws Exception - { - runTestForMessagesOfSize(128 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 129K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize129K() throws Exception - { - runTestForMessagesOfSize(129 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 255K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize255K() throws Exception - { - runTestForMessagesOfSize(255 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 256K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize256K() throws Exception - { - runTestForMessagesOfSize(256 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 257K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize257K() throws Exception - { - runTestForMessagesOfSize(257 * 1024); - } - - /** - * Sends 50 test messages of the specified size, and asserts that all were received. - * - * @param size The size of the messages to send in bytes. - */ - private void runTestForMessagesOfSize(int size) - { - Properties testConfig = new Properties(); - testConfig.setProperty("TEST_NAME", "TC4_P2PMessageSize"); - testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); - testConfig.put("P2P_NUM_MESSAGES", 50); - testConfig.put("messageSize", size); - - /*Message[] reports =*/ - getCircuitFactory().sequenceTest(null, null, testConfig); - - // Compare sender and receivers reports. - /* - int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); - int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); - - Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); - Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived); - */ - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC4_P2PMessageSize"; - } -} +/* + * + * 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.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Implements test case 4, from the interop test specification. This test sets up the TC2_P2PMessageSize test for 50 + * messages, and a variety of message sizes. It checks that the sender and receivers reports both indicate that all + * the test messages were transmitted successfully. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Setup p2p test parameters and compare with test output. {@link FrameworkBaseCase} + *
+ */ +public class InteropTestCase4P2PMessageSize extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase4P2PMessageSize.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase4P2PMessageSize(String name) + { + super(name); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 0K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize0K() throws Exception + { + runTestForMessagesOfSize(0); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 63K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize63K() throws Exception + { + runTestForMessagesOfSize(63 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 64K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize64K() throws Exception + { + runTestForMessagesOfSize(64 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 65K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize65K() throws Exception + { + runTestForMessagesOfSize(65 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 127K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize127K() throws Exception + { + runTestForMessagesOfSize(127 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 128K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize128K() throws Exception + { + runTestForMessagesOfSize(128 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 129K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize129K() throws Exception + { + runTestForMessagesOfSize(129 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 255K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize255K() throws Exception + { + runTestForMessagesOfSize(255 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 256K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize256K() throws Exception + { + runTestForMessagesOfSize(256 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 257K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize257K() throws Exception + { + runTestForMessagesOfSize(257 * 1024); + } + + /** + * Sends 50 test messages of the specified size, and asserts that all were received. + * + * @param size The size of the messages to send in bytes. + */ + private void runTestForMessagesOfSize(int size) + { + Properties testConfig = new Properties(); + testConfig.setProperty("TEST_NAME", "TC4_P2PMessageSize"); + testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); + testConfig.put("P2P_NUM_MESSAGES", 50); + testConfig.put("messageSize", size); + + /*Message[] reports =*/ + getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /* + int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); + Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived); + */ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC4_P2PMessageSize"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java index 86a0a60ea4..23d33fc115 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java @@ -1,193 +1,193 @@ -/* - * - * 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.testcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.FrameworkBaseCase; - -import java.util.Properties; - -/** - * Implements test case 5, from the interop test specification. This test sets up the TC2_PubSubMessageSize test for 10 - * messages, sent to 5 consumers, and a variety of message sizes. It checks that the sender and receivers reports both - * indicate that all the test messages were transmitted successfully. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Setup pub/sub test parameters and compare with test output. {@link FrameworkBaseCase} - *
- */ -public class InteropTestCase5PubSubMessageSize extends FrameworkBaseCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(InteropTestCase5PubSubMessageSize.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public InteropTestCase5PubSubMessageSize(String name) - { - super(name); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 0K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize0K() throws Exception - { - runTestForMessagesOfSize(0); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 63K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize63K() throws Exception - { - runTestForMessagesOfSize(63 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 64K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize64K() throws Exception - { - runTestForMessagesOfSize(64 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 65K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize65K() throws Exception - { - runTestForMessagesOfSize(65 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 127K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize127K() throws Exception - { - runTestForMessagesOfSize(127 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 128K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize128K() throws Exception - { - runTestForMessagesOfSize(128 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 129K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize129K() throws Exception - { - runTestForMessagesOfSize(129 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 255K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize255K() throws Exception - { - runTestForMessagesOfSize(255 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 256K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize256K() throws Exception - { - runTestForMessagesOfSize(256 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 257K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize257K() throws Exception - { - runTestForMessagesOfSize(257 * 1024); - } - - /** - * Sends 50 test messages of the specified size, and asserts that all were received. - * - * @param size The size of the messages to send in bytes. - */ - private void runTestForMessagesOfSize(int size) - { - Properties testConfig = new Properties(); - testConfig.put("TEST_NAME", "TC5_PubSubMessageSize"); - testConfig.put("PUBSUB_KEY", "tc3route"); - testConfig.put("PUBSUB_NUM_MESSAGES", 10); - testConfig.put("PUBSUB_NUM_RECEIVERS", 5); - testConfig.put("messageSize", size); - - /*Message[] reports =*/ - getCircuitFactory().sequenceTest(null, null, testConfig); - - // Compare sender and receivers reports. - /* - int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); - int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); - - Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); - Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived); - */ - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC5_PubSubMessageSize"; - } -} +/* + * + * 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.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Implements test case 5, from the interop test specification. This test sets up the TC2_PubSubMessageSize test for 10 + * messages, sent to 5 consumers, and a variety of message sizes. It checks that the sender and receivers reports both + * indicate that all the test messages were transmitted successfully. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Setup pub/sub test parameters and compare with test output. {@link FrameworkBaseCase} + *
+ */ +public class InteropTestCase5PubSubMessageSize extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase5PubSubMessageSize.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase5PubSubMessageSize(String name) + { + super(name); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 0K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize0K() throws Exception + { + runTestForMessagesOfSize(0); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 63K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize63K() throws Exception + { + runTestForMessagesOfSize(63 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 64K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize64K() throws Exception + { + runTestForMessagesOfSize(64 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 65K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize65K() throws Exception + { + runTestForMessagesOfSize(65 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 127K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize127K() throws Exception + { + runTestForMessagesOfSize(127 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 128K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize128K() throws Exception + { + runTestForMessagesOfSize(128 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 129K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize129K() throws Exception + { + runTestForMessagesOfSize(129 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 255K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize255K() throws Exception + { + runTestForMessagesOfSize(255 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 256K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize256K() throws Exception + { + runTestForMessagesOfSize(256 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 257K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize257K() throws Exception + { + runTestForMessagesOfSize(257 * 1024); + } + + /** + * Sends 50 test messages of the specified size, and asserts that all were received. + * + * @param size The size of the messages to send in bytes. + */ + private void runTestForMessagesOfSize(int size) + { + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "TC5_PubSubMessageSize"); + testConfig.put("PUBSUB_KEY", "tc3route"); + testConfig.put("PUBSUB_NUM_MESSAGES", 10); + testConfig.put("PUBSUB_NUM_RECEIVERS", 5); + testConfig.put("messageSize", size); + + /*Message[] reports =*/ + getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /* + int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); + Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived); + */ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC5_PubSubMessageSize"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java index 5ed502287d..63e2c75509 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java @@ -1,906 +1,906 @@ -/* - * - * 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.sustained; - -import org.apache.log4j.Logger; - -import org.apache.qpid.client.AMQNoConsumersException; -import org.apache.qpid.client.AMQNoRouteException; -import org.apache.qpid.test.framework.distributedtesting.TestClient; -import org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub; -import org.apache.qpid.test.framework.TestUtils; - -import javax.jms.Connection; -import javax.jms.Destination; -import javax.jms.ExceptionListener; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageConsumer; -import javax.jms.MessageListener; -import javax.jms.MessageProducer; -import javax.jms.Session; -import javax.jms.TextMessage; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.CountDownLatch; - -/** - * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the - * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of - * messages sent/received. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Supply the name of the test case that this implements. - *
Accept/Reject invites based on test parameters. - *
Adapt to assigned roles. - *
Send required number of test messages using pub/sub.
Generate test reports. - *
- */ -public class SustainedClientTestCase extends TestCase3BasicPubSub implements ExceptionListener, MessageListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(SustainedClientTestCase.class); - - /** Used to log to the console. */ - private static final Logger console = Logger.getLogger("SustainedTest"); - - /** The role to be played by the test. */ - private Roles role; - - /** The number of receivers connection to use. */ - private int numReceivers; - - /** The routing key to send them to on the default direct exchange. */ - private Destination sendDestination; - - /** The routing key to send updates to on the default direct exchange. */ - private Destination sendUpdateDestination; - - /** The connections to send/receive the test messages on. */ - private Connection[] connection; - - /** The sessions to send/receive the test messages on. */ - private Session[] session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** Adapter that adjusts the send rate based on the updates from clients. */ - SustainedRateAdapter _rateAdapter; - - /** */ - int _batchSize; - - private static final long TEN_MILLI_SEC = 10000000; - private static final int DEBUG_LOG_UPATE_INTERVAL = 10; - private static final int LOG_UPATE_INTERVAL = 10; - private static final boolean SLEEP_PER_MESSAGE = Boolean.getBoolean("sleepPerMessage"); - - /** - * 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() - { - log.debug("public String getName(): called"); - - return "Perf_SustainedPubSub"; - } - - /** - * 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 receivers. - * @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 - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Take note of the role to be played. - this.role = role; - - // Extract and retain the test parameters. - numReceivers = assignRoleMessage.getIntProperty("SUSTAINED_NUM_RECEIVERS"); - _batchSize = assignRoleMessage.getIntProperty("SUSTAINED_UPDATE_INTERVAL"); - String sendKey = assignRoleMessage.getStringProperty("SUSTAINED_KEY"); - String sendUpdateKey = assignRoleMessage.getStringProperty("SUSTAINED_UPDATE_KEY"); - int ackMode = assignRoleMessage.getIntProperty("ACKNOWLEDGE_MODE"); - String clientName = assignRoleMessage.getStringProperty("CLIENT_NAME"); - - if (log.isDebugEnabled()) - { - log.debug("numReceivers = " + numReceivers); - log.debug("_batchSize = " + _batchSize); - log.debug("ackMode = " + ackMode); - log.debug("sendKey = " + sendKey); - log.debug("sendUpdateKey = " + sendUpdateKey); - log.debug("role = " + role); - } - - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - console.info("Creating Sender"); - // Create a new connection to pass the test messages on. - connection = new Connection[1]; - session = new Session[1]; - - connection[0] = TestUtils.createConnection(TestClient.testContextProperties); - session[0] = connection[0].createSession(false, ackMode); - - // Extract and retain the test parameters. - sendDestination = session[0].createTopic(sendKey); - - connection[0].setExceptionListener(this); - - producer = session[0].createProducer(sendDestination); - - sendUpdateDestination = session[0].createTopic(sendUpdateKey); - MessageConsumer updateConsumer = session[0].createConsumer(sendUpdateDestination); - - _rateAdapter = new SustainedRateAdapter(this); - updateConsumer.setMessageListener(_rateAdapter); - - break; - - // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number - // of receivers connections. - case RECEIVER: - console.info("Creating Receiver"); - // Create the required number of receivers connections. - connection = new Connection[numReceivers]; - session = new Session[numReceivers]; - - for (int i = 0; i < numReceivers; i++) - { - connection[i] = TestUtils.createConnection(TestClient.testContextProperties); - session[i] = connection[i].createSession(false, ackMode); - - sendDestination = session[i].createTopic(sendKey); - - sendUpdateDestination = session[i].createTopic(sendUpdateKey); - - MessageConsumer consumer = session[i].createConsumer(sendDestination); - - consumer.setMessageListener(new SustainedListener(clientName + "-" + i, _batchSize, session[i], - sendUpdateDestination)); - } - - break; - } - - // Start all the connection dispatcher threads running. - for (int i = 0; i < connection.length; i++) - { - connection[i].start(); - } - } - - /** Performs the test case actions. - * @param numMessages*/ - public void start(int numMessages) throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - _rateAdapter.run(); - break; - case RECEIVER: - - } - - // return from here when you have finished the test.. this will signal the controller and - } - - public void terminate() throws JMSException, InterruptedException - { - if (_rateAdapter != null) - { - _rateAdapter.stop(); - } - } - - /** - * Gets a report on the actions performed by the test case in its assigned role. - * - * @param session The controlSession 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 - { - log.debug("public Message getReport(Session controlSession): called"); - - // Close the test connections. - for (int i = 0; i < connection.length; i++) - { - connection[i].close(); - } - - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - - return report; - } - - public void onException(JMSException jmsException) - { - Exception linked = jmsException.getLinkedException(); - - if (linked != null) - { - if (log.isDebugEnabled()) - { - log.debug("Linked Exception:" + linked); - } - - if ((linked instanceof AMQNoRouteException) || (linked instanceof AMQNoConsumersException)) - { - if (log.isDebugEnabled()) - { - if (linked instanceof AMQNoConsumersException) - { - log.warn("No clients currently available for message:" - + ((AMQNoConsumersException) linked).getUndeliveredMessage()); - } - else - { - log.warn("No route for message"); - } - } - - // Tell the rate adapter that there are no clients ready yet - _rateAdapter.NO_CLIENTS = true; - } - } - else - { - log.warn("Exception:" + linked); - } - } - - /** - * Inner class that listens for messages and sends a report for the time taken between receiving the 'start' and - * 'end' messages. - */ - class SustainedListener implements MessageListener - { - /** Number of messages received */ - private long _received = 0; - /** The number of messages in the batch */ - private int _batchSize = 0; - /** Record of the when the 'start' messagse was sen */ - private Long _startTime; - /** Message producer to use to send reports */ - MessageProducer _updater; - /** Session to create the report message on */ - Session _session; - /** Record of the client ID used for this SustainedListnener */ - String _client; - - /** - * Main Constructor - * - * @param clientname The _client id used to identify this connection. - * @param batchSize The number of messages that are to be sent per batch. Note: This is not used to - * control the interval between sending reports. - * @param session The controlSession used for communication. - * @param sendDestination The destination that update reports should be sent to. - * - * @throws JMSException My occur if creatingthe Producer fails - */ - public SustainedListener(String clientname, int batchSize, Session session, Destination sendDestination) - throws JMSException - { - _batchSize = batchSize; - _client = clientname; - _session = session; - _updater = session.createProducer(sendDestination); - } - - public void onMessage(Message message) - { - if (log.isDebugEnabled()) - { - log.debug("Message " + _received + "received in listener"); - } - - if (message instanceof TextMessage) - { - try - { - _received++; - if (((TextMessage) message).getText().equals("start")) - { - log.debug("Starting Batch"); - _startTime = System.nanoTime(); - } - else if (((TextMessage) message).getText().equals("end")) - { - if (_startTime != null) - { - long currentTime = System.nanoTime(); - sendStatus(currentTime - _startTime, _received, message.getIntProperty("BATCH")); - log.debug("End Batch"); - } - } - } - catch (JMSException e) - { - // ignore error - } - } - - } - - /** - * sendStatus creates and sends the report back to the publisher - * - * @param time taken for the the last batch - * @param received Total number of messages received. - * @param batchNumber the batch number - * @throws JMSException if an error occurs during the send - */ - private void sendStatus(long time, long received, int batchNumber) throws JMSException - { - Message updateMessage = _session.createTextMessage("update"); - updateMessage.setStringProperty("CLIENT_ID", ":" + _client); - updateMessage.setStringProperty("CONTROL_TYPE", "UPDATE"); - updateMessage.setLongProperty("RECEIVED", received); - updateMessage.setIntProperty("BATCH", batchNumber); - updateMessage.setLongProperty("DURATION", time); - - if (log.isInfoEnabled()) - { - log.info("**** SENDING [" + batchNumber + "]**** " + "CLIENT_ID:" + _client + " RECEIVED:" + received - + " BATCH:" + batchNumber + " DURATION:" + time); - } - - // Output on the main console.info the details of this batch - if ((batchNumber % 10) == 0) - { - console.info("Sending Report [" + batchNumber + "] " + "CLIENT_ID:" + _client + " RECEIVED:" + received - + " BATCH:" + batchNumber + " DURATION:" + time); - } - - _updater.send(updateMessage); - } - } - - /** - * This class is used here to adjust the _delay value which in turn is used to control the number of messages/second - * that are sent through the test system. - * - * By keeping a record of the messages recevied and the average time taken to process the batch size can be - * calculated and so the delay can be adjusted to maintain that rate. - * - * Given that delays of < 10ms can be rounded up the delay is only used between messages if the _delay > 10ms * no - * messages in the batch. Otherwise the delay is used at the end of the batch. - */ - class SustainedRateAdapter implements MessageListener, Runnable - { - private SustainedClientTestCase _client; - private long _batchVariance = Integer.getInteger("batchVariance", 3); // no. batches to allow drifting - private long _timeVariance = TEN_MILLI_SEC * 5; // no. nanos between send and report delay (10ms) - private volatile long _delay; // in nanos - private long _sent; - private Map _slowClients = new HashMap(); - private static final long PAUSE_SLEEP = TEN_MILLI_SEC / 1000; // 10 ms - private static final long NO_CLIENT_SLEEP = 1000; // 1s - private volatile boolean NO_CLIENTS = true; - private int _delayShifting; - private final int REPORTS_WITHOUT_CHANGE = Integer.getInteger("stableReportCount", 5); - private boolean _warmedup = false; - private static final long EXPECTED_TIME_PER_BATCH = 100000L; - private int _warmUpBatches = Integer.getInteger("warmUpBatches", 10); - - SustainedRateAdapter(SustainedClientTestCase client) - { - _client = client; - } - - public void onMessage(Message message) - { - if (log.isDebugEnabled()) - { - log.debug("SustainedRateAdapter onMessage(Message message = " + message + "): called"); - } - - try - { - String controlType = message.getStringProperty("CONTROL_TYPE"); - - // Check if the message is a test invite. - if ("UPDATE".equals(controlType)) - { - NO_CLIENTS = false; - long duration = message.getLongProperty("DURATION"); - long totalReceived = message.getLongProperty("RECEIVED"); - String client = message.getStringProperty("CLIENT_ID"); - int batchNumber = message.getIntProperty("BATCH"); - - if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) - { - log.info("Update Report: CLIENT_ID:" + client + " RECEIVED:" + totalReceived + " Recevied BATCH:" - + batchNumber + " DURATION:" + duration); - } - - recordSlow(client, totalReceived, batchNumber); - - adjustDelay(client, batchNumber, duration); - - // Warm up completes when: - // we haven't warmed up - // and the number of batches sent to each client is at least half of the required warmup batches - if (!_warmedup && (batchNumber >= _warmUpBatches)) - { - _warmedup = true; - _warmup.countDown(); - - } - } - } - catch (JMSException e) - { - // - } - } - - CountDownLatch _warmup = new CountDownLatch(1); - - int _numBatches = 10000; - - // long[] _timings = new long[_numBatches]; - private boolean _running = true; - - public void run() - { - console.info("Warming up"); - - doBatch(_warmUpBatches); - - try - { - // wait for warmup to complete. - _warmup.await(); - - // set delay to the average length of the batches - _delay = _totalDuration / _warmUpBatches / delays.size(); - - console.info("Warmup complete delay set : " + _delay + " based on _totalDuration: " + _totalDuration - + " over no. batches: " + _warmUpBatches + " with client count: " + delays.size()); - - _totalDuration = 0L; - _totalReceived = 0L; - _sent = 0L; - } - catch (InterruptedException e) - { - // - } - - doBatch(_numBatches); - - } - - private void doBatch(int batchSize) // long[] timings, - { - TextMessage testMessage = null; - try - { - testMessage = _client.session[0].createTextMessage("start"); - - for (int batch = 0; batch <= batchSize; batch++) - // while (_running) - { - long start = System.nanoTime(); - - testMessage.setText("start"); - testMessage.setIntProperty("BATCH", batch); - - _client.producer.send(testMessage); - _rateAdapter.sentMessage(); - - testMessage.setText("test"); - // start at 2 so start and end count as part of batch - for (int m = 2; m < _batchSize; m++) - { - _client.producer.send(testMessage); - _rateAdapter.sentMessage(); - } - - testMessage.setText("end"); - _client.producer.send(testMessage); - _rateAdapter.sentMessage(); - - long end = System.nanoTime(); - - long sendtime = end - start; - - if (log.isDebugEnabled()) - { - log.info("Sent batch[" + batch + "](" + _batchSize + ") in " + sendtime); // timings[batch]); - } - - if ((batch % LOG_UPATE_INTERVAL) == 0) - { - console.info("Sent Batch[" + batch + "](" + _batchSize + ")" + status()); - } - - _rateAdapter.sleepBatch(); - - } - } - catch (JMSException e) - { - console.error("Runner ended"); - } - } - - private String status() - { - return " TotalDuration: " + _totalDuration + " for " + delays.size() + " consumers" + " Delay is " + _delay - + " resulting in " - + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch")); - } - - private void sleepBatch() - { - if (checkForSlowClients()) - { // if there werwe slow clients we have already slept so don't sleep anymore again. - return; - } - - if (!SLEEP_PER_MESSAGE) - { - // per batch sleep.. if sleep is to small to spread over the batch. - if (_delay <= (TEN_MILLI_SEC * _batchSize)) - { - sleepLong(_delay); - } - else - { - log.info("Not sleeping _delay > ten*batch is:" + _delay); - } - } - } - - public void stop() - { - _running = false; - } - - Map delays = new HashMap(); - Long _totalReceived = 0L; - Long _totalDuration = 0L; - int _skipUpdate = 0; - - /** - * Adjust the delay for sending messages based on this update from the client - * - * @param client The client that send this update - * @param duration The time taken for the last batch of messagse - * @param batchNumber The reported batchnumber from the client - */ - private void adjustDelay(String client, int batchNumber, long duration) - { - // Retrieve the current total time taken for this client. - Long currentTime = delays.get(client); - - // Add the new duration time to this client - if (currentTime == null) - { - currentTime = duration; - } - else - { - currentTime += duration; - } - - delays.put(client, currentTime); - - long batchesSent = _sent / _batchSize; - - // ensure we don't divide by zero - if (batchesSent == 0) - { - batchesSent = 1L; - } - - _totalReceived += _batchSize; - _totalDuration += duration; - - // calculate average duration accross clients per batch - long averageDuration = _totalDuration / delays.size() / batchesSent; - - // calculate the difference between current send delay and average report delay - long diff = (duration) - averageDuration; - - if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) - { - log.info("TotalDuration:" + _totalDuration + " for " + delays.size() + " consumers." + " on batch: " - + batchesSent + " received batch: " + batchNumber + " Batch Duration: " + duration + " Average: " - + averageDuration + " so diff: " + diff + " for : " + client + " Delay is " + _delay + " resulting in " - + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch"))); - } - - // if the averageDuration differs from the current by more than the specified variane then adjust delay. - if (Math.abs(diff) > _timeVariance) - { - - // if the the _delay is larger than the required duration to send report - // speed up - if (diff > TEN_MILLI_SEC) - { - _delay -= TEN_MILLI_SEC; - - if (_delay < 0) - { - _delay = 0; - log.info("Reset _delay to 0"); - delayStable(); - } - else - { - delayChanged(); - } - - } - else if (diff < 0) // diff < 0 diff cannot be 0 as it is > _timeVariance - { - // the report took longer - _delay += TEN_MILLI_SEC; - delayChanged(); - } - } - else - { - delayStable(); - } - - // If we have a consumer that is behind with the batches. - if ((batchesSent - batchNumber) > _batchVariance) - { - log.debug("Increasing _delay as sending more than receiving"); - - _delay += 2 * TEN_MILLI_SEC; - delayChanged(); - } - - } - - /** Reset the number of iterations before we say the delay has stabilised. */ - private void delayChanged() - { - _delayShifting = REPORTS_WITHOUT_CHANGE; - } - - /** - * Record the fact that delay has stabilised If delay has stablised for REPORTS_WITHOUT_CHANGE then it will - * output Delay stabilised - */ - private void delayStable() - { - _delayShifting--; - - if (_delayShifting < 0) - { - _delayShifting = 0; - console.debug("Delay stabilised:" + _delay); - } - } - - /** - * Checks that the client has received enough messages. If the client has fallen behind then they are put in the - * _slowClients lists which will increase the delay. - * - * @param client The client identifier to check - * @param received the number of messages received by that client - * @param batchNumber - */ - private void recordSlow(String client, long received, int batchNumber) - { - if (Math.abs(batchNumber - (_sent / _batchSize)) > _batchVariance) - { - _slowClients.put(client, received); - } - else - { - _slowClients.remove(client); - } - } - - /** Incrment the number of sent messages and then sleep, if required. */ - public void sentMessage() - { - - _sent++; - - if (_delay > (TEN_MILLI_SEC * _batchSize)) - { - long batchDelay = _delay / _batchSize; - // less than 10ms sleep doesn't always work. - // _delay is in nano seconds - // if (batchDelay < (TEN_MILLI_SEC)) - // { - // sleep(0, (int) batchDelay); - // } - // else - { - // if (batchDelay < 30000000000L) - { - sleepLong(batchDelay); - } - } - } - else - { - if (SLEEP_PER_MESSAGE && (_delay > 0)) - { - sleepLong(_delay / _batchSize); - } - } - } - - /** - * Check at the end of each batch and pause sending messages to allow slow clients to catch up. - * - * @return true if there were slow clients that caught up. - */ - private boolean checkForSlowClients() - { - // This will allways be true as we are running this at the end of each batchSize - // if (_sent % _batchSize == 0) - { - // Cause test to pause when we have slow - if (!_slowClients.isEmpty() || NO_CLIENTS) - { - - while (!_slowClients.isEmpty()) - { - if (log.isInfoEnabled() && ((_sent / _batchSize % DEBUG_LOG_UPATE_INTERVAL) == 0)) - { - String clients = ""; - Iterator it = _slowClients.keySet().iterator(); - while (it.hasNext()) - { - clients += it.next(); - if (it.hasNext()) - { - clients += ", "; - } - } - - log.info("Pausing for slow clients:" + clients); - } - - if (console.isDebugEnabled() && ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0)) - { - console.debug(_slowClients.size() + " slow clients."); - } - - sleep(PAUSE_SLEEP); - } - - if (NO_CLIENTS) - { - sleep(NO_CLIENT_SLEEP); - } - - log.debug("Continuing"); - - return true; - } - else - { - if ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0) - { - console.info("Total Delay :" + _delay + " " - + ((_delayShifting == 0) ? "Stablised" : ("Not Stablised(" + _delayShifting + ")"))); - } - } - - } - - return false; - } - - /** - * Sleep normally takes micro-seconds this allows the use of a nano-second value. - * - * @param delay nanoseconds to sleep for. - */ - private void sleepLong(long delay) - { - sleep(delay / 1000000, (int) (delay % 1000000)); - } - - /** - * Sleep for the specified micro-seconds. - * @param sleep microseconds to sleep for. - */ - private void sleep(long sleep) - { - sleep(sleep, 0); - } - - /** - * Perform the sleep , swallowing any InteruptException. - * - * NOTE: If a sleep request is > 10s then reset only sleep for 5s - * - * @param milli to sleep for - * @param nano sub miliseconds to sleep for - */ - private void sleep(long milli, int nano) - { - try - { - log.debug("Sleep:" + milli + ":" + nano); - if (milli > 10000) - { - - if (_delay == milli) - { - _totalDuration = _totalReceived / _batchSize * EXPECTED_TIME_PER_BATCH; - log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) - + "s. Reset _totalDuration:" + _totalDuration); - } - else - { - log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + "s"); - } - - milli = 5000; - } - - Thread.sleep(milli, nano); - } - catch (InterruptedException e) - { - // - } - } - - public void setClient(SustainedClientTestCase client) - { - _client = client; - } - } - -} +/* + * + * 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.sustained; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQNoConsumersException; +import org.apache.qpid.client.AMQNoRouteException; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub; +import org.apache.qpid.test.framework.TestUtils; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +/** + * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the + * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of + * messages sent/received. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. + *
Adapt to assigned roles. + *
Send required number of test messages using pub/sub.
Generate test reports. + *
+ */ +public class SustainedClientTestCase extends TestCase3BasicPubSub implements ExceptionListener, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(SustainedClientTestCase.class); + + /** Used to log to the console. */ + private static final Logger console = Logger.getLogger("SustainedTest"); + + /** The role to be played by the test. */ + private Roles role; + + /** The number of receivers connection to use. */ + private int numReceivers; + + /** The routing key to send them to on the default direct exchange. */ + private Destination sendDestination; + + /** The routing key to send updates to on the default direct exchange. */ + private Destination sendUpdateDestination; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** Adapter that adjusts the send rate based on the updates from clients. */ + SustainedRateAdapter _rateAdapter; + + /** */ + int _batchSize; + + private static final long TEN_MILLI_SEC = 10000000; + private static final int DEBUG_LOG_UPATE_INTERVAL = 10; + private static final int LOG_UPATE_INTERVAL = 10; + private static final boolean SLEEP_PER_MESSAGE = Boolean.getBoolean("sleepPerMessage"); + + /** + * 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() + { + log.debug("public String getName(): called"); + + return "Perf_SustainedPubSub"; + } + + /** + * 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 receivers. + * @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 + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numReceivers = assignRoleMessage.getIntProperty("SUSTAINED_NUM_RECEIVERS"); + _batchSize = assignRoleMessage.getIntProperty("SUSTAINED_UPDATE_INTERVAL"); + String sendKey = assignRoleMessage.getStringProperty("SUSTAINED_KEY"); + String sendUpdateKey = assignRoleMessage.getStringProperty("SUSTAINED_UPDATE_KEY"); + int ackMode = assignRoleMessage.getIntProperty("ACKNOWLEDGE_MODE"); + String clientName = assignRoleMessage.getStringProperty("CLIENT_NAME"); + + if (log.isDebugEnabled()) + { + log.debug("numReceivers = " + numReceivers); + log.debug("_batchSize = " + _batchSize); + log.debug("ackMode = " + ackMode); + log.debug("sendKey = " + sendKey); + log.debug("sendUpdateKey = " + sendUpdateKey); + log.debug("role = " + role); + } + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + console.info("Creating Sender"); + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = TestUtils.createConnection(TestClient.testContextProperties); + session[0] = connection[0].createSession(false, ackMode); + + // Extract and retain the test parameters. + sendDestination = session[0].createTopic(sendKey); + + connection[0].setExceptionListener(this); + + producer = session[0].createProducer(sendDestination); + + sendUpdateDestination = session[0].createTopic(sendUpdateKey); + MessageConsumer updateConsumer = session[0].createConsumer(sendUpdateDestination); + + _rateAdapter = new SustainedRateAdapter(this); + updateConsumer.setMessageListener(_rateAdapter); + + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number + // of receivers connections. + case RECEIVER: + console.info("Creating Receiver"); + // Create the required number of receivers connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = TestUtils.createConnection(TestClient.testContextProperties); + session[i] = connection[i].createSession(false, ackMode); + + sendDestination = session[i].createTopic(sendKey); + + sendUpdateDestination = session[i].createTopic(sendUpdateKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + + consumer.setMessageListener(new SustainedListener(clientName + "-" + i, _batchSize, session[i], + sendUpdateDestination)); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (int i = 0; i < connection.length; i++) + { + connection[i].start(); + } + } + + /** Performs the test case actions. + * @param numMessages*/ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + _rateAdapter.run(); + break; + case RECEIVER: + + } + + // return from here when you have finished the test.. this will signal the controller and + } + + public void terminate() throws JMSException, InterruptedException + { + if (_rateAdapter != null) + { + _rateAdapter.stop(); + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession 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 + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connections. + for (int i = 0; i < connection.length; i++) + { + connection[i].close(); + } + + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + + return report; + } + + public void onException(JMSException jmsException) + { + Exception linked = jmsException.getLinkedException(); + + if (linked != null) + { + if (log.isDebugEnabled()) + { + log.debug("Linked Exception:" + linked); + } + + if ((linked instanceof AMQNoRouteException) || (linked instanceof AMQNoConsumersException)) + { + if (log.isDebugEnabled()) + { + if (linked instanceof AMQNoConsumersException) + { + log.warn("No clients currently available for message:" + + ((AMQNoConsumersException) linked).getUndeliveredMessage()); + } + else + { + log.warn("No route for message"); + } + } + + // Tell the rate adapter that there are no clients ready yet + _rateAdapter.NO_CLIENTS = true; + } + } + else + { + log.warn("Exception:" + linked); + } + } + + /** + * Inner class that listens for messages and sends a report for the time taken between receiving the 'start' and + * 'end' messages. + */ + class SustainedListener implements MessageListener + { + /** Number of messages received */ + private long _received = 0; + /** The number of messages in the batch */ + private int _batchSize = 0; + /** Record of the when the 'start' messagse was sen */ + private Long _startTime; + /** Message producer to use to send reports */ + MessageProducer _updater; + /** Session to create the report message on */ + Session _session; + /** Record of the client ID used for this SustainedListnener */ + String _client; + + /** + * Main Constructor + * + * @param clientname The _client id used to identify this connection. + * @param batchSize The number of messages that are to be sent per batch. Note: This is not used to + * control the interval between sending reports. + * @param session The controlSession used for communication. + * @param sendDestination The destination that update reports should be sent to. + * + * @throws JMSException My occur if creatingthe Producer fails + */ + public SustainedListener(String clientname, int batchSize, Session session, Destination sendDestination) + throws JMSException + { + _batchSize = batchSize; + _client = clientname; + _session = session; + _updater = session.createProducer(sendDestination); + } + + public void onMessage(Message message) + { + if (log.isDebugEnabled()) + { + log.debug("Message " + _received + "received in listener"); + } + + if (message instanceof TextMessage) + { + try + { + _received++; + if (((TextMessage) message).getText().equals("start")) + { + log.debug("Starting Batch"); + _startTime = System.nanoTime(); + } + else if (((TextMessage) message).getText().equals("end")) + { + if (_startTime != null) + { + long currentTime = System.nanoTime(); + sendStatus(currentTime - _startTime, _received, message.getIntProperty("BATCH")); + log.debug("End Batch"); + } + } + } + catch (JMSException e) + { + // ignore error + } + } + + } + + /** + * sendStatus creates and sends the report back to the publisher + * + * @param time taken for the the last batch + * @param received Total number of messages received. + * @param batchNumber the batch number + * @throws JMSException if an error occurs during the send + */ + private void sendStatus(long time, long received, int batchNumber) throws JMSException + { + Message updateMessage = _session.createTextMessage("update"); + updateMessage.setStringProperty("CLIENT_ID", ":" + _client); + updateMessage.setStringProperty("CONTROL_TYPE", "UPDATE"); + updateMessage.setLongProperty("RECEIVED", received); + updateMessage.setIntProperty("BATCH", batchNumber); + updateMessage.setLongProperty("DURATION", time); + + if (log.isInfoEnabled()) + { + log.info("**** SENDING [" + batchNumber + "]**** " + "CLIENT_ID:" + _client + " RECEIVED:" + received + + " BATCH:" + batchNumber + " DURATION:" + time); + } + + // Output on the main console.info the details of this batch + if ((batchNumber % 10) == 0) + { + console.info("Sending Report [" + batchNumber + "] " + "CLIENT_ID:" + _client + " RECEIVED:" + received + + " BATCH:" + batchNumber + " DURATION:" + time); + } + + _updater.send(updateMessage); + } + } + + /** + * This class is used here to adjust the _delay value which in turn is used to control the number of messages/second + * that are sent through the test system. + * + * By keeping a record of the messages recevied and the average time taken to process the batch size can be + * calculated and so the delay can be adjusted to maintain that rate. + * + * Given that delays of < 10ms can be rounded up the delay is only used between messages if the _delay > 10ms * no + * messages in the batch. Otherwise the delay is used at the end of the batch. + */ + class SustainedRateAdapter implements MessageListener, Runnable + { + private SustainedClientTestCase _client; + private long _batchVariance = Integer.getInteger("batchVariance", 3); // no. batches to allow drifting + private long _timeVariance = TEN_MILLI_SEC * 5; // no. nanos between send and report delay (10ms) + private volatile long _delay; // in nanos + private long _sent; + private Map _slowClients = new HashMap(); + private static final long PAUSE_SLEEP = TEN_MILLI_SEC / 1000; // 10 ms + private static final long NO_CLIENT_SLEEP = 1000; // 1s + private volatile boolean NO_CLIENTS = true; + private int _delayShifting; + private final int REPORTS_WITHOUT_CHANGE = Integer.getInteger("stableReportCount", 5); + private boolean _warmedup = false; + private static final long EXPECTED_TIME_PER_BATCH = 100000L; + private int _warmUpBatches = Integer.getInteger("warmUpBatches", 10); + + SustainedRateAdapter(SustainedClientTestCase client) + { + _client = client; + } + + public void onMessage(Message message) + { + if (log.isDebugEnabled()) + { + log.debug("SustainedRateAdapter onMessage(Message message = " + message + "): called"); + } + + try + { + String controlType = message.getStringProperty("CONTROL_TYPE"); + + // Check if the message is a test invite. + if ("UPDATE".equals(controlType)) + { + NO_CLIENTS = false; + long duration = message.getLongProperty("DURATION"); + long totalReceived = message.getLongProperty("RECEIVED"); + String client = message.getStringProperty("CLIENT_ID"); + int batchNumber = message.getIntProperty("BATCH"); + + if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + log.info("Update Report: CLIENT_ID:" + client + " RECEIVED:" + totalReceived + " Recevied BATCH:" + + batchNumber + " DURATION:" + duration); + } + + recordSlow(client, totalReceived, batchNumber); + + adjustDelay(client, batchNumber, duration); + + // Warm up completes when: + // we haven't warmed up + // and the number of batches sent to each client is at least half of the required warmup batches + if (!_warmedup && (batchNumber >= _warmUpBatches)) + { + _warmedup = true; + _warmup.countDown(); + + } + } + } + catch (JMSException e) + { + // + } + } + + CountDownLatch _warmup = new CountDownLatch(1); + + int _numBatches = 10000; + + // long[] _timings = new long[_numBatches]; + private boolean _running = true; + + public void run() + { + console.info("Warming up"); + + doBatch(_warmUpBatches); + + try + { + // wait for warmup to complete. + _warmup.await(); + + // set delay to the average length of the batches + _delay = _totalDuration / _warmUpBatches / delays.size(); + + console.info("Warmup complete delay set : " + _delay + " based on _totalDuration: " + _totalDuration + + " over no. batches: " + _warmUpBatches + " with client count: " + delays.size()); + + _totalDuration = 0L; + _totalReceived = 0L; + _sent = 0L; + } + catch (InterruptedException e) + { + // + } + + doBatch(_numBatches); + + } + + private void doBatch(int batchSize) // long[] timings, + { + TextMessage testMessage = null; + try + { + testMessage = _client.session[0].createTextMessage("start"); + + for (int batch = 0; batch <= batchSize; batch++) + // while (_running) + { + long start = System.nanoTime(); + + testMessage.setText("start"); + testMessage.setIntProperty("BATCH", batch); + + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + + testMessage.setText("test"); + // start at 2 so start and end count as part of batch + for (int m = 2; m < _batchSize; m++) + { + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + } + + testMessage.setText("end"); + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + + long end = System.nanoTime(); + + long sendtime = end - start; + + if (log.isDebugEnabled()) + { + log.info("Sent batch[" + batch + "](" + _batchSize + ") in " + sendtime); // timings[batch]); + } + + if ((batch % LOG_UPATE_INTERVAL) == 0) + { + console.info("Sent Batch[" + batch + "](" + _batchSize + ")" + status()); + } + + _rateAdapter.sleepBatch(); + + } + } + catch (JMSException e) + { + console.error("Runner ended"); + } + } + + private String status() + { + return " TotalDuration: " + _totalDuration + " for " + delays.size() + " consumers" + " Delay is " + _delay + + " resulting in " + + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch")); + } + + private void sleepBatch() + { + if (checkForSlowClients()) + { // if there werwe slow clients we have already slept so don't sleep anymore again. + return; + } + + if (!SLEEP_PER_MESSAGE) + { + // per batch sleep.. if sleep is to small to spread over the batch. + if (_delay <= (TEN_MILLI_SEC * _batchSize)) + { + sleepLong(_delay); + } + else + { + log.info("Not sleeping _delay > ten*batch is:" + _delay); + } + } + } + + public void stop() + { + _running = false; + } + + Map delays = new HashMap(); + Long _totalReceived = 0L; + Long _totalDuration = 0L; + int _skipUpdate = 0; + + /** + * Adjust the delay for sending messages based on this update from the client + * + * @param client The client that send this update + * @param duration The time taken for the last batch of messagse + * @param batchNumber The reported batchnumber from the client + */ + private void adjustDelay(String client, int batchNumber, long duration) + { + // Retrieve the current total time taken for this client. + Long currentTime = delays.get(client); + + // Add the new duration time to this client + if (currentTime == null) + { + currentTime = duration; + } + else + { + currentTime += duration; + } + + delays.put(client, currentTime); + + long batchesSent = _sent / _batchSize; + + // ensure we don't divide by zero + if (batchesSent == 0) + { + batchesSent = 1L; + } + + _totalReceived += _batchSize; + _totalDuration += duration; + + // calculate average duration accross clients per batch + long averageDuration = _totalDuration / delays.size() / batchesSent; + + // calculate the difference between current send delay and average report delay + long diff = (duration) - averageDuration; + + if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + log.info("TotalDuration:" + _totalDuration + " for " + delays.size() + " consumers." + " on batch: " + + batchesSent + " received batch: " + batchNumber + " Batch Duration: " + duration + " Average: " + + averageDuration + " so diff: " + diff + " for : " + client + " Delay is " + _delay + " resulting in " + + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch"))); + } + + // if the averageDuration differs from the current by more than the specified variane then adjust delay. + if (Math.abs(diff) > _timeVariance) + { + + // if the the _delay is larger than the required duration to send report + // speed up + if (diff > TEN_MILLI_SEC) + { + _delay -= TEN_MILLI_SEC; + + if (_delay < 0) + { + _delay = 0; + log.info("Reset _delay to 0"); + delayStable(); + } + else + { + delayChanged(); + } + + } + else if (diff < 0) // diff < 0 diff cannot be 0 as it is > _timeVariance + { + // the report took longer + _delay += TEN_MILLI_SEC; + delayChanged(); + } + } + else + { + delayStable(); + } + + // If we have a consumer that is behind with the batches. + if ((batchesSent - batchNumber) > _batchVariance) + { + log.debug("Increasing _delay as sending more than receiving"); + + _delay += 2 * TEN_MILLI_SEC; + delayChanged(); + } + + } + + /** Reset the number of iterations before we say the delay has stabilised. */ + private void delayChanged() + { + _delayShifting = REPORTS_WITHOUT_CHANGE; + } + + /** + * Record the fact that delay has stabilised If delay has stablised for REPORTS_WITHOUT_CHANGE then it will + * output Delay stabilised + */ + private void delayStable() + { + _delayShifting--; + + if (_delayShifting < 0) + { + _delayShifting = 0; + console.debug("Delay stabilised:" + _delay); + } + } + + /** + * Checks that the client has received enough messages. If the client has fallen behind then they are put in the + * _slowClients lists which will increase the delay. + * + * @param client The client identifier to check + * @param received the number of messages received by that client + * @param batchNumber + */ + private void recordSlow(String client, long received, int batchNumber) + { + if (Math.abs(batchNumber - (_sent / _batchSize)) > _batchVariance) + { + _slowClients.put(client, received); + } + else + { + _slowClients.remove(client); + } + } + + /** Incrment the number of sent messages and then sleep, if required. */ + public void sentMessage() + { + + _sent++; + + if (_delay > (TEN_MILLI_SEC * _batchSize)) + { + long batchDelay = _delay / _batchSize; + // less than 10ms sleep doesn't always work. + // _delay is in nano seconds + // if (batchDelay < (TEN_MILLI_SEC)) + // { + // sleep(0, (int) batchDelay); + // } + // else + { + // if (batchDelay < 30000000000L) + { + sleepLong(batchDelay); + } + } + } + else + { + if (SLEEP_PER_MESSAGE && (_delay > 0)) + { + sleepLong(_delay / _batchSize); + } + } + } + + /** + * Check at the end of each batch and pause sending messages to allow slow clients to catch up. + * + * @return true if there were slow clients that caught up. + */ + private boolean checkForSlowClients() + { + // This will allways be true as we are running this at the end of each batchSize + // if (_sent % _batchSize == 0) + { + // Cause test to pause when we have slow + if (!_slowClients.isEmpty() || NO_CLIENTS) + { + + while (!_slowClients.isEmpty()) + { + if (log.isInfoEnabled() && ((_sent / _batchSize % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + String clients = ""; + Iterator it = _slowClients.keySet().iterator(); + while (it.hasNext()) + { + clients += it.next(); + if (it.hasNext()) + { + clients += ", "; + } + } + + log.info("Pausing for slow clients:" + clients); + } + + if (console.isDebugEnabled() && ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0)) + { + console.debug(_slowClients.size() + " slow clients."); + } + + sleep(PAUSE_SLEEP); + } + + if (NO_CLIENTS) + { + sleep(NO_CLIENT_SLEEP); + } + + log.debug("Continuing"); + + return true; + } + else + { + if ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0) + { + console.info("Total Delay :" + _delay + " " + + ((_delayShifting == 0) ? "Stablised" : ("Not Stablised(" + _delayShifting + ")"))); + } + } + + } + + return false; + } + + /** + * Sleep normally takes micro-seconds this allows the use of a nano-second value. + * + * @param delay nanoseconds to sleep for. + */ + private void sleepLong(long delay) + { + sleep(delay / 1000000, (int) (delay % 1000000)); + } + + /** + * Sleep for the specified micro-seconds. + * @param sleep microseconds to sleep for. + */ + private void sleep(long sleep) + { + sleep(sleep, 0); + } + + /** + * Perform the sleep , swallowing any InteruptException. + * + * NOTE: If a sleep request is > 10s then reset only sleep for 5s + * + * @param milli to sleep for + * @param nano sub miliseconds to sleep for + */ + private void sleep(long milli, int nano) + { + try + { + log.debug("Sleep:" + milli + ":" + nano); + if (milli > 10000) + { + + if (_delay == milli) + { + _totalDuration = _totalReceived / _batchSize * EXPECTED_TIME_PER_BATCH; + log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + + "s. Reset _totalDuration:" + _totalDuration); + } + else + { + log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + "s"); + } + + milli = 5000; + } + + Thread.sleep(milli, nano); + } + catch (InterruptedException e) + { + // + } + } + + public void setClient(SustainedClientTestCase client) + { + _client = client; + } + } + +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java index 5979a459ec..0077b4727a 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java @@ -1,126 +1,126 @@ -/* - * - * 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.sustained; - -import org.apache.log4j.Logger; - -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.test.framework.DropInTest; -import org.apache.qpid.test.framework.FrameworkBaseCase; - -import javax.jms.JMSException; -import javax.jms.Message; - -import java.util.Properties; - -/** - * SustainedTestCase is a {@link FrameworkBaseCase} that runs the "Perf_SustainedPubSub" test case. This consists of one - * test client sending, and several receiving, and attempts to find the highest rate at which messages can be broadcast - * to the receivers. It is also a {@link DropInTest} to which more test clients may be added during a test run. - * - *

- *
CRC Card
Responsibilities Collaborations - *
- *
- */ -public class SustainedTestCase extends FrameworkBaseCase implements DropInTest -{ - /** Used for debugging. */ - Logger log = Logger.getLogger(SustainedTestCase.class); - - /** Holds the root name of the topic on which to send the test messages. */ - private static final String SUSTAINED_KEY = "Perf_SustainedPubSub"; - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public SustainedTestCase(String name) - { - super(name); - } - - /** - * Performs a single test run of the sustained test. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testBasicPubSub() throws Exception - { - log.debug("public void testSinglePubSubCycle(): called"); - - Properties testConfig = new Properties(); - testConfig.put("TEST_NAME", "Perf_SustainedPubSub"); - testConfig.put("SUSTAINED_KEY", SUSTAINED_KEY); - testConfig.put("SUSTAINED_NUM_RECEIVERS", Integer.getInteger("numReceives", 2)); - testConfig.put("SUSTAINED_UPDATE_INTERVAL", Integer.getInteger("batchSize", 1000)); - testConfig.put("SUSTAINED_UPDATE_KEY", SUSTAINED_KEY + ".UPDATE"); - testConfig.put("ACKNOWLEDGE_MODE", Integer.getInteger("ackMode", AMQSession.AUTO_ACKNOWLEDGE)); - - log.info("Created Config: " + testConfig.entrySet().toArray()); - - getCircuitFactory().sequenceTest(null, null, testConfig); - } - - /** - * Accepts a late joining client into this test case. The client will be enlisted with a control message - * with the 'CONTROL_TYPE' field set to the value 'LATEJOIN'. It should also provide values for the fields: - * - *

- *
CLIENT_NAME A unique name for the new client. - *
CLIENT_PRIVATE_CONTROL_KEY The key for the route on which the client receives its control messages. - *
- * - * @param message The late joiners join message. - * - * @throws JMSException Any JMS Exception are allowed to fall through, indicating that the join failed. - */ - public void lateJoin(Message message) throws JMSException - { - throw new RuntimeException("Not implemented."); - /* - // Extract the joining clients details from its join request message. - TestClientDetails clientDetails = new TestClientDetails(); - clientDetails.clientName = message.getStringProperty("CLIENT_NAME"); - clientDetails.privateControlKey = message.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); - - // Register the joining client, but do block for confirmation as cannot do a synchronous receivers during this - // method call, as it may have been called from an 'onMessage' method. - assignReceiverRole(clientDetails, new Properties(), false); - */ - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as known to the test - * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test - * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case - * name "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "Perf_SustainedPubSub"; - } -} +/* + * + * 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.sustained; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.test.framework.DropInTest; +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import javax.jms.JMSException; +import javax.jms.Message; + +import java.util.Properties; + +/** + * SustainedTestCase is a {@link FrameworkBaseCase} that runs the "Perf_SustainedPubSub" test case. This consists of one + * test client sending, and several receiving, and attempts to find the highest rate at which messages can be broadcast + * to the receivers. It is also a {@link DropInTest} to which more test clients may be added during a test run. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
+ *
+ */ +public class SustainedTestCase extends FrameworkBaseCase implements DropInTest +{ + /** Used for debugging. */ + Logger log = Logger.getLogger(SustainedTestCase.class); + + /** Holds the root name of the topic on which to send the test messages. */ + private static final String SUSTAINED_KEY = "Perf_SustainedPubSub"; + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public SustainedTestCase(String name) + { + super(name); + } + + /** + * Performs a single test run of the sustained test. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicPubSub() throws Exception + { + log.debug("public void testSinglePubSubCycle(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "Perf_SustainedPubSub"); + testConfig.put("SUSTAINED_KEY", SUSTAINED_KEY); + testConfig.put("SUSTAINED_NUM_RECEIVERS", Integer.getInteger("numReceives", 2)); + testConfig.put("SUSTAINED_UPDATE_INTERVAL", Integer.getInteger("batchSize", 1000)); + testConfig.put("SUSTAINED_UPDATE_KEY", SUSTAINED_KEY + ".UPDATE"); + testConfig.put("ACKNOWLEDGE_MODE", Integer.getInteger("ackMode", AMQSession.AUTO_ACKNOWLEDGE)); + + log.info("Created Config: " + testConfig.entrySet().toArray()); + + getCircuitFactory().sequenceTest(null, null, testConfig); + } + + /** + * Accepts a late joining client into this test case. The client will be enlisted with a control message + * with the 'CONTROL_TYPE' field set to the value 'LATEJOIN'. It should also provide values for the fields: + * + *

+ *
CLIENT_NAME A unique name for the new client. + *
CLIENT_PRIVATE_CONTROL_KEY The key for the route on which the client receives its control messages. + *
+ * + * @param message The late joiners join message. + * + * @throws JMSException Any JMS Exception are allowed to fall through, indicating that the join failed. + */ + public void lateJoin(Message message) throws JMSException + { + throw new RuntimeException("Not implemented."); + /* + // Extract the joining clients details from its join request message. + TestClientDetails clientDetails = new TestClientDetails(); + clientDetails.clientName = message.getStringProperty("CLIENT_NAME"); + clientDetails.privateControlKey = message.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); + + // Register the joining client, but do block for confirmation as cannot do a synchronous receivers during this + // method call, as it may have been called from an 'onMessage' method. + assignReceiverRole(clientDetails, new Properties(), false); + */ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as known to the test + * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test + * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case + * name "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "Perf_SustainedPubSub"; + } +} -- cgit v1.2.1 From 225da1d61dba68630cc6478e46493152a009a20d Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Tue, 15 Jul 2008 08:48:27 +0000 Subject: Multi-version interop test script git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@676831 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/bin/interoptests.py | 173 +++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100755 qpid/java/integrationtests/bin/interoptests.py (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/bin/interoptests.py b/qpid/java/integrationtests/bin/interoptests.py new file mode 100755 index 0000000000..210079383e --- /dev/null +++ b/qpid/java/integrationtests/bin/interoptests.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python +# +# +# 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. + +import glob, os, subprocess +from optparse import OptionParser +from subprocess import Popen + +interop_cases = ["InteropTestCase1DummyRun", "InteropTestCase2BasicP2P", "InteropTestCase3BasicPubSub", "InteropTestCase4P2PMessageSize", "InteropTestCase5PubSubMessageSize"] + +interop_command = "java -cp %s org.apache.qpid.test.framework.distributedtesting.Coordinator --xml -e interop -o . -n interop org.apache.qpid.interop.testcases.%s" + +# TODO: read this from the ant properties file +clientlibs = ["qpid-integrationtests-incubating-M3.jar", + "qpid-junit-toolkit-incubating-M3.jar", + "junit-3.8.1.jar", + "qpid-systests-incubating-M3.jar", + "qpid-junit-toolkit-incubating-M3.jar", + "geronimo-jms_1.1_spec-1.0.jar", + "log4j-1.2.12.jar"] + +def main(): + parser = OptionParser() + parser.add_option("-t", "--testlib", dest="testlib", action="store", + type="string", + help="The directory containing the test classes to run") + parser.add_option("-b", "--brokers", dest="brokers", action="store", + type="string", + help="The directory containing the unpacked brokers to test") + parser.add_option("-j", "--java", dest="java", action="store", + type="string", + help="The directory containing the java client to test") + parser.add_option("-d", "--dotnet", dest="dotnet", action="store", + type="string", + help="The directory containing the .Net client to test") + parser.add_option("-c", "--cpp", dest="cpp", action="store", + type="string", + help="The directory containing the C++ client to test") + (options, args) = parser.parse_args() + + # check available brokers + if (options.brokers == None or + not os.path.exists(options.brokers) or + not os.path.isdir(options.brokers)): + parser.error("Broker directory must be specified and must exist") + + # check available clients + if (options.java == None or + not os.path.exists(options.java) or + not os.path.isdir(options.java)): + parser.error("Java client directory must be specified and must exist") + + # check available tests + if (options.testlib == None or + not os.path.exists(options.testlib) or + not os.path.isdir(options.testlib)): + parser.error("Test directory must be specified and must exist") + + # check dotnet test client + if (options.testlib == None or + not os.path.exists(options.dotnet) or + not os.path.exists(options.dotnet+"/TestClient.exe")): + parser.error(".Net test directory must be specified and must contain TestClient.exe") + + # Get list of available broker and client versions + brokers = glob.glob(options.brokers+"/qpid-[0-9].[0-9].[0-9].[0-9]") + java_clients = glob.glob(options.java+"/qpid-[0-9].[0-9].[0-9].[0-9]*") + + if (not len(brokers) > 0): + parser.error("Broker directory did not contain any brokers!") + + if (not len(java_clients) > 0): + parser.error("Broker directory did not contain any brokers!") + + for broker in brokers: + if (os.path.isdir(broker)): + for client in java_clients: + if (os.path.isdir(client)): + test(options.testlib, broker, client, options.dotnet) + +def start_dotnet(dotnetpath): + return Popen(["%s/TestClient.exe" % os.path.abspath(dotnetpath), + "-bamqp://guest:guest@clientid/?brokerlist=\'tcp://localhost:5672\'"], + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + +def start_java(javapath): + # Setup classpath + classpath = "" + for lib in clientlibs: + classpath = classpath + testlibdir+"/"+lib+";" + + classpath = classpath + clientpath+"/lib/qpid-incubating.jar" + + # Add qpid common since the tests need that, classpath hatefulness + classpath = classpath + ";"+testlibdir+"/qpid-common-incubating-M3.jar" + + return Popen(["java", "-cp","\""+classpath+"\"", + "org.apache.qpid.test.framework.distributedtesting.TestClient"], + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + +def start_cpp(cpppath): + pass + +def run_tests(): + for testcase in interop_cases: + cmd = " ".join(["java", "-cp","\""+classpath+"\"", "org.apache.qpid.test.framework.distributedtesting.Coordinator", + "--xml", "-e", "interop", "-o", ".", "-n", "interop", ("org.apache.qpid.interop.testcases.%s" % testcase)]) + fd = os.popen(cmd, "r") + output = fd.read() + fd.close() + +def test(testlibdir, brokerpath, javapath, dotnetpath, cpppath): + print ("Testing broker in %s\nclient in %s" % (brokerpath, javapath)) + print ("Logging too %s" % ("/tmp/qpid-interop-tests/%s-%s" % + (os.path.basename(brokerpath), os.path.basename(javapath)))) + os.environ["QPID_HOME"] = brokerpath + os.environ["QPID_WORK"] = ("/tmp/qpid-interop-tests/%s-%s" % + (os.path.basename(brokerpath), os.path.basename(javapath))) + + brokerp, javacp, dotnetp, cppp, coordinatorp = (None, None, None, None, None) + + try: + # Start broker + brokerp = Popen(brokerpath+"/bin/qpid-server", + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + print ("Started broker, pid %s" % brokerp.pid) + + cppp = start_cpp(cpppath) + print ("Started C++ client, pid %s" % cppp.pid) + + dotnetp = start_dotnet(dotnetpath) + print ("Started .Net client, pid %s" % dotnetp.pid) + + javacp = start_java(javapath) + print ("Started client, pid %s" % javacp.pid) + + run_tests() + + finally: + # Shutdown broker and clients + if javacp != None: + os.kill(javacp.pid, 9) + if dotnetp != None: + os.kill(dotnetp.pid, 9) + if coordinatorp != None: + os.kill(coordinatorp.pid, 9) + if brokerp != None: + os.kill(brokerp.pid, 9) + + # Start coordinator + + +if __name__ == "__main__": + main() -- cgit v1.2.1 From b29078ad1293a07271cb278a80f9b1d8cbdd81a4 Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Tue, 15 Jul 2008 16:17:09 +0000 Subject: fix cpp client, path changes git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@676951 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/bin/interoptests.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/bin/interoptests.py b/qpid/java/integrationtests/bin/interoptests.py index 210079383e..f5a50fc1eb 100755 --- a/qpid/java/integrationtests/bin/interoptests.py +++ b/qpid/java/integrationtests/bin/interoptests.py @@ -19,8 +19,8 @@ # under the License. import glob, os, subprocess -from optparse import OptionParser from subprocess import Popen +from optparse import OptionParser interop_cases = ["InteropTestCase1DummyRun", "InteropTestCase2BasicP2P", "InteropTestCase3BasicPubSub", "InteropTestCase4P2PMessageSize", "InteropTestCase5PubSubMessageSize"] @@ -73,10 +73,16 @@ def main(): parser.error("Test directory must be specified and must exist") # check dotnet test client - if (options.testlib == None or + if (options.dotnet == None or not os.path.exists(options.dotnet) or not os.path.exists(options.dotnet+"/TestClient.exe")): parser.error(".Net test directory must be specified and must contain TestClient.exe") + + # check cpp test client + if (options.cpp == None or + not os.path.exists(options.cpp) or + not os.path.exists(options.cpp+"/src/tests/interop_runner")): + parser.error("C++ test directory must be specified and must contain test client") # Get list of available broker and client versions brokers = glob.glob(options.brokers+"/qpid-[0-9].[0-9].[0-9].[0-9]") @@ -89,10 +95,9 @@ def main(): parser.error("Broker directory did not contain any brokers!") for broker in brokers: - if (os.path.isdir(broker)): - for client in java_clients: - if (os.path.isdir(client)): - test(options.testlib, broker, client, options.dotnet) + for client in java_clients: + test(options.testlib, broker, client, options.dotnet, + options.cpp) def start_dotnet(dotnetpath): return Popen(["%s/TestClient.exe" % os.path.abspath(dotnetpath), @@ -106,7 +111,7 @@ def start_java(javapath): for lib in clientlibs: classpath = classpath + testlibdir+"/"+lib+";" - classpath = classpath + clientpath+"/lib/qpid-incubating.jar" + classpath = classpath + javapath+"/lib/qpid-incubating.jar" # Add qpid common since the tests need that, classpath hatefulness classpath = classpath + ";"+testlibdir+"/qpid-common-incubating-M3.jar" @@ -117,7 +122,9 @@ def start_java(javapath): stderr=subprocess.STDOUT) def start_cpp(cpppath): - pass + return Popen([cpppath+"tests/interop_runner"], + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) def run_tests(): for testcase in interop_cases: -- cgit v1.2.1 From e37656ebe6afe9611cf8ff0ec2e458117d4a129f Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Sat, 15 Nov 2008 14:10:10 +0000 Subject: updated version numbers and release notes for M4 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@714267 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/bin/interoptests.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/bin/interoptests.py b/qpid/java/integrationtests/bin/interoptests.py index f5a50fc1eb..128b6da183 100755 --- a/qpid/java/integrationtests/bin/interoptests.py +++ b/qpid/java/integrationtests/bin/interoptests.py @@ -27,11 +27,11 @@ interop_cases = ["InteropTestCase1DummyRun", "InteropTestCase2BasicP2P", "Intero interop_command = "java -cp %s org.apache.qpid.test.framework.distributedtesting.Coordinator --xml -e interop -o . -n interop org.apache.qpid.interop.testcases.%s" # TODO: read this from the ant properties file -clientlibs = ["qpid-integrationtests-incubating-M3.jar", - "qpid-junit-toolkit-incubating-M3.jar", +clientlibs = ["qpid-integrationtests-incubating-M4.jar", + "qpid-junit-toolkit-incubating-M4.jar", "junit-3.8.1.jar", - "qpid-systests-incubating-M3.jar", - "qpid-junit-toolkit-incubating-M3.jar", + "qpid-systests-incubating-M4.jar", + "qpid-junit-toolkit-incubating-M4.jar", "geronimo-jms_1.1_spec-1.0.jar", "log4j-1.2.12.jar"] @@ -114,7 +114,7 @@ def start_java(javapath): classpath = classpath + javapath+"/lib/qpid-incubating.jar" # Add qpid common since the tests need that, classpath hatefulness - classpath = classpath + ";"+testlibdir+"/qpid-common-incubating-M3.jar" + classpath = classpath + ";"+testlibdir+"/qpid-common-incubating-M4.jar" return Popen(["java", "-cp","\""+classpath+"\"", "org.apache.qpid.test.framework.distributedtesting.TestClient"], -- cgit v1.2.1 From cad0d986721f80d7e1dbbad3295fb9eede860648 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 10 Dec 2008 01:25:22 +0000 Subject: removed maven build system git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@724947 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/pom.xml | 146 ------------------------------------- 1 file changed, 146 deletions(-) delete mode 100644 qpid/java/integrationtests/pom.xml (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/pom.xml b/qpid/java/integrationtests/pom.xml deleted file mode 100644 index bbc12d4d00..0000000000 --- a/qpid/java/integrationtests/pom.xml +++ /dev/null @@ -1,146 +0,0 @@ - - - - 4.0.0 - org.apache.qpid - qpid-integrationtests - jar - 1.0-incubating-M3-SNAPSHOT - Qpid Integration Tests - - - org.apache.qpid - qpid - 1.0-incubating-M3-SNAPSHOT - ../pom.xml - - - - .. - - - - - - org.apache.qpid - qpid-client - - - - org.apache.qpid - qpid-systests - - - - org.slf4j - slf4j-log4j12 - 1.4.0 - - - - org.apache.qpid - junit-toolkit - compile - - - - - junit - junit - compile - - - - - net.sf.retrotranslator - retrotranslator-runtime - provided - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - - - org.codehaus.mojo - retrotranslator-maven-plugin - - - retro-intergration-tests - package - - translate-project - - - - ${project.build.directory}/${project.build.finalName}-java14.jar - ${retrotranslator.verify} - - ${retrotranslator.1.4-rt-path} - ${retrotranslator.1.4-jce-path} - ${retrotranslator.1.4-jsse-path} - ${retrotranslator.1.4-sasl-path} - - false - java14 - true - - - - - - - org.apache.maven.plugins - maven-assembly-plugin - 2.2-SNAPSHOT - - - jar-with-dependencies.xml - - target - target/assembly/work - - - - - - - - - - false - src/resources - - **/* - - - - - - - -- cgit v1.2.1 From ea21ffd2442abb830ff9860c8b2576daa7a4d353 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 10 Dec 2008 01:29:01 +0000 Subject: removed incubating from various shell scripts, release artifacts, and READMEs git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@724948 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/bin/interoptests.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/bin/interoptests.py b/qpid/java/integrationtests/bin/interoptests.py index 128b6da183..a883a22a5f 100755 --- a/qpid/java/integrationtests/bin/interoptests.py +++ b/qpid/java/integrationtests/bin/interoptests.py @@ -27,11 +27,11 @@ interop_cases = ["InteropTestCase1DummyRun", "InteropTestCase2BasicP2P", "Intero interop_command = "java -cp %s org.apache.qpid.test.framework.distributedtesting.Coordinator --xml -e interop -o . -n interop org.apache.qpid.interop.testcases.%s" # TODO: read this from the ant properties file -clientlibs = ["qpid-integrationtests-incubating-M4.jar", - "qpid-junit-toolkit-incubating-M4.jar", +clientlibs = ["qpid-integrationtests-M4.jar", + "qpid-junit-toolkit-M4.jar", "junit-3.8.1.jar", - "qpid-systests-incubating-M4.jar", - "qpid-junit-toolkit-incubating-M4.jar", + "qpid-systests-M4.jar", + "qpid-junit-toolkit-M4.jar", "geronimo-jms_1.1_spec-1.0.jar", "log4j-1.2.12.jar"] @@ -111,10 +111,10 @@ def start_java(javapath): for lib in clientlibs: classpath = classpath + testlibdir+"/"+lib+";" - classpath = classpath + javapath+"/lib/qpid-incubating.jar" + classpath = classpath + javapath+"/lib/qpid-all.jar" # Add qpid common since the tests need that, classpath hatefulness - classpath = classpath + ";"+testlibdir+"/qpid-common-incubating-M4.jar" + classpath = classpath + ";"+testlibdir+"/qpid-common-M4.jar" return Popen(["java", "-cp","\""+classpath+"\"", "org.apache.qpid.test.framework.distributedtesting.TestClient"], -- cgit v1.2.1 From 631ea4e3864ef078a2c7b336a8434a46bca9a24a Mon Sep 17 00:00:00 2001 From: Rajith Muditha Attapattu Date: Tue, 11 May 2010 23:33:55 +0000 Subject: QPID-2599 Removing all log4j xml and property files from the code base, except for the ones in test-profile folder and broker/etc folder. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@943339 13f79535-47bb-0310-9956-ffa450edef68 --- .../src/resources/sustained-log4j.xml | 69 ---------------------- 1 file changed, 69 deletions(-) delete mode 100644 qpid/java/integrationtests/src/resources/sustained-log4j.xml (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/src/resources/sustained-log4j.xml b/qpid/java/integrationtests/src/resources/sustained-log4j.xml deleted file mode 100644 index c5ab3137bf..0000000000 --- a/qpid/java/integrationtests/src/resources/sustained-log4j.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- cgit v1.2.1 From 95cf440ecdb2be54931bb6e0b3391ef855b8d098 Mon Sep 17 00:00:00 2001 From: Robert Gemmell Date: Fri, 4 Jun 2010 14:37:50 +0000 Subject: QPID-2599: restore log4j settings files which are not named log4j.xml or log4j.properties and so need not be removed completely since they wont be automatically applied by log4j if found on the classpath like the others will git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@951419 13f79535-47bb-0310-9956-ffa450edef68 --- .../src/resources/sustained-log4j.xml | 69 ++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 qpid/java/integrationtests/src/resources/sustained-log4j.xml (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/src/resources/sustained-log4j.xml b/qpid/java/integrationtests/src/resources/sustained-log4j.xml new file mode 100644 index 0000000000..c5ab3137bf --- /dev/null +++ b/qpid/java/integrationtests/src/resources/sustained-log4j.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.1 From a0931c1851659f80de2a94719af8ad573dcf0b73 Mon Sep 17 00:00:00 2001 From: Robert Gemmell Date: Fri, 6 Aug 2010 14:26:48 +0000 Subject: QPID-2787: Move QpidTestCase to Common test module so that any test can inherit from it, allowing exclusions to be applied. Add ability to gather the class name of the message store associated with the test profile in use. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@982986 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/integrationtests/build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/java/integrationtests') diff --git a/qpid/java/integrationtests/build.xml b/qpid/java/integrationtests/build.xml index 29d066edb7..ae870c5675 100644 --- a/qpid/java/integrationtests/build.xml +++ b/qpid/java/integrationtests/build.xml @@ -20,7 +20,7 @@ --> - + -- cgit v1.2.1