diff options
| author | Kim van der Riet <kpvdr@apache.org> | 2012-05-04 15:39:19 +0000 |
|---|---|---|
| committer | Kim van der Riet <kpvdr@apache.org> | 2012-05-04 15:39:19 +0000 |
| commit | 633c33f224f3196f3f9bd80bd2e418d8143fea06 (patch) | |
| tree | 1391da89470593209466df68c0b40b89c14963b1 /java/perftests/src | |
| parent | c73f9286ebff93a6c8dbc29cf05e258c4b55c976 (diff) | |
| download | qpid-python-633c33f224f3196f3f9bd80bd2e418d8143fea06.tar.gz | |
QPID-3858: Updated branch - merged from trunk r.1333987
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/asyncstore@1334037 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'java/perftests/src')
169 files changed, 15023 insertions, 6508 deletions
diff --git a/java/perftests/src/main/java/org/apache/qpid/client/message/TestMessageFactory.java b/java/perftests/src/main/java/org/apache/qpid/client/message/TestMessageFactory.java deleted file mode 100644 index d94ca5592b..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/client/message/TestMessageFactory.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.client.message; - -import javax.jms.JMSException; -import javax.jms.Session; -import javax.jms.ObjectMessage; -import javax.jms.StreamMessage; -import javax.jms.BytesMessage; -import javax.jms.TextMessage; -import javax.jms.DeliveryMode; -import javax.jms.Destination; - -public class TestMessageFactory -{ - private static final String MESSAGE_DATA_BYTES = "-message payload-message paylaod-message payload-message paylaod"; - - public static TextMessage newTextMessage(Session session, int size) throws JMSException - { - return session.createTextMessage(createMessagePayload(size)); - } - - public static TextMessage newJMSTextMessage(Session session, int size, String encoding) throws JMSException - { - - TextMessage message = session.createTextMessage(); - message.clearBody(); - message.setText(createMessagePayload(size)); - return message; - } - - public static BytesMessage newBytesMessage(Session session, int size) throws JMSException - { - BytesMessage message = session.createBytesMessage(); - message.writeUTF(createMessagePayload(size)); - return message; - } - - public static StreamMessage newStreamMessage(Session session, int size) throws JMSException - { - StreamMessage message = session.createStreamMessage(); - message.writeString(createMessagePayload(size)); - return message; - } - - public static ObjectMessage newObjectMessage(Session session, int size) throws JMSException - { - if (size == 0) - { - return session.createObjectMessage(); - } - else - { - return session.createObjectMessage(createMessagePayload(size)); - } - } - - /** - * Creates an ObjectMessage with given size and sets the JMS properties (JMSReplyTo and DeliveryMode) - * @param session - * @param replyDestination - * @param size - * @param persistent - * @return the new ObjectMessage - * @throws JMSException - */ - public static ObjectMessage newObjectMessage(Session session, Destination replyDestination, int size, boolean persistent) throws JMSException - { - ObjectMessage msg = newObjectMessage(session, size); - - // Set the messages persistent delivery flag. - msg.setJMSDeliveryMode(persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT); - - // Ensure that the temporary reply queue is set as the reply to destination for the message. - if (replyDestination != null) - { - msg.setJMSReplyTo(replyDestination); - } - - return msg; - } - - public static String createMessagePayload(int size) - { - StringBuffer buf = new StringBuffer(size); - int count = 0; - while (count <= (size - MESSAGE_DATA_BYTES.length())) - { - buf.append(MESSAGE_DATA_BYTES); - count += MESSAGE_DATA_BYTES.length(); - } - if (count < size) - { - buf.append(MESSAGE_DATA_BYTES, 0, size - count); - } - - return buf.toString(); - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java b/java/perftests/src/main/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java deleted file mode 100644 index 76fd318625..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java +++ /dev/null @@ -1,47 +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.config; - -import org.apache.qpid.client.AMQConnectionFactory; -import org.apache.qpid.client.AMQConnectionURL; -import org.apache.qpid.config.ConnectionFactoryInitialiser; -import org.apache.qpid.config.ConnectorConfig; -import org.apache.qpid.jms.ConnectionURL; -import org.apache.qpid.url.URLSyntaxException; - -import javax.jms.ConnectionFactory; - -class AMQConnectionFactoryInitialiser implements ConnectionFactoryInitialiser -{ - public ConnectionFactory getFactory(ConnectorConfig config) - { - try - { - final ConnectionURL connectionUrl = new AMQConnectionURL(ConnectionURL.AMQ_PROTOCOL + - "://guest:guest@/test_path?brokerlist='tcp://" + config.getHost() + ":" + config.getPort() + "'"); - return new AMQConnectionFactory(connectionUrl); - } - catch (URLSyntaxException e) - { - throw new RuntimeException("Problem building URL", e); - } - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/config/AbstractConfig.java b/java/perftests/src/main/java/org/apache/qpid/config/AbstractConfig.java deleted file mode 100644 index 14db74438f..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/config/AbstractConfig.java +++ /dev/null @@ -1,69 +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.config; - -public abstract class AbstractConfig -{ - public boolean setOptions(String[] argv) - { - try - { - for(int i = 0; i < argv.length - 1; i += 2) - { - String key = argv[i]; - String value = argv[i+1]; - setOption(key, value); - } - return true; - } - catch(Exception e) - { - System.out.println(e.getMessage()); - } - return false; - } - - protected int parseInt(String msg, String i) - { - try - { - return Integer.parseInt(i); - } - catch(NumberFormatException e) - { - throw new RuntimeException(msg + ": " + i, e); - } - } - - protected long parseLong(String msg, String i) - { - try - { - return Long.parseLong(i); - } - catch(NumberFormatException e) - { - throw new RuntimeException(msg + ": " + i, e); - } - } - - public abstract void setOption(String key, String value); -} diff --git a/java/perftests/src/main/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java b/java/perftests/src/main/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java deleted file mode 100644 index a0248a8f79..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java +++ /dev/null @@ -1,112 +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.config; - -import org.apache.qpid.config.ConnectionFactoryInitialiser; -import org.apache.qpid.config.ConnectorConfig; -import org.apache.qpid.client.JMSAMQException; - -import javax.jms.ConnectionFactory; -import javax.jms.JMSException; -import javax.management.MBeanServerConnection; -import javax.management.ObjectName; -import javax.management.MBeanException; -import javax.naming.InitialContext; -import javax.naming.NamingException; -import javax.naming.NameNotFoundException; -import java.util.Hashtable; - -public class JBossConnectionFactoryInitialiser implements ConnectionFactoryInitialiser -{ - public ConnectionFactory getFactory(ConnectorConfig config) throws JMSException - { - ConnectionFactory cf = null; - InitialContext ic = null; - Hashtable ht = new Hashtable(); - ht.put(InitialContext.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory"); - String jbossHost = System.getProperty("jboss.host", "eqd-lxamq01"); - String jbossPort = System.getProperty("jboss.port", "1099"); - ht.put(InitialContext.PROVIDER_URL, "jnp://" + jbossHost + ":" + jbossPort); - ht.put(InitialContext.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces"); - - try - { - ic = new InitialContext(ht); - if (!doesDestinationExist("topictest.messages", ic)) - { - deployTopic("topictest.messages", ic); - } - if (!doesDestinationExist("topictest.control", ic)) - { - deployTopic("topictest.control", ic); - } - - cf = (ConnectionFactory) ic.lookup("/ConnectionFactory"); - return cf; - } - catch (NamingException e) - { - throw new JMSAMQException("Unable to lookup object: " + e, e); - } - catch (Exception e) - { - throw new JMSAMQException("Error creating topic: " + e, e); - } - } - - private boolean doesDestinationExist(String name, InitialContext ic) throws Exception - { - try - { - ic.lookup("/" + name); - } - catch (NameNotFoundException e) - { - return false; - } - return true; - } - - private void deployTopic(String name, InitialContext ic) throws Exception - { - MBeanServerConnection mBeanServer = lookupMBeanServerProxy(ic); - - ObjectName serverObjectName = new ObjectName("jboss.messaging:service=ServerPeer"); - - String jndiName = "/" + name; - try - { - mBeanServer.invoke(serverObjectName, "createTopic", - new Object[]{name, jndiName}, - new String[]{"java.lang.String", "java.lang.String"}); - } - catch (MBeanException e) - { - System.err.println("Error: " + e); - System.err.println("Cause: " + e.getCause()); - } - } - - private MBeanServerConnection lookupMBeanServerProxy(InitialContext ic) throws NamingException - { - return (MBeanServerConnection) ic.lookup("jmx/invoker/RMIAdaptor"); - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/AbstractRunner.java b/java/perftests/src/main/java/org/apache/qpid/disttest/AbstractRunner.java new file mode 100644 index 0000000000..9e865010f8 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/AbstractRunner.java @@ -0,0 +1,74 @@ +/* + * + * 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.disttest; + +import java.io.FileInputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import javax.naming.Context; +import javax.naming.InitialContext; + +public class AbstractRunner +{ + public static final String JNDI_CONFIG_PROP = "jndi-config"; + public static final String JNDI_CONFIG_DEFAULT = "perftests-jndi.properties"; + + private Map<String,String> _cliOptions = new HashMap<String, String>(); + { + getCliOptions().put(JNDI_CONFIG_PROP, JNDI_CONFIG_DEFAULT); + } + + protected Context getContext() + { + Context context = null; + + try + { + final Properties properties = new Properties(); + properties.load(new FileInputStream(getJndiConfig())); + context = new InitialContext(properties); + } + catch (Exception e) + { + throw new DistributedTestException("Exception while loading JNDI properties", e); + } + + return context; + } + + public void parseArgumentsIntoConfig(String[] args) + { + ArgumentParser argumentParser = new ArgumentParser(); + argumentParser.parseArgumentsIntoConfig(getCliOptions(), args); + } + + protected String getJndiConfig() + { + return getCliOptions().get(AbstractRunner.JNDI_CONFIG_PROP); + } + + protected Map<String,String> getCliOptions() + { + return _cliOptions; + } +}
\ No newline at end of file diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/ArgumentParser.java b/java/perftests/src/main/java/org/apache/qpid/disttest/ArgumentParser.java new file mode 100644 index 0000000000..8c1f8675e3 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/ArgumentParser.java @@ -0,0 +1,44 @@ +/* + * + * 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.disttest; + +import java.util.Map; + +public class ArgumentParser +{ + public void parseArgumentsIntoConfig(Map<String, String> initialValues, String[] args) + { + for(String arg: args) + { + String[] splitArg = arg.split("="); + if(splitArg.length != 2) + { + throw new IllegalArgumentException("arguments must have format <name>=<value>: " + arg); + } + + if(initialValues.put(splitArg[0], splitArg[1]) == null) + { + throw new IllegalArgumentException("not a valid configuration property: " + arg); + } + } + + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/ClientRunner.java b/java/perftests/src/main/java/org/apache/qpid/disttest/ClientRunner.java new file mode 100644 index 0000000000..9c2f4a3d95 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/ClientRunner.java @@ -0,0 +1,94 @@ +/* + * 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.disttest; + +import javax.naming.Context; +import javax.naming.NamingException; + +import org.apache.qpid.disttest.client.Client; +import org.apache.qpid.disttest.jms.ClientJmsDelegate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ClientRunner extends AbstractRunner +{ + private static final Logger LOGGER = LoggerFactory.getLogger(ClientRunner.class); + + public static final String NUM_OF_CLIENTS_PROP = "number-of-clients"; + + public static final String NUM_OF_CLIENTS_DEFAULT = "1"; + + public ClientRunner() + { + getCliOptions().put(NUM_OF_CLIENTS_PROP, NUM_OF_CLIENTS_DEFAULT); + } + + public static void main(String[] args) + { + ClientRunner runner = new ClientRunner(); + runner.parseArgumentsIntoConfig(args); + runner.runClients(); + } + + public void setJndiPropertiesFileLocation(String jndiConfigFileLocation) + { + getCliOptions().put(JNDI_CONFIG_PROP, jndiConfigFileLocation); + } + + /** + * Run the clients in the background + */ + public void runClients() + { + int numClients = Integer.parseInt(getCliOptions().get(NUM_OF_CLIENTS_PROP)); + + Context context = getContext(); + + for(int i = 1; i <= numClients; i++) + { + createBackgroundClient(context); + } + } + + private void createBackgroundClient(Context context) + { + try + { + final Client client = new Client(new ClientJmsDelegate(context)); + + final Thread clientThread = new Thread(new Runnable() + { + @Override + public void run() + { + LOGGER.info("Starting client " + client.getClientName()); + client.start(); + client.waitUntilStopped(); + LOGGER.info("Stopped client " + client.getClientName()); + } + }); + clientThread.start(); + } + catch (NamingException e) + { + throw new DistributedTestException("Exception while creating client instance", e); + } + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/ControllerRunner.java b/java/perftests/src/main/java/org/apache/qpid/disttest/ControllerRunner.java new file mode 100644 index 0000000000..aa9c582bf8 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/ControllerRunner.java @@ -0,0 +1,238 @@ +/* + * 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.disttest; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.naming.Context; + +import org.apache.qpid.disttest.controller.Controller; +import org.apache.qpid.disttest.controller.ResultsForAllTests; +import org.apache.qpid.disttest.controller.config.Config; +import org.apache.qpid.disttest.controller.config.ConfigReader; +import org.apache.qpid.disttest.jms.ControllerJmsDelegate; +import org.apache.qpid.disttest.results.aggregation.Aggregator; +import org.apache.qpid.disttest.results.formatting.CSVFormater; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ControllerRunner extends AbstractRunner +{ + private static final Logger LOGGER = LoggerFactory.getLogger(ControllerRunner.class); + + public static final String TEST_CONFIG_PROP = "test-config"; + public static final String DISTRIBUTED_PROP = "distributed"; + public static final String OUTPUT_DIR_PROP = "outputdir"; + + private static final String TEST_CONFIG_DEFAULT = "perftests-config.json"; + private static final String DISTRIBUTED_DEFAULT = "false"; + private static final String OUTPUT_DIR_DEFAULT = "."; + + private final Aggregator _aggregator = new Aggregator(); + + + public ControllerRunner() + { + getCliOptions().put(TEST_CONFIG_PROP, TEST_CONFIG_DEFAULT); + getCliOptions().put(DISTRIBUTED_PROP, DISTRIBUTED_DEFAULT); + getCliOptions().put(OUTPUT_DIR_PROP, OUTPUT_DIR_DEFAULT); + } + + public static void main(String[] args) throws Exception + { + ControllerRunner runner = new ControllerRunner(); + runner.parseArgumentsIntoConfig(args); + runner.runController(); + } + + public void runController() throws Exception + { + Context context = getContext(); + + ControllerJmsDelegate jmsDelegate = new ControllerJmsDelegate(context); + + try + { + runTests(jmsDelegate); + } + finally + { + jmsDelegate.closeConnections(); + } + } + + private void runTests(ControllerJmsDelegate jmsDelegate) + { + Controller controller = new Controller(jmsDelegate, DistributedTestConstants.REGISTRATION_TIMEOUT, DistributedTestConstants.COMMAND_RESPONSE_TIMEOUT); + + final List<String> testConfigFiles = getTestConfigFiles(); + createClientsIfNotDistributed(testConfigFiles); + + try + { + for (String testConfigFile : testConfigFiles) + { + final Config testConfig = buildTestConfigFrom(testConfigFile); + controller.setConfig(testConfig); + + controller.awaitClientRegistrations(); + + LOGGER.info("Running test : " + testConfigFile); + runTest(controller, testConfigFile); + } + } + finally + { + controller.stopAllRegisteredClients(); + } + + } + + private void runTest(Controller controller, String testConfigFile) + { + final Config testConfig = buildTestConfigFrom(testConfigFile); + controller.setConfig(testConfig); + + ResultsForAllTests rawResultsForAllTests = controller.runAllTests(); + ResultsForAllTests resultsForAllTests = _aggregator.aggregateResults(rawResultsForAllTests); + + final String outputFile = generateOutputCsvNameFrom(testConfigFile); + writeResultsToFile(resultsForAllTests, outputFile); + } + + private void createClientsIfNotDistributed(final List<String> testConfigFiles) + { + if(!isDistributed()) + { + int maxNumberOfClients = 0; + for (String testConfigFile : testConfigFiles) + { + final Config testConfig = buildTestConfigFrom(testConfigFile); + final int numClients = testConfig.getTotalNumberOfClients(); + maxNumberOfClients = Math.max(numClients, maxNumberOfClients); + } + + //we must create the required test clients, running in single-jvm mode + for (int i = 1; i <= maxNumberOfClients; i++) + { + ClientRunner clientRunner = new ClientRunner(); + clientRunner.setJndiPropertiesFileLocation(getJndiConfig()); + clientRunner.runClients(); + } + } + } + + private void writeResultsToFile(ResultsForAllTests resultsForAllTests, String outputFile) + { + FileWriter writer = null; + try + { + final String outputCsv = new CSVFormater().format(resultsForAllTests); + writer = new FileWriter(outputFile); + writer.write(outputCsv); + LOGGER.info("Wrote " + resultsForAllTests.getTestResults().size() + " test result(s) to output file " + outputFile); + } + catch (IOException e) + { + throw new DistributedTestException("Unable to write output file " + outputFile, e); + } + finally + { + if (writer != null) + { + try + { + writer.close(); + } + catch (IOException e) + { + LOGGER.error("Failed to close stream for file " + outputFile, e); + } + } + } + } + + private String generateOutputCsvNameFrom(String testConfigFile) + { + final String filenameOnlyWithExtension = new File(testConfigFile).getName(); + final String cvsFile = filenameOnlyWithExtension.replaceFirst(".?\\w*$", ".csv"); + final String outputDir = String.valueOf(getCliOptions().get(ControllerRunner.OUTPUT_DIR_PROP)); + + return new File(outputDir, cvsFile).getAbsolutePath(); + } + + private List<String> getTestConfigFiles() + { + final List<String> testConfigFile = new ArrayList<String>(); + final File configFileOrDirectory = new File(getCliOptions().get(ControllerRunner.TEST_CONFIG_PROP)); + + if (configFileOrDirectory.isDirectory()) + { + final String[] configFiles = configFileOrDirectory.list(new FilenameFilter() + { + @Override + public boolean accept(File dir, String name) + { + return new File(dir, name).isFile() && name.endsWith(".json"); + } + }); + + for (String configFile : configFiles) + { + testConfigFile.add(new File(configFileOrDirectory, configFile).getAbsolutePath()); + } + } + else + { + testConfigFile.add(configFileOrDirectory.getAbsolutePath()); + } + + return testConfigFile; + } + + private Config buildTestConfigFrom(String testConfigFile) + { + ConfigReader configReader = new ConfigReader(); + Config testConfig; + try + { + testConfig = configReader.getConfigFromFile(testConfigFile); + } + catch (FileNotFoundException e) + { + throw new DistributedTestException("Exception while loading test config from " + testConfigFile, e); + } + return testConfig; + } + + private boolean isDistributed() + { + return Boolean.valueOf(getCliOptions().get(ControllerRunner.DISTRIBUTED_PROP)); + } + + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/DistributedTestConstants.java b/java/perftests/src/main/java/org/apache/qpid/disttest/DistributedTestConstants.java new file mode 100644 index 0000000000..c4892edca9 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/DistributedTestConstants.java @@ -0,0 +1,33 @@ +/* + * 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.disttest; + +public abstract class DistributedTestConstants +{ + public static final String PERF_TEST_PROPERTIES_FILE = "perftests.properties"; + + public static final String MSG_COMMAND_PROPERTY = "COMMAND"; + public static final String MSG_JSON_PROPERTY = "JSON"; + + public static final long REGISTRATION_TIMEOUT = 60000; + public static final long COMMAND_RESPONSE_TIMEOUT = 10000; + + public static final String CONTROLLER_QUEUE_JNDI_NAME = "controllerqueue"; +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/DistributedTestException.java b/java/perftests/src/main/java/org/apache/qpid/disttest/DistributedTestException.java new file mode 100644 index 0000000000..d1d24dcfff --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/DistributedTestException.java @@ -0,0 +1,66 @@ +/* + * 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.disttest; + +import org.apache.qpid.disttest.message.Command; + +public class DistributedTestException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + private final Command causeCommand; + + public DistributedTestException(final String message) + { + this(message, (Command) null); + } + + public DistributedTestException(final Throwable cause) + { + this(cause, null); + } + + public DistributedTestException(final String message, final Throwable cause) + { + this(message, cause, null); + } + + public DistributedTestException(final String message, final Command commandCause) + { + super(message); + causeCommand = commandCause; + } + + public DistributedTestException(final Throwable cause, final Command commandCause) + { + super(cause); + causeCommand = commandCause; + } + + public DistributedTestException(final String message, final Throwable cause, final Command commandCause) + { + super(message, cause); + causeCommand = commandCause; + } + + public Command getCauseCommand() + { + return causeCommand; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/Visitor.java b/java/perftests/src/main/java/org/apache/qpid/disttest/Visitor.java new file mode 100644 index 0000000000..52dad9aa4d --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/Visitor.java @@ -0,0 +1,76 @@ +/* + * 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.disttest; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * A variation of the visitor pattern that uses reflection to call the correct + * visit method. By convention, subclasses should provide public + * <pre>visit(SpecificClass)</pre> methods. + */ +public abstract class Visitor +{ + + private static final String VISITOR_METHOD_NAME = "visit"; + + public void visit(Object targetObject) + { + Class<? extends Object> targetObjectClass = targetObject.getClass(); + final Method method = findVisitMethodForTargetObjectClass(targetObjectClass); + invokeVisitMethod(targetObject, method); + } + + private Method findVisitMethodForTargetObjectClass( + Class<? extends Object> targetObjectClass) + { + final Method method; + try + { + method = getClass().getDeclaredMethod(VISITOR_METHOD_NAME, targetObjectClass); + } + catch (Exception e) + { + throw new DistributedTestException("Failed to find method " + VISITOR_METHOD_NAME + " on object of class " + targetObjectClass, e); + } + return method; + } + + private void invokeVisitMethod(Object targetObject, final Method method) + { + try + { + method.invoke(this, targetObject); + } + catch (IllegalArgumentException e) + { + throw new DistributedTestException(e); + } + catch (IllegalAccessException e) + { + throw new DistributedTestException(e); + } + catch (InvocationTargetException e) + { + throw new DistributedTestException(e.getCause()); + } + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/Client.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/Client.java new file mode 100644 index 0000000000..1d2d862301 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/Client.java @@ -0,0 +1,208 @@ +/* + * 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.disttest.client; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import javax.naming.NamingException; + +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.qpid.disttest.DistributedTestException; +import org.apache.qpid.disttest.Visitor; +import org.apache.qpid.disttest.jms.ClientJmsDelegate; +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.disttest.message.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Client +{ + private static final Logger LOGGER = LoggerFactory.getLogger(Client.class); + + private final ClientJmsDelegate _clientJmsDelegate; + + private final CountDownLatch _latch = new CountDownLatch(1); + private Visitor _visitor; + private final AtomicReference<ClientState> _state; + private ParticipantExecutorRegistry _participantRegistry = new ParticipantExecutorRegistry(); + + public Client(final ClientJmsDelegate delegate) throws NamingException + { + _clientJmsDelegate = delegate; + _state = new AtomicReference<ClientState>(ClientState.CREATED); + _visitor = new ClientCommandVisitor(this, _clientJmsDelegate); + } + + /** + * Register with the controller + */ + public void start() + { + _clientJmsDelegate.setInstructionListener(this); + _clientJmsDelegate.sendRegistrationMessage(); + _state.set(ClientState.READY); + } + + public void stop() + { + _state.set(ClientState.STOPPED); + _latch.countDown(); + } + + public void addParticipantExecutor(final ParticipantExecutor participant) + { + _participantRegistry.add(participant); + } + + public void waitUntilStopped() + { + waitUntilStopped(0); + } + + public void waitUntilStopped(final long timeout) + { + try + { + if (timeout == 0) + { + _latch.await(); + } + else + { + _latch.await(timeout, TimeUnit.MILLISECONDS); + } + } + catch (final InterruptedException ie) + { + Thread.currentThread().interrupt(); + } + + _clientJmsDelegate.destroy(); + } + + public void processInstruction(final Command command) + { + if (LOGGER.isInfoEnabled()) + { + LOGGER.info("Client " + getClientName() + " received command: " + command); + } + String responseMessage = null; + try + { + command.accept(_visitor); + } + catch (final Exception e) + { + LOGGER.error("Error processing instruction", e); + responseMessage = e.getMessage(); + } + finally + { + _clientJmsDelegate.sendResponseMessage(new Response(_clientJmsDelegate.getClientName(), command.getType(), responseMessage)); + } + } + + public ClientState getState() + { + return _state.get(); + } + + public String getClientName() + { + return _clientJmsDelegate.getClientName(); + } + + public void setClientCommandVisitor(final ClientCommandVisitor visitor) + { + _visitor = visitor; + } + + public void startTest() + { + if (_state.compareAndSet(ClientState.READY, ClientState.RUNNING_TEST)) + { + try + { + _clientJmsDelegate.startConnections(); + for (final ParticipantExecutor executor : _participantRegistry.executors()) + { + executor.start(this); + } + } + catch (final Exception e) + { + try + { + tearDownTest(); + } + catch (final Exception e2) + { + // ignore + } + throw new DistributedTestException("Error starting test: " + _clientJmsDelegate.getClientName(), e); + } + } + else + { + throw new DistributedTestException("Client '" + _clientJmsDelegate.getClientName() + + "' is not in READY state:" + _state.get()); + } + } + + public void tearDownTest() + { + if (_state.compareAndSet(ClientState.RUNNING_TEST, ClientState.READY)) + { + LOGGER.info("Tearing down test on client: " + _clientJmsDelegate.getClientName()); + + _clientJmsDelegate.closeTestConnections(); + } + else + { + throw new DistributedTestException("Client '" + _clientJmsDelegate.getClientName() + "' is not in RUNNING_TEST state! Ignoring tearDownTest"); + } + + + _participantRegistry.clear(); + } + + public void sendResults(ParticipantResult testResult) + { + _clientJmsDelegate.sendResponseMessage(testResult); + LOGGER.info("Sent test results " + testResult); + } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append("clientJmsDelegate", _clientJmsDelegate).toString(); + } + + void setParticipantRegistry(ParticipantExecutorRegistry participantRegistry) + { + _participantRegistry = participantRegistry; + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/ClientCommandVisitor.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/ClientCommandVisitor.java new file mode 100644 index 0000000000..791897323e --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/ClientCommandVisitor.java @@ -0,0 +1,98 @@ +/* + * 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.disttest.client; + +import org.apache.qpid.disttest.Visitor; +import org.apache.qpid.disttest.jms.ClientJmsDelegate; +import org.apache.qpid.disttest.message.CreateConnectionCommand; +import org.apache.qpid.disttest.message.CreateConsumerCommand; +import org.apache.qpid.disttest.message.CreateMessageProviderCommand; +import org.apache.qpid.disttest.message.CreateProducerCommand; +import org.apache.qpid.disttest.message.CreateSessionCommand; +import org.apache.qpid.disttest.message.NoOpCommand; +import org.apache.qpid.disttest.message.StartTestCommand; +import org.apache.qpid.disttest.message.StopClientCommand; +import org.apache.qpid.disttest.message.TearDownTestCommand; + +public class ClientCommandVisitor extends Visitor +{ + private final Client _client; + private final ClientJmsDelegate _clientJmsDelegate; + + public ClientCommandVisitor(final Client client, final ClientJmsDelegate clientJmsDelegate) + { + super(); + _client = client; + _clientJmsDelegate = clientJmsDelegate; + } + + public void visit(final StopClientCommand command) + { + _client.stop(); + } + + public void visit(final NoOpCommand command) + { + // no-op + } + + public void visit(final CreateConnectionCommand command) + { + _clientJmsDelegate.createConnection(command); + } + + public void visit(final CreateSessionCommand command) + { + _clientJmsDelegate.createSession(command); + } + + public void visit(final CreateProducerCommand command) + { + + final ProducerParticipant participant = new ProducerParticipant(_clientJmsDelegate, command); + _clientJmsDelegate.createProducer(command); + final ParticipantExecutor executor = new ParticipantExecutor(participant); + _client.addParticipantExecutor(executor); + } + + public void visit(final CreateConsumerCommand command) + { + final ConsumerParticipant participant = new ConsumerParticipant(_clientJmsDelegate, command); + _clientJmsDelegate.createConsumer(command); + final ParticipantExecutor executor = new ParticipantExecutor(participant); + _client.addParticipantExecutor(executor); + } + + public void visit(final StartTestCommand command) + { + _client.startTest(); + } + + public void visit(final TearDownTestCommand command) + { + _client.tearDownTest(); + } + + public void visit(final CreateMessageProviderCommand command) + { + _clientJmsDelegate.createMessageProvider(command); + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/config/ConnectorConfig.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/ClientState.java index b120ed3f12..c88c0a6c86 100644 --- a/java/perftests/src/main/java/org/apache/qpid/config/ConnectorConfig.java +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/ClientState.java @@ -1,5 +1,4 @@ /* - * * 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 @@ -7,9 +6,9 @@ * 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 @@ -18,11 +17,9 @@ * under the License. * */ -package org.apache.qpid.config; +package org.apache.qpid.disttest.client; -public interface ConnectorConfig +public enum ClientState { - public String getHost(); - public int getPort(); - public String getFactory(); + CREATED, READY, STOPPED, RUNNING_TEST; } diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/ConsumerParticipant.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/ConsumerParticipant.java new file mode 100644 index 0000000000..1b5e8276c2 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/ConsumerParticipant.java @@ -0,0 +1,251 @@ +/* + * 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.disttest.client; + + +import java.util.Date; +import java.util.NavigableSet; +import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import javax.jms.Message; +import javax.jms.MessageListener; + +import org.apache.qpid.disttest.DistributedTestException; +import org.apache.qpid.disttest.jms.ClientJmsDelegate; +import org.apache.qpid.disttest.message.ConsumerParticipantResult; +import org.apache.qpid.disttest.message.CreateConsumerCommand; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConsumerParticipant implements Participant +{ + private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerParticipant.class); + + private final AtomicInteger _totalNumberOfMessagesReceived = new AtomicInteger(0); + private final NavigableSet<Integer> _allConsumedPayloadSizes = new ConcurrentSkipListSet<Integer>(); + private final AtomicLong _totalPayloadSizeOfAllMessagesReceived = new AtomicLong(0); + private final CountDownLatch _asyncRunHasFinished = new CountDownLatch(1); + private final ClientJmsDelegate _jmsDelegate; + private final CreateConsumerCommand _command; + private final ParticipantResultFactory _resultFactory; + + private long _startTime; + + private volatile Exception _asyncMessageListenerException; + + public ConsumerParticipant(final ClientJmsDelegate delegate, final CreateConsumerCommand command) + { + _jmsDelegate = delegate; + _command = command; + _resultFactory = new ParticipantResultFactory(); + } + + @Override + public ParticipantResult doIt(String registeredClientName) throws Exception + { + final Date start = new Date(); + final int acknowledgeMode = _jmsDelegate.getAcknowledgeMode(_command.getSessionName()); + + if (_command.getMaximumDuration() == 0 && _command.getNumberOfMessages() == 0) + { + throw new DistributedTestException("number of messages and duration cannot both be zero"); + } + + if (_command.isSynchronous()) + { + synchronousRun(); + } + else + { + _jmsDelegate.registerListener(_command.getParticipantName(), new MessageListener(){ + + @Override + public void onMessage(Message message) + { + processAsynchMessage(message); + } + + }); + + waitUntilMsgListenerHasFinished(); + rethrowAnyAsyncMessageListenerException(); + } + + Date end = new Date(); + int numberOfMessagesSent = _totalNumberOfMessagesReceived.get(); + long totalPayloadSize = _totalPayloadSizeOfAllMessagesReceived.get(); + int payloadSize = getPayloadSizeForResultIfConstantOrZeroOtherwise(_allConsumedPayloadSizes); + + ConsumerParticipantResult result = _resultFactory.createForConsumer( + getName(), + registeredClientName, + _command, + acknowledgeMode, + numberOfMessagesSent, + payloadSize, + totalPayloadSize, + start, end); + + return result; + } + + private void synchronousRun() + { + LOGGER.debug("entered synchronousRun: " + this); + + _startTime = System.currentTimeMillis(); + + Message message = null; + + do + { + message = _jmsDelegate.consumeMessage(_command.getParticipantName(), + _command.getReceiveTimeout()); + } while (processMessage(message)); + } + + /** + * @return whether to continue running (ie returns false if the message quota has been reached) + */ + private boolean processMessage(Message message) + { + int messageCount = _totalNumberOfMessagesReceived.incrementAndGet(); + if (LOGGER.isTraceEnabled()) + { + LOGGER.trace("message " + messageCount + " received by " + this); + } + int messagePayloadSize = _jmsDelegate.calculatePayloadSizeFrom(message); + _allConsumedPayloadSizes.add(messagePayloadSize); + _totalPayloadSizeOfAllMessagesReceived.addAndGet(messagePayloadSize); + + boolean batchEnabled = _command.getBatchSize() > 0; + boolean batchComplete = batchEnabled && messageCount % _command.getBatchSize() == 0; + + if (!batchEnabled || batchComplete) + { + if (LOGGER.isTraceEnabled() && batchEnabled) + { + LOGGER.trace("Committing: batch size " + _command.getBatchSize() ); + } + _jmsDelegate.commitOrAcknowledgeMessage(message, _command.getSessionName()); + } + + boolean reachedExpectedNumberOfMessages = _command.getNumberOfMessages() > 0 && messageCount >= _command.getNumberOfMessages(); + boolean reachedMaximumDuration = _command.getMaximumDuration() > 0 && System.currentTimeMillis() - _startTime >= _command.getMaximumDuration(); + boolean finishedConsuming = reachedExpectedNumberOfMessages || reachedMaximumDuration; + + if (finishedConsuming) + { + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("message " + messageCount + + " reachedExpectedNumberOfMessages " + reachedExpectedNumberOfMessages + + " reachedMaximumDuration " + reachedMaximumDuration); + } + + if (batchEnabled && !batchComplete) + { + if (LOGGER.isTraceEnabled()) + { + LOGGER.trace("Committing: batch size " + _command.getBatchSize() ); + } + + // commit/acknowledge remaining messages if necessary + _jmsDelegate.commitOrAcknowledgeMessage(message, _command.getSessionName()); + } + return false; + } + + return true; + } + + + /** + * Intended to be called from a {@link MessageListener}. Updates {@link #_asyncRunHasFinished} if + * no more messages should be processed, causing {@link #doIt(String)} to exit. + */ + public void processAsynchMessage(Message message) + { + boolean continueRunning = true; + try + { + if (_startTime == 0) + { + // reset counter and start time on receiving of first message + _startTime = System.currentTimeMillis(); + } + + continueRunning = processMessage(message); + } + catch (Exception e) + { + LOGGER.error("Error occured consuming message " + _totalNumberOfMessagesReceived, e); + continueRunning = false; + _asyncMessageListenerException = e; + } + + if(!continueRunning) + { + _asyncRunHasFinished.countDown(); + } + } + + @Override + public void releaseResources() + { + _jmsDelegate.closeTestConsumer(_command.getParticipantName()); + } + + private int getPayloadSizeForResultIfConstantOrZeroOtherwise(NavigableSet<Integer> allSizes) + { + return allSizes.size() == 1 ? _allConsumedPayloadSizes.first() : 0; + } + + private void rethrowAnyAsyncMessageListenerException() + { + if (_asyncMessageListenerException != null) + { + throw new DistributedTestException(_asyncMessageListenerException); + } + } + + private void waitUntilMsgListenerHasFinished() throws Exception + { + LOGGER.debug("waiting until message listener has finished for " + this); + _asyncRunHasFinished.await(); + LOGGER.debug("Message listener has finished for " + this); + } + + @Override + public String getName() + { + return _command.getParticipantName(); + } + + @Override + public String toString() + { + return "ConsumerParticipant [_command=" + _command + ", _startTime=" + _startTime + "]"; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/MessageProvider.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/MessageProvider.java new file mode 100644 index 0000000000..2dcf8940b6 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/MessageProvider.java @@ -0,0 +1,212 @@ +/* + * 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.disttest.client; + +import java.lang.reflect.InvocationTargetException; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; +import javax.jms.TextMessage; + +import org.apache.commons.beanutils.BeanUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.qpid.disttest.DistributedTestException; +import org.apache.qpid.disttest.client.property.PropertyValue; +import org.apache.qpid.disttest.message.CreateProducerCommand; + +public class MessageProvider +{ + public static final String TTL = "ttl"; + + public static final String DELIVERY_MODE = "deliveryMode"; + + public static final String PRIORITY = "priority"; + + public static final String[] STANDARD_JMS_PROPERTIES = { "correlationID", DELIVERY_MODE, + "expiration", "messageID", PRIORITY, "redelivered", "replyTo", "timestamp", "type", TTL }; + + private Map<String, PropertyValue> _messageProperties; + private ConcurrentMap<Integer, Future<String>> _payloads; + + public MessageProvider(Map<String, PropertyValue> messageProperties) + { + _messageProperties = messageProperties; + _payloads = new ConcurrentHashMap<Integer, Future<String>>(); + } + + public Message nextMessage(Session session, CreateProducerCommand command) throws JMSException + { + Message message = createTextMessage(session, command); + setMessageProperties(message); + return message; + } + + public boolean isPropertySet(String name) + { + return _messageProperties != null && _messageProperties.containsKey(name); + } + + public void setMessageProperties(Message message) throws JMSException + { + if (_messageProperties != null) + { + for (Entry<String, PropertyValue> entry : _messageProperties.entrySet()) + { + String propertyName = entry.getKey(); + Object propertyValue = entry.getValue().getValue(); + if (isStandardProperty(propertyName)) + { + setStandardProperty(message, propertyName, propertyValue); + } + else + { + setCustomProperty(message, propertyName, propertyValue); + } + } + } + } + + protected void setCustomProperty(Message message, String propertyName, Object propertyValue) throws JMSException + { + if (propertyValue instanceof Integer) + { + message.setIntProperty(propertyName, ((Integer) propertyValue).intValue()); + } + else if (propertyValue instanceof Long) + { + message.setLongProperty(propertyName, ((Long) propertyValue).longValue()); + } + else if (propertyValue instanceof Boolean) + { + message.setBooleanProperty(propertyName, ((Boolean) propertyValue).booleanValue()); + } + else if (propertyValue instanceof Byte) + { + message.setByteProperty(propertyName, ((Byte) propertyValue).byteValue()); + } + else if (propertyValue instanceof Double) + { + message.setDoubleProperty(propertyName, ((Double) propertyValue).doubleValue()); + } + else if (propertyValue instanceof Float) + { + message.setFloatProperty(propertyName, ((Float) propertyValue).floatValue()); + } + else if (propertyValue instanceof Short) + { + message.setShortProperty(propertyName, ((Short) propertyValue).shortValue()); + } + else if (propertyValue instanceof String) + { + message.setStringProperty(propertyName, (String) propertyValue); + } + else + { + message.setObjectProperty(propertyName, propertyValue); + } + } + + protected void setStandardProperty(Message message, String property, Object propertyValue) throws JMSException + { + String propertyName = "JMS" + StringUtils.capitalize(property); + try + { + BeanUtils.setProperty(message, propertyName, propertyValue); + } + catch (IllegalAccessException e) + { + throw new DistributedTestException("Unable to set property " + propertyName + " :" + e.getMessage(), e); + } + catch (InvocationTargetException e) + { + if (e.getCause() instanceof JMSException) + { + throw ((JMSException) e.getCause()); + } + else + { + throw new DistributedTestException("Unable to set property " + propertyName + " :" + e.getMessage(), e); + } + } + } + + protected boolean isStandardProperty(String propertyName) + { + for (int i = 0; i < STANDARD_JMS_PROPERTIES.length; i++) + { + if (propertyName.equals(STANDARD_JMS_PROPERTIES[i])) + { + return true; + } + } + return false; + } + + protected Message createTextMessage(Session ssn, final CreateProducerCommand command) throws JMSException + { + String payload = getMessagePayload(command); + TextMessage msg = ssn.createTextMessage(); + msg.setText(payload); + return msg; + } + + protected String getMessagePayload(final CreateProducerCommand command) + { + FutureTask<String> createTextFuture = new FutureTask<String>(new Callable<String>() + { + @Override + public String call() throws Exception + { + return StringUtils.repeat("a", command.getMessageSize()); + } + }); + + Future<String> future = _payloads.putIfAbsent(command.getMessageSize(), createTextFuture); + if (future == null) + { + createTextFuture.run(); + future = createTextFuture; + } + String payload = null; + try + { + payload = future.get(); + } + catch (Exception e) + { + throw new DistributedTestException("Unable to create message payload :" + e.getMessage(), e); + } + return payload; + } + + @Override + public String toString() + { + return "MessageProvider [_messageProperties=" + _messageProperties + "]"; + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/Participant.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/Participant.java new file mode 100644 index 0000000000..941ec90565 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/Participant.java @@ -0,0 +1,31 @@ +/* + * 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.disttest.client; + +import org.apache.qpid.disttest.message.ParticipantResult; + +public interface Participant +{ + ParticipantResult doIt(String registeredClientName) throws Exception; + + void releaseResources(); + + String getName(); + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/ParticipantExecutor.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/ParticipantExecutor.java new file mode 100644 index 0000000000..d5e307d50f --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/ParticipantExecutor.java @@ -0,0 +1,136 @@ +/* + * 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.disttest.client; + +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ParticipantExecutor +{ + private static final Logger LOGGER = LoggerFactory.getLogger(ParticipantExecutor.class); + + private static final ExecutorService SHARED_UNBOUNDED_THREAD_POOL = Executors.newCachedThreadPool(new DaemonThreadFactory()); + + private Executor _executor = SHARED_UNBOUNDED_THREAD_POOL; + + private Client _client; + + private final Participant _participant; + + private final ParticipantResultFactory _factory; + + public ParticipantExecutor(Participant participant) + { + _participant = participant; + _factory = new ParticipantResultFactory(); + } + + /** + * Schedules the test participant to be run in a background thread. + */ + public void start(Client client) + { + _client = client; + + LOGGER.info("Starting test participant in background thread: " + this); + _executor.execute(new ParticipantRunnable()); + } + + public String getParticipantName() + { + return _participant.getName(); + } + + void setExecutor(Executor executor) + { + _executor = executor; + } + + private class ParticipantRunnable implements Runnable + { + @Override + public final void run() + { + Thread currentThread = Thread.currentThread(); + final String initialThreadName = currentThread.getName(); + currentThread.setName(initialThreadName + "-" + getParticipantName()); + + try + { + runParticipantAndSendResults(); + } + finally + { + currentThread.setName(initialThreadName); + } + } + + private void runParticipantAndSendResults() + { + ParticipantResult result = null; + try + { + if (LOGGER.isInfoEnabled()) + { + LOGGER.info("About to run participant " + _participant); + } + result = _participant.doIt(_client.getClientName()); + } + catch (Throwable t) + { + String errorMessage = "Unhandled error: " + t.getMessage(); + LOGGER.error(errorMessage, t); + result = _factory.createForError(_participant.getName(), _client.getClientName(), errorMessage); + } + finally + { + _client.sendResults(result); + _participant.releaseResources(); + } + } + } + + private static final class DaemonThreadFactory implements ThreadFactory + { + @Override + public Thread newThread(Runnable r) + { + Thread thread = new Thread(r); + thread.setDaemon(true); + return thread; + } + } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append("participantName", getParticipantName()) + .append("client", _client) + .toString(); + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/ParticipantExecutorRegistry.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/ParticipantExecutorRegistry.java new file mode 100644 index 0000000000..3d9780e640 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/ParticipantExecutorRegistry.java @@ -0,0 +1,45 @@ +/* + * 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.disttest.client; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class ParticipantExecutorRegistry +{ + private final Set<ParticipantExecutor> _participantExecutors = Collections.synchronizedSet(new HashSet<ParticipantExecutor>()); + + public void add(ParticipantExecutor participantExecutor) + { + _participantExecutors.add(participantExecutor); + } + + public void clear() + { + _participantExecutors.clear(); + } + + public Collection<ParticipantExecutor> executors() + { + return Collections.unmodifiableSet(_participantExecutors); + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/ParticipantResultFactory.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/ParticipantResultFactory.java new file mode 100644 index 0000000000..7f6b96b87c --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/ParticipantResultFactory.java @@ -0,0 +1,100 @@ +/* + * 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.disttest.client; + +import java.util.Date; + +import org.apache.qpid.disttest.message.ConsumerParticipantResult; +import org.apache.qpid.disttest.message.CreateConsumerCommand; +import org.apache.qpid.disttest.message.CreateParticpantCommand; +import org.apache.qpid.disttest.message.CreateProducerCommand; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.disttest.message.ProducerParticipantResult; + +public class ParticipantResultFactory +{ + public ConsumerParticipantResult createForConsumer(String participantName, String clientRegisteredName, CreateConsumerCommand command, int acknowledgeMode, int numberOfMessagesReceived, int payloadSize, long totalPayloadReceived, Date start, Date end) + { + ConsumerParticipantResult consumerParticipantResult = new ConsumerParticipantResult(); + + setTestProperties(consumerParticipantResult, command, participantName, clientRegisteredName, acknowledgeMode); + setTestResultProperties(consumerParticipantResult, numberOfMessagesReceived, payloadSize, totalPayloadReceived, start, end); + + consumerParticipantResult.setTopic(command.isTopic()); + consumerParticipantResult.setDurableSubscription(command.isDurableSubscription()); + consumerParticipantResult.setBrowsingSubscription(command.isBrowsingSubscription()); + consumerParticipantResult.setSelector(command.getSelector() != null); + consumerParticipantResult.setNoLocal(command.isNoLocal()); + consumerParticipantResult.setSynchronousConsumer(command.isSynchronous()); + consumerParticipantResult.setTotalNumberOfConsumers(1); + consumerParticipantResult.setTotalNumberOfProducers(0); + + return consumerParticipantResult; + } + + public ProducerParticipantResult createForProducer(String participantName, String clientRegisteredName, CreateProducerCommand command, int acknowledgeMode, int numberOfMessagesSent, int payloadSize, long totalPayloadSent, Date start, Date end) + { + final ProducerParticipantResult producerParticipantResult = new ProducerParticipantResult(); + + producerParticipantResult.setStartDelay(command.getStartDelay()); + producerParticipantResult.setDeliveryMode(command.getDeliveryMode()); + producerParticipantResult.setPriority(command.getPriority()); + producerParticipantResult.setInterval(command.getInterval()); + producerParticipantResult.setTimeToLive(command.getTimeToLive()); + producerParticipantResult.setTotalNumberOfConsumers(0); + producerParticipantResult.setTotalNumberOfProducers(1); + + + setTestProperties(producerParticipantResult, command, participantName, clientRegisteredName, acknowledgeMode); + + setTestResultProperties(producerParticipantResult, numberOfMessagesSent, payloadSize, totalPayloadSent, start, end); + + return producerParticipantResult; + } + + private void setTestResultProperties(final ParticipantResult participantResult, int numberOfMessagesSent, int payloadSize, long totalPayloadReceived, Date start, Date end) + { + participantResult.setNumberOfMessagesProcessed(numberOfMessagesSent); + participantResult.setPayloadSize(payloadSize); + participantResult.setTotalPayloadProcessed(totalPayloadReceived); + participantResult.setStartDate(start); + participantResult.setEndDate(end); + } + + private void setTestProperties(final ParticipantResult participantResult, CreateParticpantCommand command, String participantName, String clientRegisteredName, int acknowledgeMode) + { + participantResult.setParticipantName(participantName); + participantResult.setRegisteredClientName(clientRegisteredName); + participantResult.setBatchSize(command.getBatchSize()); + participantResult.setMaximumDuration(command.getMaximumDuration()); + participantResult.setAcknowledgeMode(acknowledgeMode); + + } + + public ParticipantResult createForError(String participantName, String clientRegisteredName, String errorMessage) + { + ParticipantResult result = new ParticipantResult(); + result.setParticipantName(participantName); + result.setRegisteredClientName(clientRegisteredName); + result.setErrorMessage(errorMessage); + + return result; + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/ProducerParticipant.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/ProducerParticipant.java new file mode 100644 index 0000000000..bcad81b4aa --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/ProducerParticipant.java @@ -0,0 +1,174 @@ +/* + * 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.disttest.client; + +import java.util.Date; +import java.util.NavigableSet; +import java.util.TreeSet; + +import javax.jms.Message; + +import org.apache.qpid.disttest.DistributedTestException; +import org.apache.qpid.disttest.jms.ClientJmsDelegate; +import org.apache.qpid.disttest.message.CreateProducerCommand; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ProducerParticipant implements Participant +{ + private static final Logger LOGGER = LoggerFactory.getLogger(ProducerParticipant.class); + + private final ClientJmsDelegate _jmsDelegate; + + private final CreateProducerCommand _command; + + private ParticipantResultFactory _resultFactory; + + public ProducerParticipant(final ClientJmsDelegate jmsDelegate, final CreateProducerCommand command) + { + _jmsDelegate = jmsDelegate; + _command = command; + _resultFactory = new ParticipantResultFactory(); + } + + @Override + public ParticipantResult doIt(String registeredClientName) throws Exception + { + if (_command.getMaximumDuration() == 0 && _command.getNumberOfMessages() == 0) + { + throw new DistributedTestException("number of messages and duration cannot both be zero"); + } + + int acknowledgeMode = _jmsDelegate.getAcknowledgeMode(_command.getSessionName()); + + long expectedDuration = _command.getMaximumDuration() - _command.getStartDelay(); + + doSleepForStartDelay(); + + final long startTime = System.currentTimeMillis(); + + Message lastPublishedMessage = null; + int numberOfMessagesSent = 0; + long totalPayloadSizeOfAllMessagesSent = 0; + NavigableSet<Integer> allProducedPayloadSizes = new TreeSet<Integer>(); + + while (true) + { + numberOfMessagesSent++; + + lastPublishedMessage = _jmsDelegate.sendNextMessage(_command); + + int lastPayloadSize = _jmsDelegate.calculatePayloadSizeFrom(lastPublishedMessage); + totalPayloadSizeOfAllMessagesSent += lastPayloadSize; + allProducedPayloadSizes.add(lastPayloadSize); + + if (LOGGER.isTraceEnabled()) + { + LOGGER.trace("message " + numberOfMessagesSent + " sent by " + this); + } + + final boolean batchLimitReached = _command.getBatchSize() <= 0 + || numberOfMessagesSent % _command.getBatchSize() == 0; + + if (batchLimitReached) + { + if (LOGGER.isTraceEnabled() && _command.getBatchSize() > 0) + { + LOGGER.trace("Committing: batch size " + _command.getBatchSize() ); + } + _jmsDelegate.commitOrAcknowledgeMessage(lastPublishedMessage, _command.getSessionName()); + + if (_command.getInterval() > 0) + { + // sleep for given time + Thread.sleep(_command.getInterval()); + } + } + + if (_command.getNumberOfMessages() > 0 && numberOfMessagesSent >= _command.getNumberOfMessages() + || expectedDuration > 0 && System.currentTimeMillis() - startTime >= expectedDuration) + { + break; + } + } + + // commit the remaining batch messages + if (_command.getBatchSize() > 0 && numberOfMessagesSent % _command.getBatchSize() != 0) + { + if (LOGGER.isTraceEnabled()) + { + LOGGER.trace("Committing: batch size " + _command.getBatchSize() ); + } + _jmsDelegate.commitOrAcknowledgeMessage(lastPublishedMessage, _command.getSessionName()); + } + + Date start = new Date(startTime); + Date end = new Date(); + int payloadSize = getPayloadSizeForResultIfConstantOrZeroOtherwise(allProducedPayloadSizes); + + return _resultFactory.createForProducer( + getName(), + registeredClientName, + _command, + acknowledgeMode, + numberOfMessagesSent, + payloadSize, totalPayloadSizeOfAllMessagesSent, start, end); + } + + private int getPayloadSizeForResultIfConstantOrZeroOtherwise(NavigableSet<Integer> allPayloadSizes) + { + return allPayloadSizes.size() == 1 ? allPayloadSizes.first() : 0; + } + + private void doSleepForStartDelay() + { + if (_command.getStartDelay() > 0) + { + // start delay is specified. Sleeping... + try + { + Thread.sleep(_command.getStartDelay()); + } + catch (final InterruptedException e) + { + Thread.currentThread().interrupt(); + } + } + } + + @Override + public void releaseResources() + { + _jmsDelegate.closeTestProducer(_command.getParticipantName()); + } + + @Override + public String getName() + { + return _command.getParticipantName(); + } + + @Override + public String toString() + { + return "ProducerParticipant [command=" + _command + "]"; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/GeneratedPropertySupport.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/GeneratedPropertySupport.java new file mode 100644 index 0000000000..a49ebf756e --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/GeneratedPropertySupport.java @@ -0,0 +1,68 @@ +/* + * 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.disttest.client.property; + +/** + * Provides support to generate message property values. + */ +public abstract class GeneratedPropertySupport implements GeneratedPropertyValue +{ + private Object _lastValue; + + public GeneratedPropertySupport() + { + super(); + _lastValue = null; + } + + @Override + public Object getValue() + { + Object result = nextValue(); + result = evaluate(result); + synchronized(this) + { + _lastValue = result; + } + return result; + } + + protected Object evaluate(Object result) + { + while (result instanceof PropertyValue) + { + result = ((PropertyValue)result).getValue(); + } + return result; + } + + public abstract Object nextValue(); + + public synchronized Object getLastValue() + { + return _lastValue; + } + + @Override + public String toString() + { + return "GeneratedPropertyValue [value=" + getLastValue() + ", @def=" + getDefinition() + "]"; + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/GeneratedPropertyValue.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/GeneratedPropertyValue.java new file mode 100644 index 0000000000..39c093fac5 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/GeneratedPropertyValue.java @@ -0,0 +1,27 @@ +/* + * 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.disttest.client.property; + +/** + * Provides operations to generate message property values. + */ +public interface GeneratedPropertyValue extends PropertyValue +{ + public String getDefinition(); +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/ListPropertyValue.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/ListPropertyValue.java new file mode 100644 index 0000000000..4444351976 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/ListPropertyValue.java @@ -0,0 +1,122 @@ +/* + * 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.disttest.client.property; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Provides property values from the underlining list of items. + */ +public class ListPropertyValue extends GeneratedPropertySupport +{ + public static final String DEF_VALUE = "list"; + private List<PropertyValue> _items; + private boolean _cyclic; + private int _currentIndex; + + public ListPropertyValue() + { + super(); + _cyclic = true; + _currentIndex = 0; + _items = new ArrayList<PropertyValue>(); + } + + public synchronized void setItems(List<PropertyValue> items) + { + _items = new ArrayList<PropertyValue>(items); + } + + public synchronized List<PropertyValue> getItems() + { + return Collections.unmodifiableList(_items); + } + + public synchronized void setCyclic(boolean cyclic) + { + _cyclic = cyclic; + } + + public synchronized boolean isCyclic() + { + return _cyclic; + } + + @Override + public synchronized Object nextValue() + { + if (_currentIndex >= _items.size()) + { + if (_cyclic) + { + _currentIndex = 0; + } + else + { + _currentIndex = _items.size() -1; + } + } + Object nextValue = _items.get(_currentIndex); + _currentIndex++; + return nextValue; + } + + @Override + public String getDefinition() + { + return DEF_VALUE; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + _currentIndex; + result = prime * result + (_cyclic ? 1231 : 1237); + result = prime * result + ((_items == null) ? 0 : _items.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null || !(obj instanceof ListPropertyValue)) + { + return false; + } + ListPropertyValue other = (ListPropertyValue) obj; + if (_cyclic != other._cyclic) + { + return false; + } + if (_items == null && other._items != null) + { + return false; + } + return _items.equals(other._items); + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/NumericGeneratedPropertySupport.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/NumericGeneratedPropertySupport.java new file mode 100644 index 0000000000..e0ae137c35 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/NumericGeneratedPropertySupport.java @@ -0,0 +1,179 @@ +/* + * 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.disttest.client.property; + +import java.util.Arrays; + +/** + * Provides support for numeric generators with lower and upper boundaries. + */ +public abstract class NumericGeneratedPropertySupport extends GeneratedPropertySupport +{ + public static final Class<?>[] SUPPORTED_TYPES = { double.class, float.class, int.class, long.class, short.class, + byte.class }; + + private String _type; + private double _upper; + private double _lower; + + + public NumericGeneratedPropertySupport() + { + super(); + _type = SUPPORTED_TYPES[0].getName(); + _upper = Double.MAX_VALUE; + _lower = 0.0; + } + + public synchronized String getType() + { + return _type; + } + + public synchronized double getUpper() + { + return _upper; + } + + public synchronized double getLower() + { + return _lower; + } + + public synchronized void setUpper(double upper) + { + _upper = upper; + } + + public synchronized void setLower(double lower) + { + _lower = lower; + } + + public synchronized void setType(String type) + { + _type = toClass(type).getName(); + } + + protected Class<?> toClass(String type) + { + Class<?> t = null; + for (int i = 0; i < SUPPORTED_TYPES.length; i++) + { + if (SUPPORTED_TYPES[i].getName().equals(type)) + { + t = SUPPORTED_TYPES[i]; + break; + } + } + if (t == null) + { + throw new IllegalArgumentException("Type " + type + " is not supported: " + + Arrays.toString(SUPPORTED_TYPES)); + } + return t; + } + + @Override + public Object nextValue() + { + double result = nextDouble(); + return doubleToNumber(result, toClass(_type)); + } + + protected Number doubleToNumber(double value, Class<?> targetType) + { + Number result = null; + if (targetType == double.class) + { + result = new Double(value); + } + else if (targetType == float.class) + { + result = new Float(value); + } + else if (targetType == int.class) + { + result = new Integer((int) value); + } + else if (targetType == long.class) + { + result = new Long((long) value); + } + else if (targetType == short.class) + { + result = new Short((short) value); + } + else if (targetType == byte.class) + { + result = new Byte((byte) value); + } + else + { + throw new IllegalArgumentException("Type " + targetType + " is not supported: " + + Arrays.toString(SUPPORTED_TYPES)); + } + return result; + } + + protected abstract double nextDouble(); + + @Override + public int hashCode() + { + final int prime = 31; + int result = super.hashCode(); + long temp; + temp = Double.doubleToLongBits(_lower); + result = prime * result + (int) (temp ^ (temp >>> 32)); + result = prime * result + ((_type == null) ? 0 : _type.hashCode()); + temp = Double.doubleToLongBits(_upper); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null || !(obj instanceof NumericGeneratedPropertySupport)) + { + return false; + } + NumericGeneratedPropertySupport other = (NumericGeneratedPropertySupport) obj; + if (Double.doubleToLongBits(_lower) != Double.doubleToLongBits(other._lower) + || Double.doubleToLongBits(_upper) != Double.doubleToLongBits(other._upper)) + { + return false; + } + if (_type == null && other._type != null) + { + return false; + } + else if (!_type.equals(other._type)) + { + return false; + } + return true; + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/PropertyValue.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/PropertyValue.java new file mode 100644 index 0000000000..97adc0cee1 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/PropertyValue.java @@ -0,0 +1,27 @@ +/* + * 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.disttest.client.property; + +/** + * Provides operations to get a message property value. + */ +public interface PropertyValue +{ + public Object getValue(); +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/PropertyValueFactory.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/PropertyValueFactory.java new file mode 100644 index 0000000000..fa44b2da1e --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/PropertyValueFactory.java @@ -0,0 +1,46 @@ +/* + * 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.disttest.client.property; + +import org.apache.commons.lang.StringUtils; +import org.apache.qpid.disttest.DistributedTestException; + +/** + * Creates property value instances using given alias (type) value. + */ +public class PropertyValueFactory +{ + public PropertyValue createPropertyValue(String type) + { + try + { + return (PropertyValue)getPropertyValueClass(type).newInstance(); + } + catch(Exception e) + { + throw new DistributedTestException("Unable to create a generator for a type:" + type, e); + } + } + + public Class<?> getPropertyValueClass(String type) throws ClassNotFoundException + { + String className = "org.apache.qpid.disttest.client.property." + StringUtils.capitalize(type) + "PropertyValue"; + return Class.forName(className); + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/RandomPropertyValue.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/RandomPropertyValue.java new file mode 100644 index 0000000000..4f44a4bca8 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/RandomPropertyValue.java @@ -0,0 +1,47 @@ +/* + * 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.disttest.client.property; + +/** + * Generates random property values in a given lower and upper boundaries. + */ +public class RandomPropertyValue extends NumericGeneratedPropertySupport +{ + public static final String DEF_VALUE = "random"; + + public RandomPropertyValue() + { + super(); + } + + @Override + protected double nextDouble() + { + double lower = getLower(); + double upper = getUpper(); + return lower + Math.random() * (upper - lower); + } + + @Override + public String getDefinition() + { + return DEF_VALUE; + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/RangePropertyValue.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/RangePropertyValue.java new file mode 100644 index 0000000000..3aca4d4bca --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/RangePropertyValue.java @@ -0,0 +1,129 @@ +/* + * 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.disttest.client.property; + +/** + * Generates property values from a range with given lower and upper boundaries. + */ +public class RangePropertyValue extends NumericGeneratedPropertySupport +{ + public static final String DEF_VALUE = "range"; + private double _step; + private double _currentValue; + private boolean _cyclic; + + public RangePropertyValue() + { + super(); + _cyclic = true; + _currentValue = 0.0; + _step = 1.0; + } + + public synchronized double getStep() + { + return _step; + } + + public synchronized boolean isCyclic() + { + return _cyclic; + } + + public synchronized void setCyclic(boolean cyclic) + { + _cyclic = cyclic; + } + + public synchronized void setStep(double step) + { + _step = step; + } + + @Override + public synchronized double nextDouble() + { + double result = 0.0; + double lower = getLower(); + double upper = getUpper(); + if (_currentValue < lower) + { + _currentValue = lower; + } + else if (_currentValue > upper) + { + if (_cyclic) + { + _currentValue = lower; + } + else + { + _currentValue = upper; + } + } + result = _currentValue; + _currentValue += _step; + return result; + } + + @Override + public String getDefinition() + { + return DEF_VALUE; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = super.hashCode(); + long temp; + temp = Double.doubleToLongBits(_currentValue); + result = prime * result + (int) (temp ^ (temp >>> 32)); + result = prime * result + (_cyclic ? 1231 : 1237); + temp = Double.doubleToLongBits(_step); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (!(obj instanceof RangePropertyValue)) + { + return false; + } + if (!super.equals(obj)) + { + return false; + } + RangePropertyValue other = (RangePropertyValue) obj; + if (Double.doubleToLongBits(_currentValue) != Double.doubleToLongBits(other._currentValue) + || Double.doubleToLongBits(_step) != Double.doubleToLongBits(other._step) || _cyclic != other._cyclic) + { + return false; + } + + return true; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/SimplePropertyValue.java b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/SimplePropertyValue.java new file mode 100644 index 0000000000..9141e68656 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/client/property/SimplePropertyValue.java @@ -0,0 +1,79 @@ +/* + * 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.disttest.client.property; + +/** + * Simple property value holder for a constant properties. + */ +public class SimplePropertyValue implements PropertyValue +{ + private Object _value; + + public SimplePropertyValue() + { + super(); + } + + public SimplePropertyValue(Object value) + { + super(); + this._value = value; + } + + @Override + public Object getValue() + { + return _value; + } + + @Override + public String toString() + { + return "SimplePropertyValue [value=" + _value + "]"; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((_value == null) ? 0 : _value.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null || getClass() != obj.getClass()) + { + return false; + } + SimplePropertyValue other = (SimplePropertyValue) obj; + if (_value == null && other._value != null) + { + return false; + } + return _value.equals(other._value); + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/ClientRegistry.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/ClientRegistry.java new file mode 100644 index 0000000000..eaccb54f0e --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/ClientRegistry.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.disttest.controller; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentSkipListSet; + +import org.apache.qpid.disttest.DistributedTestException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ClientRegistry +{ + private static final Logger LOGGER = LoggerFactory.getLogger(ClientRegistry.class); + + private final Set<String> _registeredClientNames = new ConcurrentSkipListSet<String>(); + + private final Object _lock = new Object(); + + public void registerClient(String clientName) + { + final boolean alreadyContainsClient = !_registeredClientNames.add(clientName); + if (alreadyContainsClient) + { + throw new DistributedTestException("Duplicate client name " + clientName); + } + + notifyAllWaiters(); + + if (LOGGER.isInfoEnabled()) + { + LOGGER.info("Client registered: " + clientName); + } + } + + + public Collection<String> getClients() + { + return Collections.unmodifiableSet(_registeredClientNames); + } + + public int awaitClients(int numberOfClientsToAwait, long timeout) + { + final long endTime = System.currentTimeMillis() + timeout; + + int numberOfClientsAbsent = numberOfClientsToAwait - _registeredClientNames.size(); + long remainingTimeout = endTime - System.currentTimeMillis(); + + while(numberOfClientsAbsent > 0 && remainingTimeout > 0) + { + synchronized (_lock) + { + try + { + _lock.wait(remainingTimeout); + } + catch (InterruptedException e) + { + Thread.currentThread().interrupt(); + } + } + + numberOfClientsAbsent = numberOfClientsToAwait - _registeredClientNames.size(); + remainingTimeout = endTime - System.currentTimeMillis(); + } + + return numberOfClientsAbsent < 0 ? 0 : numberOfClientsAbsent; + } + + private void notifyAllWaiters() + { + synchronized (_lock) + { + _lock.notifyAll(); + } + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/CommandForClient.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/CommandForClient.java new file mode 100644 index 0000000000..6c0c253807 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/CommandForClient.java @@ -0,0 +1,46 @@ +/* + * 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.disttest.controller; + +import org.apache.qpid.disttest.message.Command; + +public class CommandForClient +{ + private String _clientName; + private Command _command; + + public CommandForClient(String clientName, Command command) + { + _clientName = clientName; + _command = command; + } + + public String getClientName() + { + return _clientName; + } + + public Command getCommand() + { + return _command; + } + + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/CommandListener.java index a9984eb09a..e2f40bebe8 100644 --- a/java/perftests/src/main/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/CommandListener.java @@ -1,5 +1,4 @@ /* - * * 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 @@ -7,9 +6,9 @@ * 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 @@ -18,12 +17,15 @@ * under the License. * */ -package org.apache.qpid.config; +package org.apache.qpid.disttest.controller; -import javax.jms.ConnectionFactory; -import javax.jms.JMSException; +import org.apache.qpid.disttest.message.Command; -public interface ConnectionFactoryInitialiser +public interface CommandListener { - public ConnectionFactory getFactory(ConnectorConfig config) throws JMSException; + + public abstract void processCommand(Command command); + + public boolean supports(Command command); + } diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/Controller.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/Controller.java new file mode 100644 index 0000000000..7c935065f0 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/Controller.java @@ -0,0 +1,228 @@ +/* + * 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.disttest.controller; + +import java.util.Collection; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.qpid.disttest.DistributedTestException; +import org.apache.qpid.disttest.controller.config.Config; +import org.apache.qpid.disttest.controller.config.TestInstance; +import org.apache.qpid.disttest.jms.ControllerJmsDelegate; +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.CommandType; +import org.apache.qpid.disttest.message.RegisterClientCommand; +import org.apache.qpid.disttest.message.Response; +import org.apache.qpid.disttest.message.StopClientCommand; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Controller +{ + private static final Logger LOGGER = LoggerFactory.getLogger(Controller.class); + + private final long _registrationTimeout; + private final long _commandResponseTimeout; + + private final ControllerJmsDelegate _jmsDelegate; + + private volatile CountDownLatch _stopClientsResponseLatch = null; + + private Config _config; + private TestRunnerFactory _testRunnerFactory; + private ClientRegistry _clientRegistry; + + private long _testResultTimeout = TestRunner.WAIT_FOREVER; + + public Controller(final ControllerJmsDelegate jmsDelegate, long registrationTimeout, long commandResponseTimeout) + { + _jmsDelegate = jmsDelegate; + _registrationTimeout = registrationTimeout; + _commandResponseTimeout = commandResponseTimeout; + _testRunnerFactory = new TestRunnerFactory(); + _clientRegistry = new ClientRegistry(); + + _jmsDelegate.addCommandListener(new RegisterClientCommandListener()); + _jmsDelegate.addCommandListener(new StopClientResponseListener()); + _jmsDelegate.start(); + } + + public void setConfig(Config config) + { + _config = config; + validateConfiguration(); + } + + public void awaitClientRegistrations() + { + LOGGER.info("Awaiting client registrations"); + + final int numberOfAbsentClients = _clientRegistry.awaitClients(_config.getTotalNumberOfClients(), _registrationTimeout); + if (numberOfAbsentClients > 0) + { + String formattedMessage = String.format("Timed out waiting for registrations. Expecting %d more registrations", numberOfAbsentClients); + throw new DistributedTestException(formattedMessage); + } + + } + + private void validateConfiguration() + { + if (_config == null || _config.getTotalNumberOfClients() == 0) + { + throw new DistributedTestException("No controller config or no clients specified in test config"); + } + } + + private void awaitLatch(CountDownLatch latch, long timeout, String messageWithOneDecimalPlaceholder) + { + try + { + final boolean countedDownOK = latch.await(timeout, TimeUnit.MILLISECONDS); + if (!countedDownOK) + { + final long latchCount = latch.getCount(); + String formattedMessage = String.format(messageWithOneDecimalPlaceholder, latchCount); + throw new DistributedTestException(formattedMessage); + } + } + catch (final InterruptedException e) + { + Thread.currentThread().interrupt(); + } + } + + public void registerClient(final RegisterClientCommand registrationCommand) + { + final String clientName = registrationCommand.getClientName(); + + _jmsDelegate.registerClient(registrationCommand); + _clientRegistry.registerClient(clientName); + } + + void processStopClientResponse(final Response response) + { + // TODO clientRegistry should expose a deregisterClient + _stopClientsResponseLatch.countDown(); + if (response.hasError()) + { + LOGGER.error("Client " + response.getRegisteredClientName() + " reported exception in response to command : " + + response.getErrorMessage()); + } + } + + public void stopAllRegisteredClients() + { + Collection<String> registeredClients = _clientRegistry.getClients(); + + LOGGER.info("Stopping all clients"); + _stopClientsResponseLatch = new CountDownLatch(registeredClients.size()); + Command command = new StopClientCommand(); + for (final String clientName : registeredClients) + { + _jmsDelegate.sendCommandToClient(clientName, command); + } + + awaitLatch(_stopClientsResponseLatch, _commandResponseTimeout, "Timed out waiting for stop command responses. Expecting %d more responses."); + + LOGGER.info("Stopped all clients"); + } + + + public ResultsForAllTests runAllTests() + { + LOGGER.info("Running all tests"); + + ResultsForAllTests resultsForAllTests = new ResultsForAllTests(); + + for (TestInstance testInstance : _config.getTests()) + { + + ParticipatingClients participatingClients = new ParticipatingClients(_clientRegistry, testInstance.getClientNames()); + + LOGGER.info("Running test " + testInstance + ". Participating clients: " + participatingClients.getRegisteredNames()); + TestRunner runner = _testRunnerFactory.createTestRunner(participatingClients, + testInstance, + _jmsDelegate, + _commandResponseTimeout, + _testResultTimeout); + + TestResult testResult = runner.run(); + LOGGER.info("Finished test " + testInstance); + + resultsForAllTests.add(testResult); + } + + return resultsForAllTests; + } + + private final class StopClientResponseListener implements CommandListener + { + @Override + public boolean supports(Command command) + { + return command.getType() == CommandType.RESPONSE && ((Response)command).getInReplyToCommandType() == CommandType.STOP_CLIENT; + } + + @Override + public void processCommand(Command command) + { + processStopClientResponse((Response)command); + } + } + + private final class RegisterClientCommandListener implements + CommandListener + { + @Override + public boolean supports(Command command) + { + return command.getType() == CommandType.REGISTER_CLIENT; + } + + @Override + public void processCommand(Command command) + { + registerClient((RegisterClientCommand)command); + } + } + + public void setTestResultTimeout(final long testResultTimeout) + { + _testResultTimeout = testResultTimeout; + + } + + void setClientRegistry(ClientRegistry clientRegistry) + { + _clientRegistry = clientRegistry; + + } + + void setTestRunnerFactory(TestRunnerFactory factory) + { + if (factory == null) + { + throw new IllegalArgumentException("TestRunnerFactory cannot be null!"); + } + _testRunnerFactory = factory; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/ParticipatingClients.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/ParticipatingClients.java new file mode 100644 index 0000000000..077d628697 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/ParticipatingClients.java @@ -0,0 +1,94 @@ +/* + * 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.disttest.controller; + +import java.util.Collection; +import java.util.List; +import java.util.TreeSet; + +import org.apache.commons.collections.BidiMap; +import org.apache.commons.collections.bidimap.DualHashBidiMap; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; + +public class ParticipatingClients +{ + private final BidiMap _configuredToRegisteredNameMap; + + public ParticipatingClients(ClientRegistry clientRegistry, List<String> configuredClientNamesForTest) + { + _configuredToRegisteredNameMap = mapConfiguredToRegisteredClientNames(configuredClientNamesForTest, clientRegistry); + } + + public String getRegisteredNameFromConfiguredName(String clientConfiguredName) + { + String registeredClientName = (String) _configuredToRegisteredNameMap.get(clientConfiguredName); + if (registeredClientName == null) + { + throw new IllegalArgumentException("Unrecognised client configured name " + clientConfiguredName + + " Mapping is " + _configuredToRegisteredNameMap); + } + return registeredClientName; + } + + public String getConfiguredNameFromRegisteredName(String registeredClientName) + { + String clientConfiguredName = (String) _configuredToRegisteredNameMap.getKey(registeredClientName); + if (clientConfiguredName == null) + { + throw new IllegalArgumentException("Unrecognised client registered name " + registeredClientName + + " Mapping is " + _configuredToRegisteredNameMap); + } + + return clientConfiguredName; + } + + private BidiMap mapConfiguredToRegisteredClientNames(List<String> configuredClientNamesForTest, ClientRegistry clientRegistry) + { + BidiMap configuredToRegisteredNameMap = new DualHashBidiMap(); + + TreeSet<String> registeredClients = new TreeSet<String>(clientRegistry.getClients()); + for (String configuredClientName : configuredClientNamesForTest) + { + String allocatedClientName = registeredClients.pollFirst(); + if (allocatedClientName == null) + { + throw new IllegalArgumentException("Too few clients in registry " + clientRegistry + " configured clients " + configuredClientNamesForTest); + } + configuredToRegisteredNameMap.put(configuredClientName, allocatedClientName); + } + + return configuredToRegisteredNameMap; + } + + @SuppressWarnings("unchecked") + public Collection<String> getRegisteredNames() + { + return _configuredToRegisteredNameMap.values(); + } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append("configuredToRegisteredNameMap", _configuredToRegisteredNameMap).toString(); + } + + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/config/Connector.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/ResultsForAllTests.java index 81541a3260..6c5ff3450c 100644 --- a/java/perftests/src/main/java/org/apache/qpid/config/Connector.java +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/ResultsForAllTests.java @@ -1,5 +1,4 @@ /* - * * 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 @@ -7,37 +6,44 @@ * 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.config; +package org.apache.qpid.disttest.controller; + +import java.util.ArrayList; +import java.util.List; -import javax.jms.Connection; -import javax.jms.ConnectionFactory; +import org.apache.qpid.disttest.results.aggregation.ITestResult; -public class Connector +public class ResultsForAllTests { - public Connection createConnection(ConnectorConfig config) throws Exception + private List<ITestResult> _results = new ArrayList<ITestResult>(); + private boolean _hasErrors; + + public List<ITestResult> getTestResults() { - return getConnectionFactory(config).createConnection(); + return _results; } - ConnectionFactory getConnectionFactory(ConnectorConfig config) throws Exception + public void add(ITestResult testResult) { - String factory = config.getFactory(); - if(factory == null) + _results.add(testResult); + if(testResult.hasErrors()) { - factory = AMQConnectionFactoryInitialiser.class.getName(); + _hasErrors = true; } - System.out.println("Using " + factory); - return ((ConnectionFactoryInitialiser) Class.forName(factory).newInstance()).getFactory(config); + } + + public boolean hasErrors() + { + return _hasErrors; } } diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/TestResult.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/TestResult.java new file mode 100644 index 0000000000..756c641532 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/TestResult.java @@ -0,0 +1,70 @@ +/* + * 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.disttest.controller; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.disttest.results.aggregation.ITestResult; + +public class TestResult implements ITestResult +{ + private final SortedSet<ParticipantResult> _participantResults = Collections.synchronizedSortedSet( + new TreeSet<ParticipantResult>(ParticipantResult.PARTICIPANT_NAME_COMPARATOR)); + + private boolean _hasErrors; + private String _name; + + public TestResult(String name) + { + _name = name; + } + + @Override + public List<ParticipantResult> getParticipantResults() + { + List<ParticipantResult> list = new ArrayList<ParticipantResult>(_participantResults); + return Collections.unmodifiableList(list); + } + + public void addParticipantResult(ParticipantResult participantResult) + { + _participantResults.add(participantResult); + if(participantResult.hasError()) + { + _hasErrors = true; + } + } + + @Override + public boolean hasErrors() + { + return _hasErrors; + } + + @Override + public String getName() + { + return _name; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/TestRunner.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/TestRunner.java new file mode 100644 index 0000000000..30595269b3 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/TestRunner.java @@ -0,0 +1,342 @@ +/* + * 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.disttest.controller; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.qpid.disttest.DistributedTestException; +import org.apache.qpid.disttest.controller.config.QueueConfig; +import org.apache.qpid.disttest.controller.config.TestInstance; +import org.apache.qpid.disttest.jms.ControllerJmsDelegate; +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.CommandType; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.disttest.message.Response; +import org.apache.qpid.disttest.message.StartTestCommand; +import org.apache.qpid.disttest.message.TearDownTestCommand; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestRunner +{ + private static final Logger LOGGER = LoggerFactory.getLogger(TestRunner.class); + + private static final long PARTICIPANT_RESULTS_LOG_INTERVAL = 60000; + public static final long WAIT_FOREVER = -1; + + private final long _commandResponseTimeout; + + private final Set<CommandType> _setOfResponsesToExpect = Collections.synchronizedSet(new HashSet<CommandType>()); + + private final ParticipatingClients _participatingClients; + + private final TestInstance _testInstance; + private ControllerJmsDelegate _jmsDelegate; + + private volatile CountDownLatch _commandResponseLatch = null; + private final CountDownLatch _testResultsLatch; + private final TestResult _testResult; + + /** Length of time to await test results or {@value #WAIT_FOREVER} */ + private final long _testResultTimeout; + + private Thread _removeQueuesShutdownHook = new Thread() + { + @Override + public void run() + { + LOGGER.info("Shutdown intercepted: deleting test queues"); + try + { + deleteQueues(); + } + catch (Throwable t) + { + LOGGER.error("Failed to delete test queues during shutdown", t); + } + } + }; + + public TestRunner(ParticipatingClients participatingClients, TestInstance testInstance, ControllerJmsDelegate jmsDelegate, long commandResponseTimeout, long testResultTimeout) + { + _participatingClients = participatingClients; + _testInstance = testInstance; + _jmsDelegate = jmsDelegate; + _commandResponseTimeout = commandResponseTimeout; + _testResultsLatch = new CountDownLatch(testInstance.getTotalNumberOfParticipants()); + _testResultTimeout = testResultTimeout; + _testResult = new TestResult(testInstance.getName()); + } + + public TestResult run() + { + final ParticipantResultListener participantResultListener = new ParticipantResultListener(); + TestCommandResponseListener testCommandResponseListener = new TestCommandResponseListener(); + + try + { + _jmsDelegate.addCommandListener(testCommandResponseListener); + _jmsDelegate.addCommandListener(participantResultListener); + + runParts(); + + return _testResult; + } + finally + { + _jmsDelegate.removeCommandListener(participantResultListener); + _jmsDelegate.removeCommandListener(testCommandResponseListener); + } + } + + private void runParts() + { + boolean queuesCreated = false; + + try + { + createQueues(); + queuesCreated = true; + Runtime.getRuntime().addShutdownHook(_removeQueuesShutdownHook); + + sendTestSetupCommands(); + awaitCommandResponses(); + sendCommandToParticipatingClients(new StartTestCommand()); + awaitCommandResponses(); + + awaitTestResults(); + + sendCommandToParticipatingClients(new TearDownTestCommand()); + awaitCommandResponses(); + } + finally + { + + if (queuesCreated) + { + deleteQueues(); + } + + Runtime.getRuntime().removeShutdownHook(_removeQueuesShutdownHook); + + } + } + + void createQueues() + { + List<QueueConfig> queues = _testInstance.getQueues(); + if (!queues.isEmpty()) + { + _jmsDelegate.createQueues(queues); + } + } + + void sendTestSetupCommands() + { + List<CommandForClient> commandsForAllClients = _testInstance.createCommands(); + final int numberOfCommandsToSend = commandsForAllClients.size(); + _commandResponseLatch = new CountDownLatch(numberOfCommandsToSend); + + LOGGER.debug("About to send {} command(s)", numberOfCommandsToSend); + + for (CommandForClient commandForClient : commandsForAllClients) + { + String configuredClientName = commandForClient.getClientName(); + String registeredClientName = _participatingClients.getRegisteredNameFromConfiguredName(configuredClientName); + + Command command = commandForClient.getCommand(); + + LOGGER.debug("Sending command : {} ", command); + + sendCommandInternal(registeredClientName, command); + } + } + + void awaitCommandResponses() + { + awaitLatch(_commandResponseLatch, _commandResponseTimeout, "Timed out waiting for command responses. Expecting %d more responses."); + } + + + void processCommandResponse(final Response response) + { + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("Received response for command " + response); + } + + _commandResponseLatch.countDown(); + checkForResponseError(response); + } + + + void awaitTestResults() + { + long timeout = _testResultTimeout; + DistributedTestException lastException = null; + + boolean waitForever = _testResultTimeout == WAIT_FOREVER; + final long interval = waitForever ? PARTICIPANT_RESULTS_LOG_INTERVAL : Math.min(PARTICIPANT_RESULTS_LOG_INTERVAL, _testResultTimeout); + + while(_testResultsLatch.getCount() > 0 && (waitForever || timeout > 0)) + { + try + { + awaitLatch(_testResultsLatch, interval, "Waiting for participant results... Expecting %d more responses."); + } + catch (DistributedTestException e) + { + lastException = e; + LOGGER.info(e.getMessage()); + } + + if (!waitForever) + { + timeout =- interval; + } + } + + if (_testResultsLatch.getCount() > 0) + { + throw lastException; + } + } + + void deleteQueues() + { + List<QueueConfig> queues = _testInstance.getQueues(); + if (!queues.isEmpty()) + { + _jmsDelegate.deleteQueues(queues); + } + } + + void sendCommandToParticipatingClients(final Command command) + { + Collection<String> participatingRegisteredClients = _participatingClients.getRegisteredNames(); + final int numberOfClients = participatingRegisteredClients.size(); + _commandResponseLatch = new CountDownLatch(numberOfClients); + + LOGGER.debug("About to send command {} to {} clients", command, numberOfClients); + + for (final String clientName : participatingRegisteredClients) + { + LOGGER.debug("Sending command : {} ", command); + sendCommandInternal(clientName, command); + } + } + + public void processParticipantResult(ParticipantResult result) + { + setOriginalTestDetailsOn(result); + + _testResult.addParticipantResult(result); + LOGGER.info("Received result " + result); + + _testResultsLatch.countDown(); + checkForResponseError(result); + } + + private void setOriginalTestDetailsOn(ParticipantResult result) + { + // Client knows neither the configured client name nor test name + String registeredClientName = result.getRegisteredClientName(); + String configuredClient = _participatingClients.getConfiguredNameFromRegisteredName(registeredClientName); + + result.setConfiguredClientName(configuredClient); + result.setTestName(_testInstance.getName()); + result.setIterationNumber(_testInstance.getIterationNumber()); + } + + private void sendCommandInternal(String registeredClientName, Command command) + { + _setOfResponsesToExpect.add(command.getType()); + _jmsDelegate.sendCommandToClient(registeredClientName, command); + } + + private void awaitLatch(CountDownLatch latch, long timeout, String messageWithOneDecimalPlaceholder) + { + try + { + final boolean countedDownOK = latch.await(timeout, TimeUnit.MILLISECONDS); + if (!countedDownOK) + { + final long latchCount = latch.getCount(); + String formattedMessage = String.format(messageWithOneDecimalPlaceholder, latchCount); + throw new DistributedTestException(formattedMessage); + } + } + catch (final InterruptedException e) + { + Thread.currentThread().interrupt(); + } + } + + private void checkForResponseError(final Response response) + { + if (response.hasError()) + { + LOGGER.error("Client " + response.getRegisteredClientName() + " reported error " + response); + } + } + + final class ParticipantResultListener implements CommandListener + { + @Override + public boolean supports(Command command) + { + return command instanceof ParticipantResult; + } + + @Override + public void processCommand(Command command) + { + processParticipantResult((ParticipantResult) command); + + } + } + + final class TestCommandResponseListener implements CommandListener + { + @Override + public void processCommand(Command command) + { + processCommandResponse((Response)command); + } + + @Override + public boolean supports(Command command) + { + CommandType type = command.getType(); + if (type == CommandType.RESPONSE) + { + Response response = (Response)command; + return _setOfResponsesToExpect.contains(response.getInReplyToCommandType()); + } + return false; + } + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/TestRunnerFactory.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/TestRunnerFactory.java new file mode 100644 index 0000000000..bf0e5afb9c --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/TestRunnerFactory.java @@ -0,0 +1,31 @@ +/* + * 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.disttest.controller; + +import org.apache.qpid.disttest.controller.config.TestInstance; +import org.apache.qpid.disttest.jms.ControllerJmsDelegate; + +public class TestRunnerFactory +{ + public TestRunner createTestRunner(ParticipatingClients participatingClients, TestInstance testInstance, ControllerJmsDelegate jmsDelegate, long commandResponseTimeout, long testResultTimeout) + { + return new TestRunner(participatingClients, testInstance, jmsDelegate, commandResponseTimeout, testResultTimeout); + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ClientConfig.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ClientConfig.java new file mode 100644 index 0000000000..4353a85cd3 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ClientConfig.java @@ -0,0 +1,113 @@ +/* + * 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.disttest.controller.config; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.qpid.disttest.controller.CommandForClient; +import org.apache.qpid.disttest.message.Command; + +public class ClientConfig +{ + /* + * TODO add this field when repeating groups of clients need to be used. Talk to Phil and Keith! + * private int _instances; + */ + + private List<ConnectionConfig> _connections; + private List<MessageProviderConfig> _messageProviders; + private String _name; + + public ClientConfig() + { + _name = null; + _connections = Collections.emptyList(); + _messageProviders = Collections.emptyList(); + } + + public ClientConfig(String name, ConnectionConfig... connections) + { + this(name, Arrays.asList(connections), null); + } + + public ClientConfig(String name, List<ConnectionConfig> connections, List<MessageProviderConfig> messageProviders) + { + _name = name; + _connections = connections; + if (messageProviders == null) + { + _messageProviders = Collections.emptyList(); + } + else + { + _messageProviders = messageProviders; + } + } + + public String getName() + { + return _name; + } + + public List<ConnectionConfig> getConnections() + { + return Collections.unmodifiableList(_connections); + } + + public List<CommandForClient> createCommands() + { + List<CommandForClient> commandsForClient = new ArrayList<CommandForClient>(); + + for (MessageProviderConfig messageProvider : _messageProviders) + { + Command command = messageProvider.createCommand(); + commandsForClient.add(new CommandForClient(_name, command)); + } + for (ConnectionConfig connection : _connections) + { + List<Command> commands = connection.createCommands(); + for (Command command : commands) + { + commandsForClient.add(new CommandForClient(_name, command)); + } + } + return commandsForClient; + } + + public int getTotalNumberOfParticipants() + { + int numOfParticipants = 0; + for (ConnectionConfig connection : _connections) + { + numOfParticipants = numOfParticipants + connection.getTotalNumberOfParticipants(); + } + return numOfParticipants; + } + + public List<MessageProviderConfig> getMessageProviders() + { + return Collections.unmodifiableList(_messageProviders); + } + + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/Config.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/Config.java new file mode 100644 index 0000000000..fe56137276 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/Config.java @@ -0,0 +1,81 @@ +/* + * 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.disttest.controller.config; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class Config +{ + private List<TestConfig> _tests; + + public Config() + { + super(); + _tests = Collections.emptyList(); + } + + public Config(TestConfig... tests) + { + _tests = Arrays.asList(tests); + } + + public List<TestInstance> getTests() + { + List<TestInstance> testInstances = new ArrayList<TestInstance>(); + for (TestConfig testConfig : _tests) + { + int iterationNumber = 0; + + List<IterationValue> iterationValues = testConfig.getIterationValues(); + if(iterationValues.isEmpty()) + { + testInstances.add(new TestInstance(testConfig)); + } + else + { + for (IterationValue iterationValue : iterationValues) + { + testInstances.add(new TestInstance(testConfig, iterationNumber, iterationValue)); + iterationNumber++; + } + } + } + + return Collections.unmodifiableList(testInstances); + } + + List<TestConfig> getTestConfigs() + { + return Collections.unmodifiableList(_tests); + } + + public int getTotalNumberOfClients() + { + int numberOfClients = 0; + for (TestConfig testConfig : _tests) + { + numberOfClients = Math.max(testConfig.getTotalNumberOfClients(), numberOfClients); + } + return numberOfClients; + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ConfigReader.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ConfigReader.java new file mode 100644 index 0000000000..6288b42eac --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ConfigReader.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.disttest.controller.config; + +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.Reader; + +import org.apache.qpid.disttest.client.property.PropertyValue; +import org.apache.qpid.disttest.json.PropertyValueAdapter; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public class ConfigReader +{ + + public Config getConfigFromFile(String fileName) throws FileNotFoundException + { + FileReader reader = new FileReader(fileName); + + Config config = readConfig(reader); + return config; + } + + public Config readConfig(Reader reader) + { + Gson gson = new GsonBuilder() + .registerTypeAdapter(PropertyValue.class, new PropertyValueAdapter()) + .create(); + Config config = gson.fromJson(reader, Config.class); + return config; + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ConnectionConfig.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ConnectionConfig.java new file mode 100644 index 0000000000..e2cc31e21e --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ConnectionConfig.java @@ -0,0 +1,91 @@ +/* + * 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.disttest.controller.config; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.CreateConnectionCommand; + +public class ConnectionConfig +{ + private String _name; + private List<SessionConfig> _sessions; + private String _factory; + + // For Gson + public ConnectionConfig() + { + super(); + _sessions = Collections.emptyList(); + } + + public ConnectionConfig(String name, String factory, SessionConfig... sessions) + { + super(); + _name = name; + _factory = factory; + _sessions = Arrays.asList(sessions); + + } + + public List<SessionConfig> getSessions() + { + return Collections.unmodifiableList(_sessions); + } + + public String getName() + { + return _name; + } + + public List<Command> createCommands() + { + List<Command> commands = new ArrayList<Command>(); + commands.add(createCommand()); + for (SessionConfig sessionConfig : _sessions) + { + commands.addAll(sessionConfig.createCommands(_name)); + } + return commands; + } + + private CreateConnectionCommand createCommand() + { + CreateConnectionCommand command = new CreateConnectionCommand(); + command.setConnectionName(_name); + command.setConnectionFactoryName(_factory); + return command; + } + + public int getTotalNumberOfParticipants() + { + int numOfParticipants = 0; + + for (SessionConfig sessionConfig : _sessions) + { + numOfParticipants = numOfParticipants + sessionConfig.getTotalNumberOfParticipants(); + } + return numOfParticipants; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ConsumerConfig.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ConsumerConfig.java new file mode 100644 index 0000000000..ed47e02667 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ConsumerConfig.java @@ -0,0 +1,65 @@ +package org.apache.qpid.disttest.controller.config; + +import org.apache.qpid.disttest.message.CreateConsumerCommand; + +public class ConsumerConfig extends ParticipantConfig +{ + private boolean _isTopic; + private boolean _isDurableSubscription; + private boolean _isBrowsingSubscription; + private String _selector; + private boolean _noLocal; + private boolean _synchronous; + + // For Gson + public ConsumerConfig() + { + _isTopic = false; + _isDurableSubscription = false; + _isBrowsingSubscription = false; + _selector = null; + _noLocal = false; + _synchronous = true; + } + + public ConsumerConfig( + String consumerName, + String destinationName, + long numberOfMessages, + int batchSize, + long maximumDuration, + boolean isTopic, + boolean isDurableSubscription, + boolean isBrowsingSubscription, + String selector, + boolean noLocal, + boolean synchronous) + { + super(consumerName, destinationName, numberOfMessages, batchSize, maximumDuration); + + _isTopic = isTopic; + _isDurableSubscription = isDurableSubscription; + _isBrowsingSubscription = isBrowsingSubscription; + _selector = selector; + _noLocal = noLocal; + _synchronous = synchronous; + } + + public CreateConsumerCommand createCommand(String sessionName) + { + CreateConsumerCommand createConsumerCommand = new CreateConsumerCommand(); + + setParticipantProperties(createConsumerCommand); + + createConsumerCommand.setSessionName(sessionName); + createConsumerCommand.setTopic(_isTopic); + createConsumerCommand.setDurableSubscription(_isDurableSubscription); + createConsumerCommand.setBrowsingSubscription(_isBrowsingSubscription); + createConsumerCommand.setSelector(_selector); + createConsumerCommand.setNoLocal(_noLocal); + createConsumerCommand.setSynchronous(_synchronous); + + return createConsumerCommand; + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/IterationValue.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/IterationValue.java new file mode 100644 index 0000000000..ef953a5d07 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/IterationValue.java @@ -0,0 +1,86 @@ +/* + * 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.disttest.controller.config; + +import java.lang.reflect.InvocationTargetException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.beanutils.BeanUtilsBean; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.qpid.disttest.message.Command; + +public class IterationValue +{ + private final Map<String, String> _iterationPropertyValuesWithUnderscores; + + public IterationValue(Map<String, String> iterationMap) + { + _iterationPropertyValuesWithUnderscores = iterationMap; + } + + public IterationValue() + { + _iterationPropertyValuesWithUnderscores = Collections.emptyMap(); + } + + public Map<String, String> getIterationPropertyValuesWithUnderscores() + { + return _iterationPropertyValuesWithUnderscores; + } + + public void applyToCommand(Command command) + { + try + { + Map<String, String> withoutUnderscoresToMatchCommandPropertyNames = getIterationPropertyValuesWithoutUnderscores(); + BeanUtilsBean.getInstance().copyProperties(command, withoutUnderscoresToMatchCommandPropertyNames); + } + catch (IllegalAccessException e) + { + throw new RuntimeException("Couldn't copy properties from iteration " + this + " to " + command, e); + } + catch (InvocationTargetException e) + { + throw new RuntimeException("Couldn't copy properties from iteration " + this + " to " + command, e); + } + } + + private Map<String, String> getIterationPropertyValuesWithoutUnderscores() + { + Map<String, String> iterationPropertyValues = new HashMap<String, String>(); + for (String propertyNameWithUnderscore : _iterationPropertyValuesWithUnderscores.keySet()) + { + String propertyName = propertyNameWithUnderscore.replaceFirst("_", ""); + String propertyValue = _iterationPropertyValuesWithUnderscores.get(propertyNameWithUnderscore); + + iterationPropertyValues.put(propertyName, propertyValue); + } + return iterationPropertyValues; + } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("iterationMap", _iterationPropertyValuesWithUnderscores).toString(); + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/MessageProviderConfig.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/MessageProviderConfig.java new file mode 100644 index 0000000000..318ec7f045 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/MessageProviderConfig.java @@ -0,0 +1,60 @@ +/* + * 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.disttest.controller.config; + +import java.util.Map; + +import org.apache.qpid.disttest.client.property.PropertyValue; +import org.apache.qpid.disttest.message.CreateMessageProviderCommand; + +public class MessageProviderConfig +{ + private String _name; + private Map<String, PropertyValue> _messageProperties; + + public MessageProviderConfig() + { + super(); + } + + public MessageProviderConfig(String name, Map<String, PropertyValue> messageProperties) + { + super(); + _name = name; + _messageProperties = messageProperties; + } + + public String getName() + { + return _name; + } + + public Map<String, PropertyValue> getMessageProperties() + { + return _messageProperties; + } + + public CreateMessageProviderCommand createCommand() + { + CreateMessageProviderCommand command = new CreateMessageProviderCommand(); + command.setProviderName(_name); + command.setMessageProperties(_messageProperties); + return command; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ParticipantConfig.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ParticipantConfig.java new file mode 100644 index 0000000000..31037a3038 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ParticipantConfig.java @@ -0,0 +1,64 @@ +/* + * 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.disttest.controller.config; + +import org.apache.qpid.disttest.message.CreateParticpantCommand; + +public abstract class ParticipantConfig +{ + private String _destinationName; + private long _numberOfMessages; + private String _name; + private int _batchSize; + private long _maximumDuration; + + // For GSON + public ParticipantConfig() + { + _name = null; + _destinationName = null; + _numberOfMessages = 0; + _batchSize = 0; + _maximumDuration = 0; + } + + public ParticipantConfig( + String name, + String destinationName, + long numberOfMessages, + int batchSize, + long maximumDuration) + { + _name = name; + _destinationName = destinationName; + _numberOfMessages = numberOfMessages; + _batchSize = batchSize; + _maximumDuration = maximumDuration; + } + + protected void setParticipantProperties(CreateParticpantCommand createParticipantCommand) + { + createParticipantCommand.setParticipantName(_name); + createParticipantCommand.setDestinationName(_destinationName); + createParticipantCommand.setNumberOfMessages(_numberOfMessages); + createParticipantCommand.setBatchSize(_batchSize); + createParticipantCommand.setMaximumDuration(_maximumDuration); + } + +}
\ No newline at end of file diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ProducerConfig.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ProducerConfig.java new file mode 100644 index 0000000000..7806528a8c --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ProducerConfig.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.disttest.controller.config; + +import javax.jms.Message; + +import org.apache.qpid.disttest.message.CreateProducerCommand; + +public class ProducerConfig extends ParticipantConfig +{ + private int _deliveryMode; + private int _messageSize; + private int _priority; + private long _timeToLive; + private long _interval; + private long _startDelay; + private String _messageProviderName; + + // For Gson + public ProducerConfig() + { + _deliveryMode = Message.DEFAULT_DELIVERY_MODE; + _messageSize = 0; + _priority = Message.DEFAULT_PRIORITY; + _timeToLive = Message.DEFAULT_TIME_TO_LIVE; + _interval = 0; + _startDelay = 0; + _messageProviderName = null; + } + + public ProducerConfig( + String producerName, + String destinationName, + long numberOfMessages, + int batchSize, + long maximumDuration, + int deliveryMode, + int messageSize, + int priority, + long timeToLive, + long interval, + long startDelay, + String messageProviderName) + { + super(producerName, destinationName, numberOfMessages, batchSize, maximumDuration); + + _deliveryMode = deliveryMode; + _messageSize = messageSize; + _priority = priority; + _timeToLive = timeToLive; + _interval = interval; + _startDelay = startDelay; + _messageProviderName = messageProviderName; + } + + public CreateProducerCommand createCommand(String sessionName) + { + CreateProducerCommand command = new CreateProducerCommand(); + + setParticipantProperties(command); + + command.setSessionName(sessionName); + command.setDeliveryMode(_deliveryMode); + command.setMessageSize(_messageSize); + command.setPriority(_priority); + command.setTimeToLive(_timeToLive); + command.setInterval(_interval); + command.setStartDelay(_startDelay); + command.setMessageProviderName(_messageProviderName); + + return command; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/QueueConfig.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/QueueConfig.java new file mode 100644 index 0000000000..cffc2b7c50 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/QueueConfig.java @@ -0,0 +1,69 @@ +/* + * 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.disttest.controller.config; + +import java.util.Collections; +import java.util.Map; + +import org.apache.commons.lang.builder.ReflectionToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; + +public class QueueConfig +{ + private String _name; + private boolean _durable; + private Map<String, Object> _attributes; + + public QueueConfig() + { + super(); + _attributes = Collections.emptyMap(); + } + + public QueueConfig(String name, boolean durable, Map<String, Object> attributes) + { + super(); + this._name = name; + this._durable = durable; + this._attributes = attributes; + } + + public String getName() + { + return _name; + } + + // TODO x-qpid-capacity and x-qpid-flow-resume-capacity need to be typed as numeric but we currrently + // pass these as a string. + public Map<String, Object> getAttributes() + { + return _attributes; + } + + public boolean isDurable() + { + return _durable; + } + + @Override + public String toString() + { + return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/SessionConfig.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/SessionConfig.java new file mode 100644 index 0000000000..12372e5391 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/SessionConfig.java @@ -0,0 +1,114 @@ +/* + * 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.disttest.controller.config; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import javax.jms.Session; + +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.CreateSessionCommand; + +public class SessionConfig +{ + private static final List<ProducerConfig> EMPTY_PRODUCER_LIST = Collections.emptyList(); + private static final List<ConsumerConfig> EMPTY_CONSUMER_LIST = Collections.emptyList(); + + private int _acknowledgeMode; + private String _sessionName; + private List<ProducerConfig> _producers; + private List<ConsumerConfig> _consumers; + + // For Gson + public SessionConfig() + { + this(null, Session.SESSION_TRANSACTED, EMPTY_CONSUMER_LIST, EMPTY_PRODUCER_LIST); + } + + public SessionConfig(String sessionName, int acknowledgeMode, ProducerConfig...producers) + { + this(sessionName, acknowledgeMode, EMPTY_CONSUMER_LIST, Arrays.asList(producers)); + } + + public SessionConfig(String sessionName, int acknowledgeMode, ConsumerConfig... consumers) + { + this(sessionName, acknowledgeMode, Arrays.asList(consumers), EMPTY_PRODUCER_LIST); + } + + public SessionConfig(String sessionName, int acknowledgeMode, List<ConsumerConfig> consumers, List<ProducerConfig> producers) + { + _sessionName = sessionName; + _acknowledgeMode = acknowledgeMode; + _consumers = consumers; + _producers = producers; + } + + public int getAcknowledgeMode() + { + return _acknowledgeMode; + } + + public String getSessionName() + { + return _sessionName; + } + + public List<ProducerConfig> getProducers() + { + return Collections.unmodifiableList(_producers); + } + + public List<ConsumerConfig> getConsumers() + { + return Collections.unmodifiableList(_consumers); + } + + public List<Command> createCommands(String connectionName) + { + List<Command> commands = new ArrayList<Command>(); + commands.add(createCommand(connectionName)); + for (ProducerConfig producer : _producers) + { + commands.add(producer.createCommand(_sessionName)); + } + for (ConsumerConfig consumer : _consumers) + { + commands.add(consumer.createCommand(_sessionName)); + } + return commands; + } + + private CreateSessionCommand createCommand(String connectionName) + { + CreateSessionCommand command = new CreateSessionCommand(); + command.setAcknowledgeMode(_acknowledgeMode); + command.setConnectionName(connectionName); + command.setSessionName(_sessionName); + return command; + } + + public int getTotalNumberOfParticipants() + { + return _producers.size() + _consumers.size(); + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/TestConfig.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/TestConfig.java new file mode 100644 index 0000000000..2bb5f1b289 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/TestConfig.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.disttest.controller.config; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.disttest.controller.CommandForClient; + +public class TestConfig +{ + private final String _name; + + private final List<ClientConfig> _clients; + + private final List<QueueConfig> _queues; + + private final List<Map<String, String>> _iterations; + + public TestConfig() + { + _clients = Collections.emptyList(); + _queues = Collections.emptyList(); + _name = null; + _iterations = Collections.emptyList(); + } + + public TestConfig(String name, ClientConfig[] clients, QueueConfig[] queues) + { + _clients = Arrays.asList(clients); + _queues = Arrays.asList(queues); + _name = name; + _iterations = Collections.emptyList(); + } + + public List<String> getClientNames() + { + List<String> clientNames = new ArrayList<String>(); + for (ClientConfig clientConfig : _clients) + { + clientNames.add(clientConfig.getName()); + } + return clientNames; + } + + public int getTotalNumberOfClients() + { + return _clients.size(); + } + + public int getTotalNumberOfParticipants() + { + int numOfParticipants = 0; + for (ClientConfig client : _clients) + { + numOfParticipants = numOfParticipants + client.getTotalNumberOfParticipants(); + } + return numOfParticipants; + } + + public List<CommandForClient> createCommands() + { + List<CommandForClient> commandsForClients = new ArrayList<CommandForClient>(); + for (ClientConfig client : _clients) + { + commandsForClients.addAll(client.createCommands()); + } + + return Collections.unmodifiableList(commandsForClients); + } + + public List<QueueConfig> getQueues() + { + return Collections.unmodifiableList(_queues); + } + + public String getName() + { + return _name; + } + + public List<IterationValue> getIterationValues() + { + List<IterationValue> iterationValues = new ArrayList<IterationValue>(); + for (Map<String, String> iterationMap : _iterations) + { + iterationValues.add(new IterationValue(iterationMap)); + } + + return iterationValues; + } + + public List<ClientConfig> getClients() + { + return Collections.unmodifiableList(_clients); + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/TestInstance.java b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/TestInstance.java new file mode 100644 index 0000000000..9f555ef4da --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/TestInstance.java @@ -0,0 +1,102 @@ +/* + * 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.disttest.controller.config; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.qpid.disttest.controller.CommandForClient; +import org.apache.qpid.disttest.message.Command; + +public class TestInstance +{ + private static final IterationValue EMPTY_ITERATION_VALUES = new IterationValue(); + + private TestConfig _testConfig; + private IterationValue _iterationValue; + private int _iterationNumber; + + public TestInstance(TestConfig testConfig, int iterationNumber, IterationValue iterationValue) + { + _testConfig = testConfig; + _iterationNumber = iterationNumber; + _iterationValue = iterationValue; + } + + public TestInstance(TestConfig testConfig) + { + this(testConfig, 0, EMPTY_ITERATION_VALUES); + } + + public List<CommandForClient> createCommands() + { + List<CommandForClient> commands = _testConfig.createCommands(); + List<CommandForClient> newCommands = new ArrayList<CommandForClient>(commands.size()); + + for (CommandForClient commandForClient : commands) + { + String clientName = commandForClient.getClientName(); + Command command = commandForClient.getCommand(); + + _iterationValue.applyToCommand(command); + + newCommands.add(new CommandForClient(clientName, command)); + } + + return newCommands; + + } + + public String getName() + { + return _testConfig.getName(); + } + + public int getIterationNumber() + { + return _iterationNumber; + } + + public int getTotalNumberOfParticipants() + { + return _testConfig.getTotalNumberOfParticipants(); + } + + public List<QueueConfig> getQueues() + { + return _testConfig.getQueues(); + } + + public List<String> getClientNames() + { + return _testConfig.getClientNames(); + } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append("testName", getName()) + .append("iterationNumber", _iterationNumber) + .toString(); + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/jms/ClientJmsDelegate.java b/java/perftests/src/main/java/org/apache/qpid/disttest/jms/ClientJmsDelegate.java new file mode 100644 index 0000000000..d68fc86a0e --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/jms/ClientJmsDelegate.java @@ -0,0 +1,592 @@ +/* + * 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.disttest.jms; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.Context; +import javax.naming.NamingException; + +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.qpid.disttest.DistributedTestConstants; +import org.apache.qpid.disttest.DistributedTestException; +import org.apache.qpid.disttest.client.Client; +import org.apache.qpid.disttest.client.MessageProvider; +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.CreateConnectionCommand; +import org.apache.qpid.disttest.message.CreateConsumerCommand; +import org.apache.qpid.disttest.message.CreateMessageProviderCommand; +import org.apache.qpid.disttest.message.CreateProducerCommand; +import org.apache.qpid.disttest.message.CreateSessionCommand; +import org.apache.qpid.disttest.message.RegisterClientCommand; +import org.apache.qpid.disttest.message.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ClientJmsDelegate +{ + private static final Logger LOGGER = LoggerFactory.getLogger(ClientJmsDelegate.class); + + private final Context _context; + private final Destination _controllerQueue; + private final Connection _controllerConnection; + private final Session _controllerSession; + private final MessageProducer _controlQueueProducer; + + private final String _clientName; + private Queue _instructionQueue; + + private Map<String, Connection> _testConnections; + private Map<String, Session> _testSessions; + private Map<String, MessageProducer> _testProducers; + private Map<String, MessageConsumer> _testConsumers; + private Map<String, MessageProvider> _testMessageProviders; + + private final MessageProvider _defaultMessageProvider; + + public ClientJmsDelegate(final Context context) + { + try + { + _context = context; + final ConnectionFactory connectionFactory = (ConnectionFactory) _context.lookup("connectionfactory"); + _controllerConnection = connectionFactory.createConnection(); + _controllerConnection.start(); + _controllerQueue = (Destination) context.lookup(DistributedTestConstants.CONTROLLER_QUEUE_JNDI_NAME); + _controllerSession = _controllerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + _controlQueueProducer = _controllerSession.createProducer(_controllerQueue); + _clientName = UUID.randomUUID().toString(); + _testConnections = new HashMap<String, Connection>(); + _testSessions = new HashMap<String, Session>(); + _testProducers = new HashMap<String, MessageProducer>(); + _testConsumers = new HashMap<String, MessageConsumer>(); + _testMessageProviders = new HashMap<String, MessageProvider>(); + _defaultMessageProvider = new MessageProvider(null); + } + catch (final NamingException ne) + { + throw new DistributedTestException("Unable to create client jms delegate", ne); + } + catch (final JMSException jmse) + { + throw new DistributedTestException("Unable to create client jms delegate", jmse); + } + } + + public void setInstructionListener(final Client client) + { + try + { + _instructionQueue = _controllerSession.createTemporaryQueue(); + final MessageConsumer instructionConsumer = _controllerSession.createConsumer(_instructionQueue); + instructionConsumer.setMessageListener(new MessageListener() + { + @Override + public void onMessage(final Message message) + { + client.processInstruction(JmsMessageAdaptor.messageToCommand(message)); + } + }); + } + catch (final JMSException jmse) + { + throw new DistributedTestException("Unable to setup instruction listener", jmse); + } + } + + public void sendRegistrationMessage() + { + Command command; + try + { + command = new RegisterClientCommand(_clientName, _instructionQueue.getQueueName()); + } + catch (final JMSException e) + { + throw new DistributedTestException(e); + } + sendCommand(command); + } + + public void sendResponseMessage(final Response responseMessage) + { + sendCommand(responseMessage); + } + + private void sendCommand(final Command command) + { + try + { + final Message message = JmsMessageAdaptor.commandToMessage(_controllerSession, command); + _controlQueueProducer.send(message); + LOGGER.debug("Sent message for " + command.getType() + ". message id: " + message.getJMSMessageID()); + } + catch (final JMSException jmse) + { + throw new DistributedTestException("Unable to send command: " + command, jmse); + } + } + + public void createConnection(final CreateConnectionCommand command) + { + try + { + final ConnectionFactory connectionFactory = (ConnectionFactory) _context.lookup(command + .getConnectionFactoryName()); + final Connection newConnection = connectionFactory.createConnection(); + addConnection(command.getConnectionName(), newConnection); + } + catch (final NamingException ne) + { + throw new DistributedTestException("Unable to lookup factoryName: " + command.getConnectionFactoryName(), + ne); + } + catch (final JMSException jmse) + { + throw new DistributedTestException("Unable to create connection: " + command.getConnectionName() + + " (using factory name: " + command.getConnectionFactoryName() + ")", jmse); + } + } + + public void createSession(final CreateSessionCommand command) + { + try + { + final Connection connection = _testConnections.get(command.getConnectionName()); + if (connection == null) + { + throw new DistributedTestException("No test connection found called: " + command.getConnectionName(), + command); + } + final boolean transacted = command.getAcknowledgeMode() == Session.SESSION_TRANSACTED; + + final Session newSession = connection.createSession(transacted, command.getAcknowledgeMode()); + LOGGER.info("Created session " + command.getSessionName() + " with transacted = " + newSession.getTransacted() + " and acknowledgeMode = " + newSession.getAcknowledgeMode()); + + addSession(command.getSessionName(), newSession); + } + catch (final JMSException jmse) + { + throw new DistributedTestException("Unable to create new session: " + command, jmse); + } + } + + public void createProducer(final CreateProducerCommand command) + { + try + { + final Session session = _testSessions.get(command.getSessionName()); + if (session == null) + { + throw new DistributedTestException("No test session found called: " + command.getSessionName(), command); + } + final Destination destination = session.createQueue(command.getDestinationName()); + final MessageProducer jmsProducer = session.createProducer(destination); + if (command.getPriority() != -1) + { + jmsProducer.setPriority(command.getPriority()); + } + if (command.getTimeToLive() > 0) + { + jmsProducer.setTimeToLive(command.getTimeToLive()); + } + + if (command.getDeliveryMode() == DeliveryMode.NON_PERSISTENT + || command.getDeliveryMode() == DeliveryMode.PERSISTENT) + { + jmsProducer.setDeliveryMode(command.getDeliveryMode()); + } + + addProducer(command.getParticipantName(), jmsProducer); + } + catch (final JMSException jmse) + { + throw new DistributedTestException("Unable to create new producer: " + command, jmse); + } + + } + + public void createConsumer(final CreateConsumerCommand command) + { + try + { + final Session session = _testSessions.get(command.getSessionName()); + if (session == null) + { + throw new DistributedTestException("No test session found called: " + command.getSessionName(), command); + } + final Destination destination = command.isTopic() ? session.createTopic(command.getDestinationName()) + : session.createQueue(command.getDestinationName()); + final MessageConsumer jmsConsumer = session.createConsumer(destination, command.getSelector()); + + _testConsumers.put(command.getParticipantName(), jmsConsumer); + } + catch (final JMSException jmse) + { + throw new DistributedTestException("Unable to create new consumer: " + command, jmse); + } + } + + /** + * destroy the client. Don't call from the Dispatcher thread. + */ + public void destroy() + { + try + { + // Stopping the connection allows in-flight onMessage calls to + // finish. + _controllerConnection.stop(); + + if (_controllerSession != null) + { + _controllerSession.close(); + } + if (_controllerConnection != null) + { + _controllerConnection.close(); + } + + } + catch (final JMSException jmse) + { + throw new DistributedTestException("Unable to destroy cleanly", jmse); + } + } + + public Destination getControllerQueue() + { + return _controllerQueue; + } + + public String getClientName() + { + return _clientName; + } + + public int getNoOfTestConnections() + { + return _testConnections.size(); + } + + public int getNoOfTestSessions() + { + return _testSessions.size(); + } + + public int getNoOfTestProducers() + { + return _testProducers.size(); + } + + public int getNoOfTestConsumers() + { + return _testConsumers.size(); + } + + public void startConnections() + { + // start connections for consumers + // it would be better if we could track consumer connections and start + // only those + if (!_testConsumers.isEmpty()) + { + for (final Map.Entry<String, Connection> entry : _testConnections.entrySet()) + { + final Connection connection = entry.getValue(); + try + { + connection.start(); + } + catch (final JMSException e) + { + throw new DistributedTestException("Failed to start connection '" + entry.getKey() + "' :" + + e.getLocalizedMessage()); + } + } + } + } + + public void commitOrAcknowledgeMessage(final Message message, final String sessionName) + { + try + { + final Session session = _testSessions.get(sessionName); + if (session.getTransacted()) + { + session.commit(); + } + else if (message != null && session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE) + { + message.acknowledge(); + } + } + catch (final JMSException jmse) + { + throw new DistributedTestException("Unable to commit or acknowledge message on session: " + + sessionName, jmse); + } + } + + public int getAcknowledgeMode(final String sessionName) + { + try + { + final Session session = _testSessions.get(sessionName); + return session.getAcknowledgeMode(); + } + catch (final JMSException jmse) + { + throw new DistributedTestException("Unable to determine acknowledgement mode for session: " + + sessionName, jmse); + } + } + + public Message sendNextMessage(final CreateProducerCommand command) + { + Message sentMessage = null; + MessageProvider messageProvider = _testMessageProviders.get(command.getMessageProviderName()); + if (messageProvider == null) + { + messageProvider = _defaultMessageProvider; + } + + final Session session = _testSessions.get(command.getSessionName()); + final MessageProducer producer = _testProducers.get(command.getParticipantName()); + try + { + sentMessage = messageProvider.nextMessage(session, command); + int deliveryMode = producer.getDeliveryMode(); + int priority = producer.getPriority(); + long ttl = producer.getTimeToLive(); + if (messageProvider.isPropertySet(MessageProvider.PRIORITY)) + { + priority = sentMessage.getJMSPriority(); + } + if (messageProvider.isPropertySet(MessageProvider.DELIVERY_MODE)) + { + deliveryMode = sentMessage.getJMSDeliveryMode(); + } + if (messageProvider.isPropertySet(MessageProvider.TTL)) + { + ttl = sentMessage.getLongProperty(MessageProvider.TTL); + } + producer.send(sentMessage, deliveryMode, priority, ttl); + } + catch (final JMSException jmse) + { + throw new DistributedTestException("Unable to create and send message with producer: " + + command.getParticipantName() + " on session: " + command.getSessionName(), jmse); + } + return sentMessage; + } + + protected void addSession(final String sessionName, final Session newSession) + { + _testSessions.put(sessionName, newSession); + } + + protected void addConnection(final String connectionName, final Connection newConnection) + { + _testConnections.put(connectionName, newConnection); + } + + protected void addProducer(final String producerName, final MessageProducer jmsProducer) + { + _testProducers.put(producerName, jmsProducer); + } + + public Message consumeMessage(String consumerName, long receiveInterval) + { + Message consumedMessage = null; + MessageConsumer consumer = _testConsumers.get(consumerName); + try + { + consumedMessage = consumer.receive(receiveInterval); + } + catch (JMSException e) + { + throw new DistributedTestException("Unable to consume message with consumer: " + consumerName, e); + } + return consumedMessage; + } + + public void registerListener(String consumerName, MessageListener messageListener) + { + MessageConsumer consumer = _testConsumers.get(consumerName); + try + { + consumer.setMessageListener(messageListener); + } + catch (JMSException e) + { + throw new DistributedTestException("Unable to register message listener with consumer: " + consumerName, e); + } + } + + public void rollbackOrRecover(String sessionName) + { + try + { + final Session session = _testSessions.get(sessionName); + if (session.getTransacted()) + { + session.rollback(); + } + else if (session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE) + { + session.recover(); + } + } + catch (final JMSException jmse) + { + throw new DistributedTestException("Unable to rollback or recover on session: " + + sessionName, jmse); + } + } + + public void releaseMessage(String sessionName) + { + try + { + final Session session = _testSessions.get(sessionName); + if (session.getTransacted()) + { + session.rollback(); + } + else + { + session.recover(); + } + } + catch (final JMSException jmse) + { + LOGGER.warn("Unable to rollback or recover on session: " + sessionName, jmse); + } + } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("clientName", _clientName).toString(); + } + + public void closeTestConnections() + { + StringBuilder jmsErrorMessages = new StringBuilder(); + int failedCloseCounter = 0; + for (final Map.Entry<String, Connection> entry : _testConnections.entrySet()) + { + final Connection connection = entry.getValue(); + try + { + connection.close(); + } + catch (final JMSException e) + { + LOGGER.error("Failed to close connection '" + entry.getKey() + "' :" + e.getLocalizedMessage(), e); + failedCloseCounter++; + if (jmsErrorMessages.length() > 0) + { + jmsErrorMessages.append('\n'); + } + jmsErrorMessages.append(e.getMessage()); + } + } + _testConnections.clear(); + _testSessions.clear(); + _testProducers.clear(); + _testConsumers.clear(); + if (failedCloseCounter > 0) + { + throw new DistributedTestException("Close failed for " + failedCloseCounter + " connection(s) with the following errors: " + jmsErrorMessages.toString()); + } + } + + public void closeTestConsumer(String consumerName) + { + MessageConsumer consumer = _testConsumers.get(consumerName); + if (consumer != null) + { + try + { + consumer.close(); + LOGGER.info("Closed test consumer " + consumerName); + } + catch (JMSException e) + { + throw new DistributedTestException("Failed to close consumer: " + consumerName, e); + } + } + } + + public void closeTestProducer(String producerName) + { + MessageProducer producer = _testProducers.get(producerName); + if (producer != null) + { + try + { + producer.close(); + } + catch (JMSException e) + { + throw new DistributedTestException("Failed to close producer: " + producerName, e); + } + } + } + + public int calculatePayloadSizeFrom(Message message) + { + try + { + if (message != null && message instanceof TextMessage) + { + return ((TextMessage) message).getText().getBytes().length; + } + // TODO support other message types + return 0; + } + catch (JMSException e) + { + throw new DistributedTestException("Unable to determine the payload size for message " + message, e); + } + } + + public void createMessageProvider(CreateMessageProviderCommand command) + { + _testMessageProviders.put(command.getProviderName(), new MessageProvider(command.getMessageProperties())); + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/jms/ControllerJmsDelegate.java b/java/perftests/src/main/java/org/apache/qpid/disttest/jms/ControllerJmsDelegate.java new file mode 100644 index 0000000000..69da409be5 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/jms/ControllerJmsDelegate.java @@ -0,0 +1,210 @@ +/* + * 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.disttest.jms; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +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.NamingException; + +import org.apache.qpid.disttest.DistributedTestException; +import org.apache.qpid.disttest.controller.CommandListener; +import org.apache.qpid.disttest.controller.config.QueueConfig; +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.RegisterClientCommand; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ControllerJmsDelegate +{ + private static final Logger LOGGER = LoggerFactory.getLogger(ControllerJmsDelegate.class); + + private final Map<String, Destination> _clientNameToQueueMap = new ConcurrentHashMap<String, Destination>(); + private final Connection _connection; + private final Destination _controllerQueue; + private final Session _session; + private final QueueCreator _queueCreator; + + private List<CommandListener> _commandListeners = new CopyOnWriteArrayList<CommandListener>(); + + public ControllerJmsDelegate(final Context context) throws NamingException, JMSException + { + final ConnectionFactory connectionFactory = (ConnectionFactory) context.lookup("connectionfactory"); + _connection = connectionFactory.createConnection(); + _connection.start(); + _controllerQueue = (Destination) context.lookup("controllerqueue"); + _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + _queueCreator = new QpidQueueCreator(); + } + + public void start() + { + try + { + final MessageConsumer consumer = _session.createConsumer(_controllerQueue); + consumer.setMessageListener(new MessageListener() + { + @Override + public void onMessage(final Message message) + { + try + { + String jmsMessageID = message.getJMSMessageID(); + LOGGER.debug("Received message " + jmsMessageID); + + final Command command = JmsMessageAdaptor.messageToCommand(message); + LOGGER.debug("Converted message " + jmsMessageID + " into command: " + command); + + processCommandWithFirstSupportingListener(command); + LOGGER.debug("Finished processing command for message " + jmsMessageID); + } + catch (Throwable t) + { + LOGGER.error("Can't handle JMS message", t); + } + } + }); + } + catch (final JMSException e) + { + throw new DistributedTestException(e); + } + } + + /** ensures connections are closed, otherwise the JVM may be prevented from terminating */ + public void closeConnections() + { + try + { + _session.close(); + } + catch (JMSException e) + { + LOGGER.error("Unable to close session", e); + } + + try + { + _connection.stop(); + } + catch (JMSException e) + { + LOGGER.error("Unable to stop connection", e); + } + + try + { + _connection.close(); + } + catch (JMSException e) + { + throw new DistributedTestException("Unable to close connection", e); + } + } + + public void registerClient(final RegisterClientCommand command) + { + final String clientName = command.getClientName(); + final Destination clientIntructionQueue = createDestinationFromString(command.getClientQueueName()); + _clientNameToQueueMap.put(clientName, clientIntructionQueue); + } + + public void sendCommandToClient(final String clientName, final Command command) + { + final Destination clientQueue = _clientNameToQueueMap.get(clientName); + if (clientQueue == null) + { + throw new DistributedTestException("Client name " + clientName + " not known. I know about: " + + _clientNameToQueueMap.keySet()); + } + + try + { + final MessageProducer producer = _session.createProducer(clientQueue); + final Message message = JmsMessageAdaptor.commandToMessage(_session, command); + + producer.send(message); + } + catch (final JMSException e) + { + throw new DistributedTestException(e); + } + } + + private void processCommandWithFirstSupportingListener(Command command) + { + for (CommandListener listener : _commandListeners) + { + if (listener.supports(command)) + { + listener.processCommand(command); + return; + } + } + + throw new IllegalStateException("There is no registered listener to process command " + command); + } + + private Destination createDestinationFromString(final String clientQueueName) + { + Destination clientIntructionQueue; + try + { + clientIntructionQueue = _session.createQueue(clientQueueName); + } + catch (JMSException e) + { + throw new DistributedTestException("Unable to create Destination from " + clientQueueName); + } + return clientIntructionQueue; + } + + public void createQueues(List<QueueConfig> queues) + { + _queueCreator.createQueues(_session, queues); + } + + public void deleteQueues(List<QueueConfig> queues) + { + _queueCreator.deleteQueues(_session, queues); + } + + public void addCommandListener(CommandListener commandListener) + { + _commandListeners.add(commandListener); + } + + public void removeCommandListener(CommandListener commandListener) + { + _commandListeners.remove(commandListener); + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/jms/JmsMessageAdaptor.java b/java/perftests/src/main/java/org/apache/qpid/disttest/jms/JmsMessageAdaptor.java new file mode 100644 index 0000000000..c9dba21a74 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/jms/JmsMessageAdaptor.java @@ -0,0 +1,124 @@ +/* + * 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.disttest.jms; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; + +import org.apache.qpid.disttest.DistributedTestConstants; +import org.apache.qpid.disttest.DistributedTestException; +import org.apache.qpid.disttest.json.JsonHandler; +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.CommandType; +import org.apache.qpid.disttest.message.ConsumerParticipantResult; +import org.apache.qpid.disttest.message.CreateConnectionCommand; +import org.apache.qpid.disttest.message.CreateConsumerCommand; +import org.apache.qpid.disttest.message.CreateMessageProviderCommand; +import org.apache.qpid.disttest.message.CreateProducerCommand; +import org.apache.qpid.disttest.message.CreateResponderCommand; +import org.apache.qpid.disttest.message.CreateSessionCommand; +import org.apache.qpid.disttest.message.NoOpCommand; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.disttest.message.ProducerParticipantResult; +import org.apache.qpid.disttest.message.RegisterClientCommand; +import org.apache.qpid.disttest.message.Response; +import org.apache.qpid.disttest.message.StartTestCommand; +import org.apache.qpid.disttest.message.StopClientCommand; +import org.apache.qpid.disttest.message.TearDownTestCommand; + +public class JmsMessageAdaptor +{ + public static Message commandToMessage(final Session session, final Command command) + { + Message jmsMessage = null; + try + { + jmsMessage = session.createMessage(); + jmsMessage.setStringProperty(DistributedTestConstants.MSG_COMMAND_PROPERTY, command.getType().name()); + final JsonHandler jsonHandler = new JsonHandler(); + jmsMessage.setStringProperty(DistributedTestConstants.MSG_JSON_PROPERTY, jsonHandler.marshall(command)); + } + catch (final JMSException jmse) + { + throw new DistributedTestException("Unable to convert command " + command + " to JMS Message", jmse); + } + + return jmsMessage; + } + + public static Command messageToCommand(final Message jmsMessage) + { + Command command = null; + try + { + final CommandType commandType = CommandType.valueOf(jmsMessage + .getStringProperty(DistributedTestConstants.MSG_COMMAND_PROPERTY)); + final JsonHandler jsonHandler = new JsonHandler(); + command = jsonHandler.unmarshall(jmsMessage.getStringProperty(DistributedTestConstants.MSG_JSON_PROPERTY), + getCommandClassFromType(commandType)); + } + catch (final JMSException jmse) + { + throw new DistributedTestException("Unable to convert JMS message " + jmsMessage + " to command object", + jmse); + } + return command; + } + + static Class<? extends Command> getCommandClassFromType(final CommandType type) + { + switch (type) + { + case CREATE_CONNECTION: + return CreateConnectionCommand.class; + case CREATE_SESSION: + return CreateSessionCommand.class; + case CREATE_PRODUCER: + return CreateProducerCommand.class; + case CREATE_CONSUMER: + return CreateConsumerCommand.class; + case CREATE_RESPONDER: + return CreateResponderCommand.class; + case NO_OP: + return NoOpCommand.class; + case REGISTER_CLIENT: + return RegisterClientCommand.class; + case STOP_CLIENT: + return StopClientCommand.class; + case RESPONSE: + return Response.class; + case START_TEST: + return StartTestCommand.class; + case TEAR_DOWN_TEST: + return TearDownTestCommand.class; + case PARTICIPANT_RESULT: + return ParticipantResult.class; + case CONSUMER_PARTICIPANT_RESULT: + return ConsumerParticipantResult.class; + case PRODUCER_PARTICIPANT_RESULT: + return ProducerParticipantResult.class; + case CREATE_MESSAGE_PROVIDER: + return CreateMessageProviderCommand.class; + default: + throw new DistributedTestException("No class defined for type: " + type); + } + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/jms/QpidQueueCreator.java b/java/perftests/src/main/java/org/apache/qpid/disttest/jms/QpidQueueCreator.java new file mode 100644 index 0000000000..912ce54495 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/jms/QpidQueueCreator.java @@ -0,0 +1,97 @@ +/* + * 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.disttest.jms; + +import java.util.List; + +import javax.jms.Session; + +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.disttest.DistributedTestException; +import org.apache.qpid.disttest.controller.config.QueueConfig; +import org.apache.qpid.framing.FieldTable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class QpidQueueCreator implements QueueCreator +{ + private static final Logger LOGGER = LoggerFactory.getLogger(QpidQueueCreator.class); + + private static final FieldTable EMPTY_QUEUE_BIND_ARGUMENTS = new FieldTable(); + + @Override + public void createQueues(Session session, List<QueueConfig> configs) + { + AMQSession<?, ?> amqSession = (AMQSession<?, ?>)session; + for (QueueConfig queueConfig : configs) + { + createQueue(amqSession, queueConfig); + } + } + + @Override + public void deleteQueues(Session session, List<QueueConfig> configs) + { + AMQSession<?, ?> amqSession = (AMQSession<?, ?>)session; + for (QueueConfig queueConfig : configs) + { + deleteQueue(amqSession, queueConfig); + } + } + + private void createQueue(AMQSession<?, ?> session, QueueConfig queueConfig) + { + try + { + AMQDestination destination = (AMQDestination) session.createQueue(queueConfig.getName()); + boolean autoDelete = false; + boolean exclusive = false; + session.createQueue(destination.getAMQQueueName(), autoDelete, + queueConfig.isDurable(), exclusive, queueConfig.getAttributes()); + session.bindQueue(destination.getAMQQueueName(), destination.getRoutingKey(), + EMPTY_QUEUE_BIND_ARGUMENTS, destination.getExchangeName(), + destination, autoDelete); + + LOGGER.info("Created queue " + queueConfig); + } + catch (Exception e) + { + throw new DistributedTestException("Failed to create queue:" + queueConfig, e); + } + } + + private void deleteQueue(AMQSession<?, ?> session, QueueConfig queueConfig) + { + try + { + // The Qpid AMQSession API currently makes the #deleteQueue method protected and the + // raw protocol method public. This should be changed then we should switch the below to + // use #deleteQueue. + AMQDestination destination = (AMQDestination) session.createQueue(queueConfig.getName()); + session.sendQueueDelete(destination.getAMQQueueName()); + LOGGER.info("Deleted queue " + queueConfig.getName()); + } + catch (Exception e) + { + throw new DistributedTestException("Failed to delete queue:" + queueConfig.getName(), e); + } + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/jms/QueueCreator.java b/java/perftests/src/main/java/org/apache/qpid/disttest/jms/QueueCreator.java new file mode 100644 index 0000000000..0947dd53cb --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/jms/QueueCreator.java @@ -0,0 +1,31 @@ +/* + * 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.disttest.jms; + +import java.util.List; + +import javax.jms.Session; + +import org.apache.qpid.disttest.controller.config.QueueConfig; + +public interface QueueCreator +{ + public void createQueues(final Session session, final List<QueueConfig> configs); + public void deleteQueues(final Session session, final List<QueueConfig> configs); +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/json/JsonHandler.java b/java/perftests/src/main/java/org/apache/qpid/disttest/json/JsonHandler.java new file mode 100644 index 0000000000..8e50cd4f11 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/json/JsonHandler.java @@ -0,0 +1,43 @@ +/* + * 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.disttest.json; + +import org.apache.qpid.disttest.client.property.PropertyValue; +import org.apache.qpid.disttest.message.Command; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public class JsonHandler +{ + private final Gson _gson = new GsonBuilder() + .registerTypeAdapter(PropertyValue.class, new PropertyValueAdapter()) + .create(); + + public <T extends Command> T unmarshall(final String jsonParams, final Class<T> clazz) + { + return _gson.fromJson(jsonParams, clazz); + } + + public <T extends Command> String marshall(final T command) + { + return _gson.toJson(command); + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/json/PropertyValueAdapter.java b/java/perftests/src/main/java/org/apache/qpid/disttest/json/PropertyValueAdapter.java new file mode 100644 index 0000000000..94f712e652 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/json/PropertyValueAdapter.java @@ -0,0 +1,147 @@ +/* + * 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.disttest.json; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.disttest.client.property.GeneratedPropertyValue; +import org.apache.qpid.disttest.client.property.PropertyValue; +import org.apache.qpid.disttest.client.property.PropertyValueFactory; +import org.apache.qpid.disttest.client.property.SimplePropertyValue; + +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +public class PropertyValueAdapter implements JsonDeserializer<PropertyValue>, JsonSerializer<PropertyValue> +{ + private static final String DEF_FIELD = "@def"; + private PropertyValueFactory _factory = new PropertyValueFactory(); + + @Override + public PropertyValue deserialize(JsonElement json, Type type, JsonDeserializationContext context) + throws JsonParseException + { + if (json.isJsonNull()) + { + return null; + } + else if (json.isJsonPrimitive()) + { + Object result = null; + JsonPrimitive primitive = json.getAsJsonPrimitive(); + if (primitive.isString()) + { + result = primitive.getAsString(); + } + else if (primitive.isNumber()) + { + String asString = primitive.getAsString(); + if (asString.indexOf('.') != -1 || asString.indexOf('e') != -1) + { + result = primitive.getAsDouble(); + } + else + { + result = primitive.getAsLong(); + } + } + else if (primitive.isBoolean()) + { + result = primitive.getAsBoolean(); + } + else + { + throw new JsonParseException("Unsupported primitive value " + primitive); + } + return new SimplePropertyValue(result); + } + else if (json.isJsonArray()) + { + JsonArray array = json.getAsJsonArray(); + List<Object> result = new ArrayList<Object>(array.size()); + for (JsonElement element : array) + { + result.add(context.deserialize(element, Object.class)); + } + return new SimplePropertyValue(result); + } + else if (json.isJsonObject()) + { + JsonObject object = json.getAsJsonObject(); + JsonElement defElement = object.getAsJsonPrimitive(DEF_FIELD); + Class<?> classInstance = null; + if (defElement != null) + { + try + { + classInstance = _factory.getPropertyValueClass(defElement.getAsString()); + } + catch (ClassNotFoundException e) + { + // ignore + } + } + if (classInstance == null) + { + Map<String, Object> result = new HashMap<String, Object>(); + for (Map.Entry<String, JsonElement> entry : object.entrySet()) + { + Object value = context.deserialize(entry.getValue(), Object.class); + result.put(entry.getKey(), value); + } + return new SimplePropertyValue(result); + } + else + { + return context.deserialize(json, classInstance); + } + } + else + { + throw new JsonParseException("Unsupported JSON type " + json); + } + } + + @Override + public JsonElement serialize(PropertyValue src, Type typeOfSrc, JsonSerializationContext context) + { + if (src instanceof GeneratedPropertyValue) + { + JsonObject object = (JsonObject) context.serialize(src, Object.class); + object.addProperty(DEF_FIELD, ((GeneratedPropertyValue) src).getDefinition()); + return object; + } + else + { + return context.serialize(src.getValue(), Object.class); + } + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/Command.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/Command.java new file mode 100644 index 0000000000..86b4d0e439 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/Command.java @@ -0,0 +1,56 @@ +/* + * 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.disttest.message; + + +import org.apache.commons.lang.builder.ReflectionToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.qpid.disttest.Visitor; +import org.apache.qpid.disttest.client.Client; +import org.apache.qpid.disttest.controller.Controller; + +/** + * A command sent between the {@link Controller} and a {@link Client} + */ +public abstract class Command +{ + private final CommandType type; + + public Command(final CommandType type) + { + this.type = type; + } + + public CommandType getType() + { + return type; + } + + public void accept(Visitor visitor) + { + visitor.visit(this); + } + + @Override + public String toString() + { + return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/CommandType.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/CommandType.java new file mode 100644 index 0000000000..b04cbdaba1 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/CommandType.java @@ -0,0 +1,39 @@ +/* + * 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.disttest.message; + +public enum CommandType +{ + CREATE_CONNECTION, + CREATE_CONSUMER, + CREATE_PRODUCER, + CREATE_RESPONDER, + CREATE_SESSION, + NO_OP, + REGISTER_CLIENT, + RESPONSE, + START_TEST, + STOP_CLIENT, + TEAR_DOWN_TEST, + PARTICIPANT_RESULT, + CONSUMER_PARTICIPANT_RESULT, + PRODUCER_PARTICIPANT_RESULT, + CREATE_MESSAGE_PROVIDER +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/ConsumerParticipantResult.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/ConsumerParticipantResult.java new file mode 100644 index 0000000000..f92e3ea538 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/ConsumerParticipantResult.java @@ -0,0 +1,118 @@ +/* + * 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.disttest.message; + +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_BROWSIING_SUBSCRIPTION; +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_DURABLE_SUBSCRIPTION; +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_NO_LOCAL; +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_SELECTOR; +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_SYNCHRONOUS_CONSUMER; +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_TOPIC; + +public class ConsumerParticipantResult extends ParticipantResult +{ + private boolean _topic; + private boolean _durableSubscription; + private boolean _browsingSubscription; + private boolean _selector; + private boolean _noLocal; + private boolean _synchronousConsumer; + + public ConsumerParticipantResult() + { + super(CommandType.CONSUMER_PARTICIPANT_RESULT); + } + + public ConsumerParticipantResult(String participantName) + { + this(); + setParticipantName(participantName); + } + + @OutputAttribute(attribute=IS_DURABLE_SUBSCRIPTION) + public boolean isDurableSubscription() + { + return _durableSubscription; + } + + public void setDurableSubscription(boolean durable) + { + _durableSubscription = durable; + } + + + @OutputAttribute(attribute=IS_BROWSIING_SUBSCRIPTION) + public boolean isBrowsingSubscription() + { + return _browsingSubscription; + } + + public void setBrowsingSubscription(boolean browsingSubscription) + { + _browsingSubscription = browsingSubscription; + } + + + @OutputAttribute(attribute=IS_SELECTOR) + public boolean isSelector() + { + return _selector; + } + + public void setSelector(boolean selector) + { + _selector = selector; + } + + + @OutputAttribute(attribute=IS_NO_LOCAL) + public boolean isNoLocal() + { + return _noLocal; + + } + + public void setNoLocal(boolean noLocal) + { + _noLocal = noLocal; + } + + @OutputAttribute(attribute=IS_SYNCHRONOUS_CONSUMER) + public boolean isSynchronousConsumer() + { + return _synchronousConsumer; + } + + public void setSynchronousConsumer(boolean synchronousConsumer) + { + _synchronousConsumer = synchronousConsumer; + } + + + public void setTopic(boolean isTopic) + { + _topic = isTopic; + } + + @OutputAttribute(attribute=IS_TOPIC) + public boolean isTopic() + { + return _topic; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateConnectionCommand.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateConnectionCommand.java new file mode 100644 index 0000000000..c5a96e9a94 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateConnectionCommand.java @@ -0,0 +1,58 @@ +/* + * 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.disttest.message; + +public class CreateConnectionCommand extends Command +{ + private String _connectionName; + private String _connectionFactoryName; + + public CreateConnectionCommand() + { + super(CommandType.CREATE_CONNECTION); + } + + public CreateConnectionCommand(String connectionName, String connectionFactoryName) + { + super(CommandType.CREATE_CONNECTION); + _connectionName = connectionName; + _connectionFactoryName = connectionFactoryName; + } + + public void setConnectionName(final String connectionName) + { + this._connectionName = connectionName; + } + + public String getConnectionName() + { + return _connectionName; + } + + public void setConnectionFactoryName(final String connectionFactoryName) + { + this._connectionFactoryName = connectionFactoryName; + } + + public String getConnectionFactoryName() + { + return _connectionFactoryName; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateConsumerCommand.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateConsumerCommand.java new file mode 100644 index 0000000000..678e428f94 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateConsumerCommand.java @@ -0,0 +1,108 @@ +/* + * 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.disttest.message; + +public class CreateConsumerCommand extends CreateParticpantCommand +{ + private boolean _isTopic; + private boolean _isDurableSubscription; + private boolean _isBrowsingSubscription; + private String _selector; + private boolean _noLocal; + private boolean _synchronous; + private long _receiveTimeout = 5000; + + + public CreateConsumerCommand() + { + super(CommandType.CREATE_CONSUMER); + } + + public boolean isDurableSubscription() + { + return _isDurableSubscription; + } + + public void setDurableSubscription(final boolean isDurableSubscription) + { + this._isDurableSubscription = isDurableSubscription; + } + + public boolean isBrowsingSubscription() + { + return _isBrowsingSubscription; + } + + public void setBrowsingSubscription(final boolean isBrowsingSubscription) + { + _isBrowsingSubscription = isBrowsingSubscription; + } + + public String getSelector() + { + return _selector; + } + + public void setSelector(final String selector) + { + this._selector = selector; + } + + public boolean isNoLocal() + { + return _noLocal; + } + + public void setNoLocal(final boolean noLocal) + { + this._noLocal = noLocal; + } + + public boolean isTopic() + { + return _isTopic; + } + + public void setTopic(boolean isTopic) + { + this._isTopic = isTopic; + } + + public boolean isSynchronous() + { + return _synchronous; + } + + public void setSynchronous(boolean synchronous) + { + _synchronous = synchronous; + } + + public void setReceiveTimeout(long receiveTimeout) + { + _receiveTimeout = receiveTimeout; + + } + + public long getReceiveTimeout() + { + return _receiveTimeout; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateMessageProviderCommand.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateMessageProviderCommand.java new file mode 100644 index 0000000000..3f30fdd96a --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateMessageProviderCommand.java @@ -0,0 +1,54 @@ +/* + * 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.disttest.message; + +import java.util.Map; + +import org.apache.qpid.disttest.client.property.PropertyValue; + +public class CreateMessageProviderCommand extends Command +{ + private String _providerName; + private Map<String, PropertyValue> _messageProperties; + + public CreateMessageProviderCommand() + { + super(CommandType.CREATE_MESSAGE_PROVIDER); + } + + public String getProviderName() + { + return _providerName; + } + + public void setProviderName(String providerName) + { + this._providerName = providerName; + } + + public Map<String, PropertyValue> getMessageProperties() + { + return _messageProperties; + } + + public void setMessageProperties(Map<String, PropertyValue> messageProperties) + { + this._messageProperties = messageProperties; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateParticpantCommand.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateParticpantCommand.java new file mode 100644 index 0000000000..b1caa6ef75 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateParticpantCommand.java @@ -0,0 +1,103 @@ +/* + * 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.disttest.message; + +import org.apache.commons.lang.builder.ToStringBuilder; + +public abstract class CreateParticpantCommand extends Command +{ + private String _participantName; + private String _sessionName; + private String _destinationName; + private long _numberOfMessages; + private int _batchSize; + private long _maximumDuration; + + public CreateParticpantCommand(CommandType type) + { + super(type); + } + + public String getParticipantName() + { + return _participantName; + } + + public void setParticipantName(final String participantName) + { + _participantName = participantName; + } + + public String getSessionName() + { + return _sessionName; + } + + public void setSessionName(final String sessionName) + { + _sessionName = sessionName; + } + + public String getDestinationName() + { + return _destinationName; + } + + public void setDestinationName(final String destinationName) + { + _destinationName = destinationName; + } + + public long getNumberOfMessages() + { + return _numberOfMessages; + } + + public void setNumberOfMessages(final long numberOfMessages) + { + _numberOfMessages = numberOfMessages; + } + + public int getBatchSize() + { + return _batchSize; + } + + public void setBatchSize(int batchSize) + { + _batchSize = batchSize; + } + + public long getMaximumDuration() + { + return _maximumDuration; + } + + public void setMaximumDuration(long maximumDuration) + { + _maximumDuration = maximumDuration; + } + + @Override + public String toString() + { + return ToStringBuilder.reflectionToString(this); + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateProducerCommand.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateProducerCommand.java new file mode 100644 index 0000000000..69dfe1ff5a --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateProducerCommand.java @@ -0,0 +1,106 @@ +/* + * 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.disttest.message; + +public class CreateProducerCommand extends CreateParticpantCommand +{ + private int _deliveryMode; + private int _messageSize; + private int _priority; + private long _timeToLive; + private long _interval; + private long _startDelay; + private String _messageProviderName; + + public CreateProducerCommand() + { + super(CommandType.CREATE_PRODUCER); + } + + public int getMessageSize() + { + return _messageSize; + } + + public void setMessageSize(final int messageSize) + { + this._messageSize = messageSize; + } + + public int getPriority() + { + return _priority; + } + + public void setPriority(final int priority) + { + this._priority = priority; + } + + public int getDeliveryMode() + { + return _deliveryMode; + } + + public void setDeliveryMode(final int deliveryMode) + { + this._deliveryMode = deliveryMode; + } + + public long getTimeToLive() + { + return _timeToLive; + } + + public void setTimeToLive(final long timeToLive) + { + this._timeToLive = timeToLive; + } + + public long getInterval() + { + return _interval; + } + + public void setInterval(long interval) + { + this._interval = interval; + } + + public long getStartDelay() + { + return _startDelay; + } + + public void setStartDelay(long startDelay) + { + this._startDelay = startDelay; + } + + public String getMessageProviderName() + { + return _messageProviderName; + } + + public void setMessageProviderName(String messageProviderName) + { + this._messageProviderName = messageProviderName; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateResponderCommand.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateResponderCommand.java new file mode 100644 index 0000000000..85a2b5e548 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateResponderCommand.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.disttest.message; + +public class CreateResponderCommand extends Command +{ + public CreateResponderCommand() + { + super(CommandType.CREATE_RESPONDER); + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateSessionCommand.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateSessionCommand.java new file mode 100644 index 0000000000..f6f59c26af --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/CreateSessionCommand.java @@ -0,0 +1,62 @@ +/* + * 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.disttest.message; + +public class CreateSessionCommand extends Command +{ + private String sessionName; + private String connectionName; + private int acknowledgeMode; + + public CreateSessionCommand() + { + super(CommandType.CREATE_SESSION); + } + + public String getSessionName() + { + return sessionName; + } + + public void setSessionName(final String sessionName) + { + this.sessionName = sessionName; + } + + public String getConnectionName() + { + return connectionName; + } + + public void setConnectionName(final String connectionName) + { + this.connectionName = connectionName; + } + + public int getAcknowledgeMode() + { + return acknowledgeMode; + } + + public void setAcknowledgeMode(final int acknowledgeMode) + { + this.acknowledgeMode = acknowledgeMode; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/NoOpCommand.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/NoOpCommand.java new file mode 100644 index 0000000000..1cdaf00163 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/NoOpCommand.java @@ -0,0 +1,30 @@ +/* + * 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.disttest.message; + + +public class NoOpCommand extends Command +{ + public NoOpCommand() + { + super(CommandType.NO_OP); + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/OutputAttribute.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/OutputAttribute.java new file mode 100644 index 0000000000..b912eaa1cb --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/OutputAttribute.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.disttest.message; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +/** + * Marks an attribute of {@link ParticipantResult} that should be written to the test output file. + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface OutputAttribute +{ + ParticipantAttribute attribute(); +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/ParticipantAttribute.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/ParticipantAttribute.java new file mode 100644 index 0000000000..ccc7c0d9fb --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/ParticipantAttribute.java @@ -0,0 +1,70 @@ +/* + * 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.disttest.message; + +import org.apache.qpid.disttest.client.Participant; + +/** + * Meta-date representing the attributes of {@link Participant} that we write to the test output file. + * + * Order of declaration is currently important - it determines they order they appear in the output. + * + * @see OutputAttribute + */ +public enum ParticipantAttribute +{ + TEST_NAME("testName"), + ITERATION_NUMBER("iterationNumber"), + CONFIGURED_CLIENT_NAME("clientName"), + PARTICIPANT_NAME("participantName"), + NUMBER_OF_MESSAGES_PROCESSED("numberOfMessages"), + PAYLOAD_SIZE("payloadSizeB"), + PRIORITY("priority"), + TIME_TO_LIVE("timeToLiveMs"), + ACKNOWLEDGE_MODE("acknowledgeMode"), + DELIVERY_MODE("deliveryMode"), + BATCH_SIZE("batchSize"), + MAXIMUM_DURATION("maximumDurationMs"), + PRODUCER_START_DELAY("producerStartDelayMs"), + PRODUCER_INTERVAL("producerIntervalMs"), + IS_TOPIC("isTopic"), + IS_DURABLE_SUBSCRIPTION("isDurableSubscription"), + IS_BROWSIING_SUBSCRIPTION("isBrowsingSubscription"), + IS_SELECTOR("isSelector"), + IS_NO_LOCAL("isNoLocal"), + IS_SYNCHRONOUS_CONSUMER("isSynchronousConsumer"), + TOTAL_NUMBER_OF_CONSUMERS("totalNumberOfConsumers"), + TOTAL_NUMBER_OF_PRODUCERS("totalNumberOfProducers"), + TOTAL_PAYLOAD_PROCESSED("totalPayloadProcessedB"), + THROUGHPUT("throughputKbPerS"), + TIME_TAKEN("timeTakenMs"), + ERROR_MESSAGE("errorMessage"); + + private String _displayName; + + ParticipantAttribute(String displayName) + { + _displayName = displayName; + } + + public String getDisplayName() + { + return _displayName; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/ParticipantAttributeExtractor.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/ParticipantAttributeExtractor.java new file mode 100644 index 0000000000..95a19ceefc --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/ParticipantAttributeExtractor.java @@ -0,0 +1,89 @@ +/* + * 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.disttest.message; + +import java.beans.PropertyDescriptor; +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.beanutils.PropertyUtils; + + +public class ParticipantAttributeExtractor +{ + public static Map<ParticipantAttribute, Object> getAttributes(Object targetObject) + { + Map<ParticipantAttribute, Object> attributes = new HashMap<ParticipantAttribute, Object>(); + + + PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(targetObject); + for (PropertyDescriptor propertyDescriptor : descriptors) + { + final Method readMethod = getPropertyReadMethod(targetObject, propertyDescriptor); + + for (Annotation annotation : readMethod.getDeclaredAnnotations()) + { + if (annotation instanceof OutputAttribute) + { + OutputAttribute outputAttribute = (OutputAttribute) annotation; + + Object value = getPropertyValue(targetObject, propertyDescriptor.getName()); + attributes.put(outputAttribute.attribute(), value); + } + } + } + + return attributes; + } + + public static Method getPropertyReadMethod(Object targetObject, PropertyDescriptor propertyDescriptor) + { + final Method readMethod = propertyDescriptor.getReadMethod(); + + if (readMethod == null) + { + throw new RuntimeException("No read method for property " + propertyDescriptor.getName() + " on " + targetObject); + } + return readMethod; + } + + public static Object getPropertyValue(Object targetObject, String propertyName) + { + try + { + return PropertyUtils.getProperty(targetObject, propertyName); + } + catch (IllegalAccessException e) + { + throw new RuntimeException("Couldn't get value of property " + propertyName + " from " + targetObject, e); + } + catch (InvocationTargetException e) + { + throw new RuntimeException("Couldn't get value of property " + propertyName + " from " + targetObject, e); + } + catch (NoSuchMethodException e) + { + throw new RuntimeException("Couldn't get value of property " + propertyName + " from " + targetObject, e); + } + + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/ParticipantResult.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/ParticipantResult.java new file mode 100644 index 0000000000..a6d3d91bae --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/ParticipantResult.java @@ -0,0 +1,272 @@ +/* + * 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.disttest.message; + +import static org.apache.qpid.disttest.message.ParticipantAttribute.BATCH_SIZE; +import static org.apache.qpid.disttest.message.ParticipantAttribute.CONFIGURED_CLIENT_NAME; +import static org.apache.qpid.disttest.message.ParticipantAttribute.ITERATION_NUMBER; +import static org.apache.qpid.disttest.message.ParticipantAttribute.MAXIMUM_DURATION; +import static org.apache.qpid.disttest.message.ParticipantAttribute.PAYLOAD_SIZE; +import static org.apache.qpid.disttest.message.ParticipantAttribute.NUMBER_OF_MESSAGES_PROCESSED; +import static org.apache.qpid.disttest.message.ParticipantAttribute.THROUGHPUT; +import static org.apache.qpid.disttest.message.ParticipantAttribute.PARTICIPANT_NAME; +import static org.apache.qpid.disttest.message.ParticipantAttribute.TEST_NAME; + +import java.util.Comparator; +import java.util.Date; +import java.util.Map; + +public class ParticipantResult extends Response +{ + private String _testName; + private String _participantName; + private int _iterationNumber; + + private long _startInMillis; + private long _endInMillis; + private int _batchSize; + private long _maximumDuration; + + private String _configuredClientName; + + private long _numberOfMessagesProcessed; + private long _totalPayloadProcessed; + private int _payloadSize; + private double _throughput; + + private int _totalNumberOfConsumers; + private int _totalNumberOfProducers; + + // As Session.SESSION_TRANSACTED is 0, we use value -1 so we can distinguish the case where an aggregated result + // summarizes results from participants using different session acknowledge modes. + private int _acknowledgeMode = -1; + + public static final Comparator<? super ParticipantResult> PARTICIPANT_NAME_COMPARATOR = new Comparator<ParticipantResult>() + { + @Override + public int compare(ParticipantResult participantResult1, ParticipantResult participantResult2) + { + return participantResult1.getParticipantName().compareTo(participantResult2.getParticipantName()); + } + }; + + public ParticipantResult() + { + this(CommandType.PARTICIPANT_RESULT); + } + + public ParticipantResult(CommandType commandType) + { + super(commandType); + } + + public ParticipantResult(String participantName) + { + this(); + setParticipantName(participantName); + } + + @OutputAttribute(attribute=TEST_NAME) + public String getTestName() + { + return _testName; + } + + public void setTestName(String testName) + { + _testName = testName; + } + + @OutputAttribute(attribute=ITERATION_NUMBER) + public int getIterationNumber() + { + return _iterationNumber; + } + + public void setIterationNumber(int iterationNumber) + { + _iterationNumber = iterationNumber; + } + + public void setStartDate(Date start) + { + _startInMillis = start.getTime(); + } + + public void setEndDate(Date end) + { + _endInMillis = end.getTime(); + } + + public Date getStartDate() + { + return new Date(_startInMillis); + } + + public Date getEndDate() + { + return new Date(_endInMillis); + } + + + public long getStartInMillis() + { + return _startInMillis; + } + + public long getEndInMillis() + { + return _endInMillis; + } + + + @OutputAttribute(attribute=PARTICIPANT_NAME) + public String getParticipantName() + { + return _participantName; + } + + + public void setParticipantName(String participantName) + { + _participantName = participantName; + } + + @OutputAttribute(attribute=ParticipantAttribute.TIME_TAKEN) + public long getTimeTaken() + { + return _endInMillis - _startInMillis; + } + + @OutputAttribute(attribute=CONFIGURED_CLIENT_NAME) + public String getConfiguredClientName() + { + return _configuredClientName; + } + + public void setConfiguredClientName(String configuredClientName) + { + _configuredClientName = configuredClientName; + } + + @OutputAttribute(attribute=NUMBER_OF_MESSAGES_PROCESSED) + public long getNumberOfMessagesProcessed() + { + return _numberOfMessagesProcessed; + } + + public void setNumberOfMessagesProcessed(long numberOfMessagesProcessed) + { + _numberOfMessagesProcessed = numberOfMessagesProcessed; + } + + @OutputAttribute(attribute=ParticipantAttribute.TOTAL_PAYLOAD_PROCESSED) + public long getTotalPayloadProcessed() + { + return _totalPayloadProcessed; + } + + @OutputAttribute(attribute = PAYLOAD_SIZE) + public int getPayloadSize() + { + return _payloadSize; + } + + public void setPayloadSize(int payloadSize) + { + _payloadSize = payloadSize; + } + + public void setTotalPayloadProcessed(long totalPayloadProcessed) + { + _totalPayloadProcessed = totalPayloadProcessed; + } + + public Map<ParticipantAttribute, Object> getAttributes() + { + return ParticipantAttributeExtractor.getAttributes(this); + } + + public void setBatchSize(int batchSize) + { + _batchSize = batchSize; + } + + @OutputAttribute(attribute=BATCH_SIZE) + public int getBatchSize() + { + return _batchSize; + } + + public void setMaximumDuration(long maximumDuration) + { + _maximumDuration = maximumDuration; + } + + @OutputAttribute(attribute=MAXIMUM_DURATION) + public long getMaximumDuration() + { + return _maximumDuration; + } + + @OutputAttribute(attribute=THROUGHPUT) + public double getThroughput() + { + return _throughput; + } + + public void setThroughput(double throughput) + { + _throughput = throughput; + } + + public void setTotalNumberOfConsumers(int totalNumberOfConsumers) + { + _totalNumberOfConsumers = totalNumberOfConsumers; + } + + @OutputAttribute(attribute=ParticipantAttribute.TOTAL_NUMBER_OF_CONSUMERS) + public int getTotalNumberOfConsumers() + { + return _totalNumberOfConsumers; + } + + public void setTotalNumberOfProducers(int totalNumberOfProducers) + { + _totalNumberOfProducers = totalNumberOfProducers; + } + + @OutputAttribute(attribute=ParticipantAttribute.TOTAL_NUMBER_OF_PRODUCERS) + public int getTotalNumberOfProducers() + { + return _totalNumberOfProducers; + } + + @OutputAttribute(attribute=ParticipantAttribute.ACKNOWLEDGE_MODE) + public int getAcknowledgeMode() + { + return _acknowledgeMode; + } + + public void setAcknowledgeMode(int acknowledgeMode) + { + _acknowledgeMode = acknowledgeMode; + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/ProducerParticipantResult.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/ProducerParticipantResult.java new file mode 100644 index 0000000000..766c90eec8 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/ProducerParticipantResult.java @@ -0,0 +1,100 @@ +/* + * 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.disttest.message; + +import static org.apache.qpid.disttest.message.ParticipantAttribute.DELIVERY_MODE; +import static org.apache.qpid.disttest.message.ParticipantAttribute.PRIORITY; +import static org.apache.qpid.disttest.message.ParticipantAttribute.PRODUCER_INTERVAL; +import static org.apache.qpid.disttest.message.ParticipantAttribute.PRODUCER_START_DELAY; +import static org.apache.qpid.disttest.message.ParticipantAttribute.TIME_TO_LIVE; + +public class ProducerParticipantResult extends ParticipantResult +{ + private int _priority; + private long _timeToLive; + private long _startDelay; + private long _interval; + private int _deliveryMode; + public ProducerParticipantResult() + { + super(CommandType.PRODUCER_PARTICIPANT_RESULT); + } + + public ProducerParticipantResult(String participantName) + { + this(); + setParticipantName(participantName); + } + + @OutputAttribute(attribute=PRIORITY) + public int getPriority() + { + return _priority; + } + + public void setPriority(int priority) + { + _priority = priority; + } + + @OutputAttribute(attribute=TIME_TO_LIVE) + public long getTimeToLive() + { + return _timeToLive; + } + + public void setTimeToLive(long timeToLive) + { + _timeToLive = timeToLive; + } + + @OutputAttribute(attribute=PRODUCER_START_DELAY) + public long getStartDelay() + { + return _startDelay; + } + + public void setStartDelay(long startDelay) + { + _startDelay = startDelay; + } + + @OutputAttribute(attribute=PRODUCER_INTERVAL) + public long getInterval() + { + return _interval; + } + + public void setInterval(long producerInterval) + { + _interval = producerInterval; + } + + @OutputAttribute(attribute=DELIVERY_MODE) + public int getDeliveryMode() + { + return _deliveryMode; + } + + public void setDeliveryMode(int deliveryMode) + { + this._deliveryMode = deliveryMode; + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/RegisterClientCommand.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/RegisterClientCommand.java new file mode 100644 index 0000000000..af880a37d9 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/RegisterClientCommand.java @@ -0,0 +1,43 @@ +/* + * 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.disttest.message; + +public class RegisterClientCommand extends Command +{ + private final String _clientName; + private final String _clientQueueName; + + public RegisterClientCommand(final String clientName, final String clientQueueName) + { + super(CommandType.REGISTER_CLIENT); + _clientName = clientName; + _clientQueueName = clientQueueName; + } + + public String getClientName() + { + return _clientName; + } + + public String getClientQueueName() + { + return _clientQueueName; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/Response.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/Response.java new file mode 100644 index 0000000000..aac056efcb --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/Response.java @@ -0,0 +1,80 @@ +/* + * 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.disttest.message; + + +public class Response extends Command +{ + protected String _registeredClientName; + protected String _errorMessage; + private CommandType _inReplyToCommandType; + + public Response(final String registeredclientName, final CommandType inReplyToCommandType, final String errorMessage) + { + super(CommandType.RESPONSE); + _registeredClientName = registeredclientName; + _errorMessage = errorMessage; + _inReplyToCommandType = inReplyToCommandType; + } + + public Response(String clientName, CommandType inReplyToCommandType) + { + this(clientName, inReplyToCommandType, null); + } + + /** + * Provided so that subclasses can call super(commandType) + */ + protected Response(CommandType commandType) + { + super(commandType); + } + + public String getRegisteredClientName() + { + return _registeredClientName; + } + + public void setRegisteredClientName(String registeredClientName) + { + _registeredClientName = registeredClientName; + } + + @OutputAttribute(attribute=ParticipantAttribute.ERROR_MESSAGE) + public String getErrorMessage() + { + return _errorMessage; + } + + public void setErrorMessage(String errorMessage) + { + _errorMessage = errorMessage; + } + + public boolean hasError() + { + return _errorMessage != null; + } + + public CommandType getInReplyToCommandType() + { + return _inReplyToCommandType; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/StartTestCommand.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/StartTestCommand.java new file mode 100644 index 0000000000..4a53697ecd --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/StartTestCommand.java @@ -0,0 +1,29 @@ +/* + * 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.disttest.message; + +public class StartTestCommand extends Command +{ + + public StartTestCommand() + { + super(CommandType.START_TEST); + } + +}
\ No newline at end of file diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/StopClientCommand.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/StopClientCommand.java new file mode 100644 index 0000000000..08758aaa69 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/StopClientCommand.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.disttest.message; + +public class StopClientCommand extends Command +{ + public StopClientCommand() + { + super(CommandType.STOP_CLIENT); + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/message/TearDownTestCommand.java b/java/perftests/src/main/java/org/apache/qpid/disttest/message/TearDownTestCommand.java new file mode 100644 index 0000000000..6b1367d4f9 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/message/TearDownTestCommand.java @@ -0,0 +1,29 @@ +/* + * 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.disttest.message; + +public class TearDownTestCommand extends Command +{ + + public TearDownTestCommand() + { + super(CommandType.TEAR_DOWN_TEST); + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/results/aggregation/AggregatedTestResult.java b/java/perftests/src/main/java/org/apache/qpid/disttest/results/aggregation/AggregatedTestResult.java new file mode 100644 index 0000000000..5e6da2e65b --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/results/aggregation/AggregatedTestResult.java @@ -0,0 +1,97 @@ +/* + * 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.disttest.results.aggregation; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.qpid.disttest.message.ParticipantResult; + +public class AggregatedTestResult implements ITestResult +{ + private ParticipantResult _allParticipantResult; + private ParticipantResult _allConsumerParticipantResult; + private ParticipantResult _allProducerParticipantResult; + private final ITestResult _originalTestResult; + + public AggregatedTestResult(ITestResult originalTestResult) + { + _originalTestResult = originalTestResult; + } + + /** + * Returns the result where {@link ParticipantResult#getNumberOfMessagesProcessed()} + * is the total number of messages consumed during the test, and {@link ParticipantResult#getTimeTaken()} + * is the time between the start of the first producer and the end of the last consumer to finish. + */ + public ParticipantResult getAllParticipantResult() + { + return _allParticipantResult; + } + + public void setAllParticipantResult(ParticipantResult allParticipantResult) + { + _allParticipantResult = allParticipantResult; + } + + public ParticipantResult getAllConsumerParticipantResult() + { + return _allConsumerParticipantResult; + } + public void setAllConsumerParticipantResult(ParticipantResult allConsumerParticipantResult) + { + _allConsumerParticipantResult = allConsumerParticipantResult; + } + public ParticipantResult getAllProducerParticipantResult() + { + return _allProducerParticipantResult; + } + public void setAllProducerParticipantResult(ParticipantResult allProducerParticipantResult) + { + _allProducerParticipantResult = allProducerParticipantResult; + } + + // TODO should weaken to Collection + @Override + public List<ParticipantResult> getParticipantResults() + { + List<ParticipantResult> allParticipantResults = new ArrayList<ParticipantResult>(_originalTestResult.getParticipantResults()); + + allParticipantResults.add(_allConsumerParticipantResult); + allParticipantResults.add(_allProducerParticipantResult); + allParticipantResults.add(_allParticipantResult); + + return allParticipantResults; + } + + @Override + public boolean hasErrors() + { + return _originalTestResult.hasErrors(); + } + + @Override + public String getName() + { + return _originalTestResult.getName(); + } + + + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/results/aggregation/Aggregator.java b/java/perftests/src/main/java/org/apache/qpid/disttest/results/aggregation/Aggregator.java new file mode 100644 index 0000000000..cde30d36e5 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/results/aggregation/Aggregator.java @@ -0,0 +1,49 @@ +/* + * 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.disttest.results.aggregation; + +import org.apache.qpid.disttest.controller.ResultsForAllTests; + +public class Aggregator +{ + + private TestResultAggregator _testResultAggregator = new TestResultAggregator(); + + public ResultsForAllTests aggregateResults(ResultsForAllTests rawResultsForAllTests) + { + + ResultsForAllTests aggregatedResultsForAllTests = new ResultsForAllTests(); + + + for (ITestResult testResult : rawResultsForAllTests.getTestResults()) + { + AggregatedTestResult aggregateTestResult = _testResultAggregator.aggregateTestResult(testResult); + aggregatedResultsForAllTests.add(aggregateTestResult); + } + + + return aggregatedResultsForAllTests; + } + + void setTestResultAggregator(TestResultAggregator testResultAggregator) + { + _testResultAggregator = testResultAggregator; + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/results/aggregation/ITestResult.java b/java/perftests/src/main/java/org/apache/qpid/disttest/results/aggregation/ITestResult.java new file mode 100644 index 0000000000..3f9cdff69d --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/results/aggregation/ITestResult.java @@ -0,0 +1,36 @@ +/* + * 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.disttest.results.aggregation; + +import java.util.List; + +import org.apache.qpid.disttest.message.ParticipantResult; + +// TODO rename me!! +public interface ITestResult +{ + + // TODO should weaken to Collection + List<ParticipantResult> getParticipantResults(); + + boolean hasErrors(); + + String getName(); + +}
\ No newline at end of file diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/results/aggregation/ParticipantResultAggregator.java b/java/perftests/src/main/java/org/apache/qpid/disttest/results/aggregation/ParticipantResultAggregator.java new file mode 100644 index 0000000000..207d0131eb --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/results/aggregation/ParticipantResultAggregator.java @@ -0,0 +1,143 @@ +/* + * 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.disttest.results.aggregation; + +import java.util.Date; +import java.util.NavigableSet; +import java.util.TreeSet; + +import org.apache.qpid.disttest.message.ParticipantResult; + +public class ParticipantResultAggregator +{ + private final String _aggregatedResultName; + private final Class<? extends ParticipantResult> _targetClass; + + private long _minStartDate = Long.MAX_VALUE; + private long _maxEndDate = 0; + private long _numberOfMessagesProcessed = 0; + private long _totalPayloadProcessed = 0; + + private int _totalNumberOfConsumers = 0; + private int _totalNumberOfProducers = 0; + + private NavigableSet<Integer> _encounteredPayloadSizes = new TreeSet<Integer>(); + private NavigableSet<Integer> _encounteredIterationNumbers = new TreeSet<Integer>(); + private NavigableSet<Integer> _encounteredBatchSizes = new TreeSet<Integer>(); + private NavigableSet<Integer> _encounteredAcknowledgeMode = new TreeSet<Integer>(); + private NavigableSet<String> _encountedTestNames = new TreeSet<String>(); + + public ParticipantResultAggregator(Class<? extends ParticipantResult> targetClass, String aggregateResultName) + { + _aggregatedResultName = aggregateResultName; + _targetClass = targetClass; + } + + public void aggregate(ParticipantResult result) + { + if (isAggregatable(result)) + { + rollupConstantAttributes(result); + computeVariableAttributes(result); + } + } + + public ParticipantResult getAggregatedResult() + { + ParticipantResult aggregatedResult = new ParticipantResult(_aggregatedResultName); + + setRolledUpConstantAttributes(aggregatedResult); + setComputedVariableAttributes(aggregatedResult); + + return aggregatedResult; + } + + private boolean isAggregatable(ParticipantResult result) + { + return _targetClass.isAssignableFrom(result.getClass()); + } + + private void computeVariableAttributes(ParticipantResult result) + { + _numberOfMessagesProcessed += result.getNumberOfMessagesProcessed(); + _totalPayloadProcessed += result.getTotalPayloadProcessed(); + _totalNumberOfConsumers += result.getTotalNumberOfConsumers(); + _totalNumberOfProducers += result.getTotalNumberOfProducers(); + _minStartDate = Math.min(_minStartDate, result.getStartInMillis()); + _maxEndDate = Math.max(_maxEndDate, result.getEndInMillis()); + } + + private void rollupConstantAttributes(ParticipantResult result) + { + if (result.getTestName() != null) + { + _encountedTestNames.add(result.getTestName()); + } + _encounteredPayloadSizes.add(result.getPayloadSize()); + _encounteredIterationNumbers.add(result.getIterationNumber()); + _encounteredBatchSizes.add(result.getBatchSize()); + _encounteredAcknowledgeMode.add(result.getAcknowledgeMode()); + } + + private void setComputedVariableAttributes(ParticipantResult aggregatedResult) + { + aggregatedResult.setNumberOfMessagesProcessed(_numberOfMessagesProcessed); + aggregatedResult.setTotalPayloadProcessed(_totalPayloadProcessed); + aggregatedResult.setTotalNumberOfConsumers(_totalNumberOfConsumers); + aggregatedResult.setTotalNumberOfProducers(_totalNumberOfProducers); + aggregatedResult.setStartDate(new Date(_minStartDate)); + aggregatedResult.setEndDate(new Date(_maxEndDate)); + aggregatedResult.setThroughput(calculateThroughputInKiloBytesPerSecond()); + } + + private void setRolledUpConstantAttributes(ParticipantResult aggregatedResult) + { + if (_encounteredIterationNumbers.size() == 1) + { + aggregatedResult.setIterationNumber( _encounteredIterationNumbers.first()); + } + if (_encounteredPayloadSizes.size() == 1) + { + aggregatedResult.setPayloadSize(_encounteredPayloadSizes.first()); + } + if (_encountedTestNames.size() == 1) + { + aggregatedResult.setTestName(_encountedTestNames.first()); + } + if (_encounteredBatchSizes.size() == 1) + { + aggregatedResult.setBatchSize(_encounteredBatchSizes.first()); + } + if (_encounteredAcknowledgeMode.size() == 1) + { + aggregatedResult.setAcknowledgeMode(_encounteredAcknowledgeMode.first()); + } + } + + private double calculateThroughputInKiloBytesPerSecond() + { + double durationInMillis = _maxEndDate - _minStartDate; + double durationInSeconds = durationInMillis / 1000; + double totalPayloadProcessedInKiloBytes = ((double)_totalPayloadProcessed) / 1024; + + return totalPayloadProcessedInKiloBytes/durationInSeconds; + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/results/aggregation/TestResultAggregator.java b/java/perftests/src/main/java/org/apache/qpid/disttest/results/aggregation/TestResultAggregator.java new file mode 100644 index 0000000000..5934e0e997 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/results/aggregation/TestResultAggregator.java @@ -0,0 +1,106 @@ +/* + * 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.disttest.results.aggregation; + +import org.apache.qpid.disttest.message.ConsumerParticipantResult; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.disttest.message.ProducerParticipantResult; + +public class TestResultAggregator +{ + static final String AGGREGATED_ERROR_MESSAGE = "One or more participants reported errors."; + public static final String ALL_PARTICIPANTS_NAME = "All"; + public static final String ALL_PRODUCER_PARTICIPANTS_NAME = "All Producers"; + public static final String ALL_CONSUMER_PARTICIPANTS_NAME = "All Consumers"; + + public AggregatedTestResult aggregateTestResult(ITestResult originalTestResult) + { + ParticipantResultAggregator consumerResultAggregator = new ParticipantResultAggregator(ConsumerParticipantResult.class, + ALL_CONSUMER_PARTICIPANTS_NAME); + ParticipantResultAggregator producerResultAggregator = new ParticipantResultAggregator(ProducerParticipantResult.class, + ALL_PRODUCER_PARTICIPANTS_NAME); + ParticipantResultAggregator aggregatedResultsAggregator = new ParticipantResultAggregator(ParticipantResult.class, + ALL_PARTICIPANTS_NAME); + + boolean hasError = aggregateOriginalResults(originalTestResult, + consumerResultAggregator, + producerResultAggregator); + + ParticipantResult aggregatedProducerResult = producerResultAggregator.getAggregatedResult(); + ParticipantResult aggregaredConsumerResult = consumerResultAggregator.getAggregatedResult(); + + ParticipantResult aggregatedAllResult = aggregateAggregatedResults( + aggregatedResultsAggregator, aggregatedProducerResult, + aggregaredConsumerResult); + + applyNonAggregateablesToAll(aggregatedAllResult, + aggregatedProducerResult, aggregaredConsumerResult); + + AggregatedTestResult newTestResult = new AggregatedTestResult(originalTestResult); + newTestResult.setAllProducerParticipantResult(aggregatedProducerResult); + newTestResult.setAllConsumerParticipantResult(aggregaredConsumerResult); + newTestResult.setAllParticipantResult(aggregatedAllResult); + + if (hasError) + { + aggregatedAllResult.setErrorMessage(TestResultAggregator.AGGREGATED_ERROR_MESSAGE); + } + + return newTestResult; + } + + private ParticipantResult aggregateAggregatedResults( + ParticipantResultAggregator aggregatedResultsAggregator, + ParticipantResult aggregatedProducerResult, + ParticipantResult aggregaredConsumerResult) + { + aggregatedResultsAggregator.aggregate(aggregatedProducerResult); + aggregatedResultsAggregator.aggregate(aggregaredConsumerResult); + ParticipantResult aggregatedAllResult = aggregatedResultsAggregator.getAggregatedResult(); + return aggregatedAllResult; + } + + private boolean aggregateOriginalResults(ITestResult originalTestResult, + ParticipantResultAggregator consumerParticipantResultAggregator, + ParticipantResultAggregator producerParticipantResultAggregator) + { + boolean hasError = false; + for (ParticipantResult result : originalTestResult.getParticipantResults()) + { + consumerParticipantResultAggregator.aggregate(result); + producerParticipantResultAggregator.aggregate(result); + + if (result.hasError()) + { + hasError = true; + } + } + return hasError; + } + + private void applyNonAggregateablesToAll(ParticipantResult aggregatedAllResult, ParticipantResult aggregatedProducerResult, ParticipantResult aggregatedConsumerResult) + { + aggregatedAllResult.setStartDate(aggregatedProducerResult.getStartDate()); + aggregatedAllResult.setEndDate(aggregatedConsumerResult.getEndDate()); + + aggregatedAllResult.setNumberOfMessagesProcessed(aggregatedConsumerResult.getNumberOfMessagesProcessed()); + aggregatedAllResult.setTotalPayloadProcessed(aggregatedConsumerResult.getTotalPayloadProcessed()); + aggregatedAllResult.setThroughput(aggregatedConsumerResult.getThroughput()); + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/results/formatting/CSVFormater.java b/java/perftests/src/main/java/org/apache/qpid/disttest/results/formatting/CSVFormater.java new file mode 100644 index 0000000000..52e53ca624 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/results/formatting/CSVFormater.java @@ -0,0 +1,89 @@ +/* + * 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.disttest.results.formatting; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; +import org.apache.qpid.disttest.controller.ResultsForAllTests; +import org.apache.qpid.disttest.message.ParticipantAttribute; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.disttest.results.aggregation.ITestResult; + +/** + * produces CSV output using the ordered enums in {@link ParticipantAttribute} + */ +public class CSVFormater +{ + public String format(ResultsForAllTests results) + { + StringBuilder builder = new StringBuilder(); + + builder.append(header()); + + List<ITestResult> testResults = results.getTestResults(); + + for (ITestResult testResult : testResults) + { + + List<ParticipantResult> participantResults = new ArrayList<ParticipantResult>(testResult.getParticipantResults()); + Collections.sort(participantResults, new CSVOrderParticipantResultComparator()); + + for (ParticipantResult participantResult : participantResults) + { + Map<ParticipantAttribute, Object> attributes = participantResult.getAttributes(); + builder.append(row(attributes)); + } + } + + return builder.toString(); + } + + /** + * return a row, including a newline character at the end + */ + private String row(Map<ParticipantAttribute, Object> attributeValueMap) + { + List<Object> attributeValues = new ArrayList<Object>(); + for (ParticipantAttribute attribute : ParticipantAttribute.values()) + { + attributeValues.add(attributeValueMap.get(attribute)); + } + + String row = StringUtils.join(attributeValues.toArray(), ","); + return row + "\n"; + } + + /** return the header row, including a newline at the end */ + private String header() + { + List<String> displayNames = new ArrayList<String>(); + for (ParticipantAttribute attribute : ParticipantAttribute.values()) + { + displayNames.add(attribute.getDisplayName()); + } + + String header = StringUtils.join(displayNames.toArray(), ","); + return header + "\n"; + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/disttest/results/formatting/CSVOrderParticipantResultComparator.java b/java/perftests/src/main/java/org/apache/qpid/disttest/results/formatting/CSVOrderParticipantResultComparator.java new file mode 100644 index 0000000000..0e1fbbc3c6 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/disttest/results/formatting/CSVOrderParticipantResultComparator.java @@ -0,0 +1,55 @@ +/* + * 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.disttest.results.formatting; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang.builder.CompareToBuilder; +import org.apache.qpid.disttest.message.ConsumerParticipantResult; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.disttest.message.ProducerParticipantResult; + +public class CSVOrderParticipantResultComparator implements Comparator<ParticipantResult> +{ + // TODO yuk + private static final Map<Class<? extends ParticipantResult>, Integer> TYPE_CODES = new HashMap<Class<? extends ParticipantResult>, Integer>(); + static { + TYPE_CODES.put(ProducerParticipantResult.class, 0); + TYPE_CODES.put(ConsumerParticipantResult.class, 1); + TYPE_CODES.put(ParticipantResult.class, 2); + } + + @Override + public int compare(ParticipantResult left, ParticipantResult right) + { + return new CompareToBuilder() + .append(getTypeCode(left), getTypeCode(right)) + .append(left.getParticipantName(), right.getParticipantName()) + .toComparison(); + } + + + private int getTypeCode(ParticipantResult participantResult) + { + return TYPE_CODES.get(participantResult.getClass()); + } + +} diff --git a/java/perftests/src/main/java/org/apache/qpid/oldtopic/Config.java b/java/perftests/src/main/java/org/apache/qpid/oldtopic/Config.java deleted file mode 100644 index 5b6169ed2d..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/oldtopic/Config.java +++ /dev/null @@ -1,243 +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.oldtopic; - -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.config.ConnectorConfig; -import org.apache.qpid.config.ConnectionFactoryInitialiser; -import org.apache.qpid.config.Connector; -import org.apache.qpid.config.AbstractConfig; - -import javax.jms.Connection; -import javax.jms.ConnectionFactory; - -class Config extends AbstractConfig implements ConnectorConfig -{ - - private String host = "localhost"; - private int port = 5672; - private String factory = null; - - private int payload = 256; - private int messages = 1000; - private int clients = 1; - private int batch = 1; - private long delay = 1; - private int warmup; - private int ackMode= AMQSession.NO_ACKNOWLEDGE; - private String clientId; - private String subscriptionId; - private boolean persistent; - - public Config() - { - } - - int getAckMode() - { - return ackMode; - } - - void setPayload(int payload) - { - this.payload = payload; - } - - int getPayload() - { - return payload; - } - - void setClients(int clients) - { - this.clients = clients; - } - - int getClients() - { - return clients; - } - - void setMessages(int messages) - { - this.messages = messages; - } - - int getMessages() - { - return messages; - } - - public String getHost() - { - return host; - } - - public void setHost(String host) - { - this.host = host; - } - - public int getPort() - { - return port; - } - - public String getFactory() - { - return factory; - } - - public void setPort(int port) - { - this.port = port; - } - - int getBatch() - { - return batch; - } - - void setBatch(int batch) - { - this.batch = batch; - } - - int getWarmup() - { - return warmup; - } - - void setWarmup(int warmup) - { - this.warmup = warmup; - } - - public long getDelay() - { - return delay; - } - - public void setDelay(long delay) - { - this.delay = delay; - } - - String getClientId() - { - return clientId; - } - - String getSubscriptionId() - { - return subscriptionId; - } - - boolean usePersistentMessages() - { - return persistent; - } - - public void setOption(String key, String value) - { - if("-host".equalsIgnoreCase(key)) - { - setHost(value); - } - else if("-port".equalsIgnoreCase(key)) - { - try - { - setPort(Integer.parseInt(value)); - } - catch(NumberFormatException e) - { - throw new RuntimeException("Bad port number: " + value); - } - } - else if("-payload".equalsIgnoreCase(key)) - { - setPayload(parseInt("Bad payload size", value)); - } - else if("-messages".equalsIgnoreCase(key)) - { - setMessages(parseInt("Bad message count", value)); - } - else if("-clients".equalsIgnoreCase(key)) - { - setClients(parseInt("Bad client count", value)); - } - else if("-batch".equalsIgnoreCase(key)) - { - setBatch(parseInt("Bad batch count", value)); - } - else if("-delay".equalsIgnoreCase(key)) - { - setDelay(parseLong("Bad batch delay", value)); - } - else if("-warmup".equalsIgnoreCase(key)) - { - setWarmup(parseInt("Bad warmup count", value)); - } - else if("-ack".equalsIgnoreCase(key)) - { - ackMode = parseInt("Bad ack mode", value); - } - else if("-factory".equalsIgnoreCase(key)) - { - factory = value; - } - else if("-clientId".equalsIgnoreCase(key)) - { - clientId = value; - } - else if("-subscriptionId".equalsIgnoreCase(key)) - { - subscriptionId = value; - } - else if("-persistent".equalsIgnoreCase(key)) - { - persistent = "true".equalsIgnoreCase(value); - } - else - { - System.out.println("Ignoring unrecognised option: " + key); - } - } - - static String getAckModeDescription(int ackMode) - { - switch(ackMode) - { - case AMQSession.NO_ACKNOWLEDGE: return "NO_ACKNOWLEDGE"; - case AMQSession.AUTO_ACKNOWLEDGE: return "AUTO_ACKNOWLEDGE"; - case AMQSession.CLIENT_ACKNOWLEDGE: return "CLIENT_ACKNOWLEDGE"; - case AMQSession.DUPS_OK_ACKNOWLEDGE: return "DUPS_OK_ACKNOWELDGE"; - case AMQSession.PRE_ACKNOWLEDGE: return "PRE_ACKNOWLEDGE"; - } - return "AckMode=" + ackMode; - } - - public Connection createConnection() throws Exception - { - return new Connector().createConnection(this); - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/oldtopic/Listener.java b/java/perftests/src/main/java/org/apache/qpid/oldtopic/Listener.java deleted file mode 100644 index 4732782d4c..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/oldtopic/Listener.java +++ /dev/null @@ -1,141 +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.oldtopic; -import org.apache.log4j.*; -import javax.jms.Connection; -import javax.jms.Message; -import javax.jms.MessageListener; -import javax.jms.MessageProducer; -import javax.jms.Session; - -public class Listener implements MessageListener -{ - private final Connection _connection; - private final MessageProducer _controller; - private final javax.jms.Session _session; - private final MessageFactory _factory; - private boolean init; - private int count; - private long start; - - Listener(Connection connection, int ackMode) throws Exception - { - this(connection, ackMode, null); - } - - Listener(Connection connection, int ackMode, String name) throws Exception - { - _connection = connection; - _session = connection.createSession(false, ackMode); - _factory = new MessageFactory(_session); - - //register for events - if(name == null) - { - _factory.createTopicConsumer().setMessageListener(this); - } - else - { - _factory.createDurableTopicConsumer(name).setMessageListener(this); - } - - _connection.start(); - - _controller = _factory.createControlPublisher(); - System.out.println("Waiting for messages " + - Config.getAckModeDescription(ackMode) - + (name == null ? "" : " (subscribed with name " + name + " and client id " + connection.getClientID() + ")") - + "..."); - - } - - private void shutdown() - { - try - { - _session.close(); - _connection.stop(); - _connection.close(); - } - catch(Exception e) - { - e.printStackTrace(System.out); - } - } - - private void report() - { - try - { - String msg = getReport(); - _controller.send(_factory.createReportResponseMessage(msg)); - System.out.println("Sent report: " + msg); - } - catch(Exception e) - { - e.printStackTrace(System.out); - } - } - - private String getReport() - { - long time = (System.currentTimeMillis() - start); - return "Received " + count + " in " + time + "ms"; - } - - public void onMessage(Message message) - { - if(!init) - { - start = System.currentTimeMillis(); - count = 0; - init = true; - } - - if(_factory.isShutdown(message)) - { - shutdown(); - } - else if(_factory.isReport(message)) - { - //send a report: - report(); - init = false; - } - else if (++count % 100 == 0) - { - System.out.println("Received " + count + " messages."); - } - } - - public static void main(String[] argv) throws Exception - { - Config config = new Config(); - config.setOptions(argv); - - Connection con = config.createConnection(); - if(config.getClientId() != null) - { - con.setClientID(config.getClientId()); - } - new Listener(con, config.getAckMode(), config.getSubscriptionId()); - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/oldtopic/MessageFactory.java b/java/perftests/src/main/java/org/apache/qpid/oldtopic/MessageFactory.java deleted file mode 100644 index b2fbeb7e35..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/oldtopic/MessageFactory.java +++ /dev/null @@ -1,153 +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.oldtopic; - -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.client.AMQTopic; - -import javax.jms.*; - -/** - */ -class MessageFactory -{ - private static final char[] DATA = "abcdefghijklmnopqrstuvwxyz".toCharArray(); - - private final Session _session; - private final Topic _topic; - private final Topic _control; - private final byte[] _payload; - - - MessageFactory(Session session) throws JMSException - { - this(session, 256); - } - - MessageFactory(Session session, int size) throws JMSException - { - _session = session; -/* if(session instanceof AMQSession) - { - _topic = new AMQTopic("topictest.messages"); - _control = new AMQTopic("topictest.control"); - } - else*/ - { - _topic = session.createTopic("topictest.messages"); - _control = session.createTopic("topictest.control"); - } - _payload = new byte[size]; - - for(int i = 0; i < size; i++) - { - _payload[i] = (byte) DATA[i % DATA.length]; - } - } - - Topic getTopic() - { - return _topic; - } - - Message createEventMessage() throws JMSException - { - BytesMessage msg = _session.createBytesMessage(); - msg.writeBytes(_payload); - return msg; - } - - Message createShutdownMessage() throws JMSException - { - return _session.createTextMessage("SHUTDOWN"); - } - - Message createReportRequestMessage() throws JMSException - { - return _session.createTextMessage("REPORT"); - } - - Message createReportResponseMessage(String msg) throws JMSException - { - return _session.createTextMessage(msg); - } - - boolean isShutdown(Message m) - { - return checkText(m, "SHUTDOWN"); - } - - boolean isReport(Message m) - { - return checkText(m, "REPORT"); - } - - Object getReport(Message m) - { - try - { - return ((TextMessage) m).getText(); - } - catch (JMSException e) - { - e.printStackTrace(System.out); - return e.toString(); - } - } - - MessageConsumer createTopicConsumer() throws Exception - { - return _session.createConsumer(_topic); - } - - MessageConsumer createDurableTopicConsumer(String name) throws Exception - { - return _session.createDurableSubscriber(_topic, name); - } - - MessageConsumer createControlConsumer() throws Exception - { - return _session.createConsumer(_control); - } - - MessageProducer createTopicPublisher() throws Exception - { - return _session.createProducer(_topic); - } - - MessageProducer createControlPublisher() throws Exception - { - return _session.createProducer(_control); - } - - private static boolean checkText(Message m, String s) - { - try - { - return m instanceof TextMessage && ((TextMessage) m).getText().equals(s); - } - catch (JMSException e) - { - e.printStackTrace(System.out); - return false; - } - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/oldtopic/Publisher.java b/java/perftests/src/main/java/org/apache/qpid/oldtopic/Publisher.java deleted file mode 100644 index 841fcc63ad..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/oldtopic/Publisher.java +++ /dev/null @@ -1,178 +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.oldtopic; - -import javax.jms.*; - -public class Publisher implements MessageListener -{ - private final Object _lock = new Object(); - private final Connection _connection; - private final Session _session; - private final MessageFactory _factory; - private final MessageProducer _publisher; - private int _count; - - Publisher(Connection connection, int size, int ackMode, boolean persistent) throws Exception - { - _connection = connection; - _session = _connection.createSession(false, ackMode); - _factory = new MessageFactory(_session, size); - _publisher = _factory.createTopicPublisher(); - _publisher.setDeliveryMode(persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT); - System.out.println("Publishing " + (persistent ? "persistent" : "non-persistent") + " messages of " + size + " bytes, " + Config.getAckModeDescription(ackMode) + "."); - } - - private void test(Config config) throws Exception - { - test(config.getBatch(), config.getDelay(), config.getMessages(), config.getClients(), config.getWarmup()); - } - - private void test(int batches, long delay, int msgCount, int consumerCount, int warmup) throws Exception - { - _factory.createControlConsumer().setMessageListener(this); - _connection.start(); - - if(warmup > 0) - { - System.out.println("Runing warmup (" + warmup + " msgs)"); - long time = batch(warmup, consumerCount); - System.out.println("Warmup completed in " + time + "ms"); - } - - long[] times = new long[batches]; - for(int i = 0; i < batches; i++) - { - if(i > 0) - { - Thread.sleep(delay*1000); - } - times[i] = batch(msgCount, consumerCount); - System.out.println("Batch " + (i+1) + " of " + batches + " completed in " + times[i] + " ms."); - } - - long min = min(times); - long max = max(times); - System.out.println("min: " + min + ", max: " + max + " avg: " + avg(times, min, max)); - - //request shutdown - _publisher.send(_factory.createShutdownMessage()); - - _connection.stop(); - _connection.close(); - } - - private long batch(int msgCount, int consumerCount) throws Exception - { - _count = consumerCount; - long start = System.currentTimeMillis(); - publish(msgCount); - waitForCompletion(consumerCount); - return System.currentTimeMillis() - start; - } - - private void publish(int count) throws Exception - { - - //send events - for (int i = 0; i < count; i++) - { - _publisher.send(_factory.createEventMessage()); - if ((i + 1) % 100 == 0) - { - System.out.println("Sent " + (i + 1) + " messages"); - } - } - - //request report - _publisher.send(_factory.createReportRequestMessage()); - } - - private void waitForCompletion(int consumers) throws Exception - { - System.out.println("Waiting for completion..."); - synchronized (_lock) - { - while (_count > 0) - { - _lock.wait(); - } - } - } - - - public void onMessage(Message message) - { - System.out.println("Received report " + _factory.getReport(message) + " " + --_count + " remaining"); - if (_count == 0) - { - synchronized (_lock) - { - _lock.notify(); - } - } - } - - static long min(long[] times) - { - long min = times.length > 0 ? times[0] : 0; - for(int i = 0; i < times.length; i++) - { - min = Math.min(min, times[i]); - } - return min; - } - - static long max(long[] times) - { - long max = times.length > 0 ? times[0] : 0; - for(int i = 0; i < times.length; i++) - { - max = Math.max(max, times[i]); - } - return max; - } - - static long avg(long[] times, long min, long max) - { - long sum = 0; - for(int i = 0; i < times.length; i++) - { - sum += times[i]; - } - sum -= min; - sum -= max; - - return (sum / (times.length - 2)); - } - - public static void main(String[] argv) throws Exception - { - Config config = new Config(); - config.setOptions(argv); - - Connection con = config.createConnection(); - int size = config.getPayload(); - int ackMode = config.getAckMode(); - boolean persistent = config.usePersistentMessages(); - new Publisher(con, size, ackMode, persistent).test(config); - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/ping/PingAsyncTestPerf.java b/java/perftests/src/main/java/org/apache/qpid/ping/PingAsyncTestPerf.java deleted file mode 100644 index b55dac45c7..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/ping/PingAsyncTestPerf.java +++ /dev/null @@ -1,323 +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.ping; - -import junit.framework.Test; -import junit.framework.TestSuite; - -import org.apache.log4j.Logger; - -import org.apache.qpid.requestreply.PingPongProducer; - -import org.apache.qpid.junit.extensions.TimingController; -import org.apache.qpid.junit.extensions.TimingControllerAware; - -import javax.jms.JMSException; -import javax.jms.Message; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; - -/** - * PingAsyncTestPerf is a performance test that outputs multiple timings from its test method, using the timing controller - * interface supplied by the test runner from a seperate listener thread. It differs from the {@link PingTestPerf} test - * that it extends because it can output timings as replies are received, rather than waiting until all expected replies - * are received. This is less 'blocky' than the tests in {@link PingTestPerf}, and provides a truer simulation of sending - * and recieving clients working asynchronously. - * - * <p/><table id="crc"><caption>CRC Card</caption> - * <tr><td> Responsibilities <th> Collaborations - * <tr><td> Send many ping messages and output timings asynchronously on batches received. - * </table> - */ -public class PingAsyncTestPerf extends PingTestPerf implements TimingControllerAware -{ - private static Logger _logger = Logger.getLogger(PingAsyncTestPerf.class); - - /** Holds the name of the property to get the test results logging batch size. */ - public static final String TEST_RESULTS_BATCH_SIZE_PROPNAME = "batchSize"; - - /** Holds the default test results logging batch size. */ - public static final int TEST_RESULTS_BATCH_SIZE_DEFAULT = 1000; - - /** Used to hold the timing controller passed from the test runner. */ - private TimingController _timingController; - - /** Used to generate unique correlation ids for each test run. */ - private AtomicLong corellationIdGenerator = new AtomicLong(); - - /** Holds test specifics by correlation id. This consists of the expected number of messages and the timing controler. */ - private Map<String, PerCorrelationId> perCorrelationIds = - Collections.synchronizedMap(new HashMap<String, PerCorrelationId>()); - - /** Holds the batched results listener, that does logging on batch boundaries. */ - private BatchedResultsListener batchedResultsListener = null; - - /** - * Creates a new asynchronous ping performance test with the specified name. - * - * @param name The test name. - */ - public PingAsyncTestPerf(String name) - { - super(name); - - // Sets up the test parameters with defaults. - testParameters.setPropertyIfNull(TEST_RESULTS_BATCH_SIZE_PROPNAME, - Integer.toString(TEST_RESULTS_BATCH_SIZE_DEFAULT)); - } - - /** - * Compile all the tests into a test suite. - * @return The test suite to run. Should only contain testAsyncPingOk method. - */ - public static Test suite() - { - // Build a new test suite - TestSuite suite = new TestSuite("Ping Performance Tests"); - - // Run performance tests in read committed mode. - suite.addTest(new PingAsyncTestPerf("testAsyncPingOk")); - - return suite; - } - - /** - * Accepts a timing controller from the test runner. - * - * @param timingController The timing controller to register mutliple timings with. - */ - public void setTimingController(TimingController timingController) - { - _timingController = timingController; - } - - /** - * Gets the timing controller passed in by the test runner. - * - * @return The timing controller passed in by the test runner. - */ - public TimingController getTimingController() - { - return _timingController; - } - - /** - * Sends the specified number of pings, asynchronously outputs timings on every batch boundary, and waits until - * all replies have been received or a time out occurs before exiting this method. - * - * @param numPings The number of pings to send. - * @throws Exception pass all errors out to the test harness - */ - public void testAsyncPingOk(int numPings) throws Exception - { - // _logger.debug("public void testAsyncPingOk(int numPings): called"); - - // get prefill count to update the expected count - int preFill = testParameters.getPropertyAsInteger(PingPongProducer.PREFILL_PROPNAME); - - // Ensure that at least one ping was requeusted. - if (numPings + preFill == 0) - { - _logger.error("Number of pings requested was zero."); - fail("Number of pings requested was zero."); - } - - // Get the per thread test setup to run the test through. - PerThreadSetup perThreadSetup = threadSetup.get(); - PingClient pingClient = perThreadSetup._pingClient; - - // Advance the correlation id of messages to send, to make it unique for this run. - perThreadSetup._correlationId = Long.toString(corellationIdGenerator.incrementAndGet()); - // String messageCorrelationId = perThreadSetup._correlationId; - // _logger.debug("messageCorrelationId = " + messageCorrelationId); - - - // Initialize the count and timing controller for the new correlation id. - // This perCorrelationId is only used for controlling the test. - // The PingClient itself uses its own perCorrelationId see in PingPongProducer - PerCorrelationId perCorrelationId = new PerCorrelationId(); - TimingController tc = getTimingController().getControllerForCurrentThread(); - perCorrelationId._tc = tc; - perCorrelationId._expectedCount = pingClient.getExpectedNumPings(numPings + preFill); - perCorrelationIds.put(perThreadSetup._correlationId, perCorrelationId); - - // Must be called before pingAndWaitForReply to setup the CorrelationID. - // This is required because pingClient.start() will start all client threads - // This means that the CorrelationID must be registered before hand. - pingClient.setupCorrelationID(perThreadSetup._correlationId, perCorrelationId._expectedCount); - - // Start the client connection if: - // 1) we are not in a SEND_ONLY test. - // 2) if we have not yet started client because messages are sitting on broker. - // This is either due to a preFill or a consume only test. - if (!testParameters.getPropertyAsBoolean(PingPongProducer.SEND_ONLY_PROPNAME) && - (preFill > 0 || testParameters.getPropertyAsBoolean(PingPongProducer.CONSUME_ONLY_PROPNAME))) - { - pingClient.start(); - } - - // Send the requested number of messages, and wait until they have all been received. - long timeout = Long.parseLong(testParameters.getProperty(PingPongProducer.TIMEOUT_PROPNAME)); - int numReplies = pingClient.pingAndWaitForReply(null, numPings , preFill, timeout, perThreadSetup._correlationId); - - // Check that all the replies were received and log a fail if they were not. - if (numReplies < perCorrelationId._expectedCount) - { - System.out.println("##### " + numReplies + "replies, expected " + perCorrelationId._expectedCount + " #####"); - perCorrelationId._tc.completeTest(false, numPings - perCorrelationId._expectedCount); - } - - // Remove the expected count and timing controller for the message correlation id, to ensure they are cleaned up. - perCorrelationIds.remove(perThreadSetup._correlationId); - } - - /** - * Performs test fixture creation on a per thread basis. This will only be called once for each test thread. - */ - public void threadSetUp() - { - _logger.debug("public void threadSetUp(): called"); - - try - { - // Call the set up method in the super class. This creates a PingClient pinger. - super.threadSetUp(); - - // Create the chained message listener, only if it has not already been created. This is set up with the - // batch size property, to tell it what batch size to output results on. A synchronized block is used to - // ensure that only one thread creates this. - synchronized (this) - { - if (batchedResultsListener == null) - { - int batchSize = Integer.parseInt(testParameters.getProperty(TEST_RESULTS_BATCH_SIZE_PROPNAME)); - batchedResultsListener = new BatchedResultsListener(batchSize); - } - } - - // Get the set up that the super class created. - PerThreadSetup perThreadSetup = threadSetup.get(); - - // Register the chained message listener on the pinger to do its asynchronous test timings from. - perThreadSetup._pingClient.setChainedMessageListener(batchedResultsListener); - } - catch (Exception e) - { - _logger.warn("There was an exception during per thread setup.", e); - } - } - - /** - * BatchedResultsListener is a {@link PingPongProducer.ChainedMessageListener} that can be attached to the - * pinger, in order to receive notifications about every message received and the number remaining to be - * received. Whenever the number remaining crosses a batch size boundary this results listener outputs - * a test timing for the actual number of messages received in the current batch. - */ - private class BatchedResultsListener implements PingPongProducer.ChainedMessageListener - { - /** The test results logging batch size. */ - int _batchSize; - - /** The latency recoreded for the batch */ - private long _batchLatency = 0; - - /** - * Creates a results listener on the specified batch size. - * - * @param batchSize The batch size to use. - */ - public BatchedResultsListener(int batchSize) - { - _batchSize = batchSize; - } - - /** - * This callback method is called from all of the pingers that this test creates. It uses the correlation id - * from the message to identify the timing controller for the test thread that was responsible for sending those - * messages. - * - * @param message The message. - * @param remainingCount The count of messages remaining to be received with a particular correlation id. - * - * @throws JMSException Any underlying JMSException is allowed to fall through. - */ - public void onMessage(Message message, int remainingCount, long latency) throws JMSException - { - // Record the latency for the whole batch - _batchLatency += latency; - // Check if a batch boundary has been crossed. - if ((remainingCount % _batchSize) == 0) - { - // Extract the correlation id from the message. - String correlationId = message.getJMSCorrelationID(); - - /*_logger.debug("public void onMessage(Message message, int remainingCount = " + remainingCount - + "): called on batch boundary for message id: " + correlationId + " with thread id: " - + Thread.currentThread().getId());*/ - - // Get the details for the correlation id and check that they are not null. They can become null - // if a test times out. - PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationId); - if (perCorrelationId != null) - { - // Get the timing controller and expected count for this correlation id. - TimingController tc = perCorrelationId._tc; - int expected = perCorrelationId._expectedCount; - - // Calculate how many messages were actually received in the last batch. This will be the batch size - // except where the number expected is not a multiple of the batch size and this is the first remaining - // count to cross a batch size boundary, in which case it will be the number expected modulo the batch - // size. - int receivedInBatch = ((expected - remainingCount) < _batchSize) ? (expected % _batchSize) : _batchSize; - - // Register a test result for the correlation id. - try - { - // Record the total latency for the batch. - // if batchSize=1 then this will just be the message latency - tc.completeTest(true, receivedInBatch, null, _batchSize == 1 ? latency : _batchLatency); - // Reset latency - _batchLatency = 0; - } - catch (InterruptedException e) - { - // Ignore this. It means the test runner wants to stop as soon as possible. - _logger.warn("Got InterruptedException.", e); - } - } - // Else ignore, test timed out. Should log a fail here? - } - } - } - - /** - * Holds state specific to each correlation id, needed to output test results. This consists of the count of - * the total expected number of messages, and the timing controller for the thread sending those message ids. - */ - private static class PerCorrelationId - { - public int _expectedCount; - public TimingController _tc; - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/ping/PingClient.java b/java/perftests/src/main/java/org/apache/qpid/ping/PingClient.java deleted file mode 100644 index dcfc67d4fc..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/ping/PingClient.java +++ /dev/null @@ -1,112 +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.ping; - -import org.apache.log4j.Logger; - -import org.apache.qpid.requestreply.PingPongProducer; - -import javax.jms.Destination; - -import java.util.List; -import java.util.Properties; - -/** - * PingClient is a {@link PingPongProducer} that does not need a {@link org.apache.qpid.requestreply.PingPongBouncer} - * to send replies to its pings. It simply listens to its own ping destinations, rather than seperate reply queues. - * It is an all in one ping client, that produces and consumes its own pings. - * - * <p/>The constructor increments a count of the number of ping clients created. It is assumed that where many - * are created they will all be run in parallel and be active in sending and consuming pings at the same time. - * If the unique destinations flag is not set and a pub/sub ping cycle is being run, this means that they will all hear - * pings sent by each other. The expected number of pings received will therefore be multiplied up by the number of - * active ping clients. The {@link #getConsumersPerDestination()} method is used to supply this multiplier under these - * conditions. - * - * <p/><table id="crc"><caption>CRC Card</caption> - * <tr><th> Responsibilities <th> Collaborations - * <tr><td> Create a ping producer that listens to its own pings <td> {@link PingPongProducer} - * <tr><td> Count the number of ping producers and produce multiplier for scaling up messages expected over topic pings. - * </table> - */ -public class PingClient extends PingPongProducer -{ - /** Used for debugging. */ - private final Logger log = Logger.getLogger(PingClient.class); - - /** Used to count the number of ping clients created. */ - private static int _pingClientCount; - - /** - * Creates a ping producer with the specified parameters, of which there are many. See the class level comments - * for {@link PingPongProducer} for details. This constructor creates a connection to the broker and creates - * producer and consumer sessions on it, to send and recieve its pings and replies on. - * - * @param overrides Properties containing any desired overrides to the defaults. - * - * @throws Exception Any exceptions are allowed to fall through. - */ - public PingClient(Properties overrides) throws Exception - { - super(overrides); - - _pingClientCount++; - } - - /** - * Returns the ping destinations themselves as the reply destinations for this pinger to listen to. This has the - * effect of making this pinger listen to its own pings. - * - * @return The ping destinations. - */ - public List<Destination> getReplyDestinations() - { - return _pingDestinations; - } - - /** - * Supplies the multiplier for the number of ping clients that will hear each ping when doing pub/sub pinging. - * - * @return The scaling up of the number of expected pub/sub pings. - */ - public int getConsumersPerDestination() - { - log.debug("public int getConsumersPerDestination(): called"); - - if (_isUnique) - { - log.debug(_noOfConsumers + " consumer per destination."); - - return _noOfConsumers; - } - else - { - log.debug((_pingClientCount * _noOfConsumers) + " consumers per destination."); - - return _pingClientCount * _noOfConsumers; - } - } - - public int getClientCount() - { - return _pingClientCount; - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java b/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java deleted file mode 100644 index a15897c82b..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java +++ /dev/null @@ -1,452 +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.ping; - -import org.apache.log4j.Logger; - -import org.apache.qpid.requestreply.PingPongProducer; -import org.apache.qpid.util.CommandLineParser; - -import org.apache.qpid.junit.extensions.util.MathUtils; -import org.apache.qpid.junit.extensions.util.ParsedProperties; - -import javax.jms.*; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.List; -import java.util.Properties; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * PingDurableClient is a variation of the {@link PingPongProducer} ping tool. Instead of sending its pings and - * receiving replies to them at the same time, this tool sends pings until it is signalled by some 'event' to stop - * sending. It then waits for another signal before it re-opens a fresh connection and attempts to receive all of the - * pings that it has succesfully sent. It is intended to be an interactive test that lets a user experiment with - * failure conditions when using durable messaging. - * - * <p/>The events that can stop it from sending are input from the user on the console, failure of its connection to - * the broker, completion of sending a specified number of messages, or expiry of a specified duration. In all cases - * it will do its best to clean up and close the connection before opening a fresh connection to receive the pings - * with. - * - * <p/>The event to re-connect and attempt to recieve the pings is input from the user on the console. - * - * <p/>This ping client inherits the configuration properties of its parent class ({@link PingPongProducer}) and - * additionally accepts the following parameters: - * - * <p/><table><caption>Parameters</caption> - * <tr><th> Parameter <th> Default <th> Comments - * <tr><td> numMessages <td> 100 <td> The total number of messages to send. - * <tr><td> numMessagesToAction <td> -1 <td> The number of messages to send before taking a custom 'action'. - * <tr><td> duration <td> 30S <td> The length of time to ping for. (Format dDhHmMsS, for d days, h hours, - * m minutes and s seconds). - * </table> - * - * <p/>This ping client also overrides some of the defaults of its parent class, to provide a reasonable set up - * when no parameters are specified. - * - * <p/><table><caption>Parameters</caption> - * <tr><th> Parameter <th> Default <th> Comments - * <tr><td> uniqueDests <td> false <td> Prevents destination names being timestamped. - * <tr><td> transacted <td> true <td> Only makes sense to test with transactions. - * <tr><td> persistent <td> true <td> Only makes sense to test persistent. - * <tr><td> durableDests <td> true <td> Should use durable queues with persistent messages. - * <tr><td> commitBatchSize <td> 10 - * <tr><td> rate <td> 20 <td> Total default test time is 5 seconds. - * </table> - * - * <p/>When a number of messages or duration is specified, this ping client will ping until the first of those limits - * is reached. Reaching the limit will be interpreted as the first signal to stop sending, and the ping client will - * wait for the second signal before receiving its pings. - * - * <p/>This class provides a mechanism for extensions to add arbitrary actions, after a particular number of messages - * have been sent. When the number of messages equal the value set in the 'numMessagesToAction' property is method, - * the {@link #takeAction} method is called. By default this does nothing, but extensions of this class can provide - * custom behaviour with alternative implementations of this method (for example taking a backup). - * - * <p><table id="crc"><caption>CRC Card</caption> - * <tr><th> Responsibilities <th> Collaborations - * <tr><td> Send and receive pings. - * <tr><td> Accept user input to signal stop sending. - * <tr><td> Accept user input to signal start receiving. - * <tr><td> Provide feedback on pings sent versus pings received. - * <tr><td> Provide extension point for arbitrary action on a particular message count. - * </table> - */ -public class PingDurableClient extends PingPongProducer implements ExceptionListener -{ - private static final Logger log = Logger.getLogger(PingDurableClient.class); - - public static final String NUM_MESSAGES_PROPNAME = "numMessages"; - public static final String NUM_MESSAGES_DEFAULT = "100"; - public static final String DURATION_PROPNAME = "duration"; - public static final String DURATION_DEFAULT = "30S"; - public static final String NUM_MESSAGES_TO_ACTION_PROPNAME = "numMessagesToAction"; - public static final String NUM_MESSAGES_TO_ACTION_DEFAULT = "-1"; - - /** The maximum length of time to wait whilst receiving pings before assuming that no more are coming. */ - private static final long TIME_OUT = 3000; - - static - { - defaults.setProperty(NUM_MESSAGES_PROPNAME, NUM_MESSAGES_DEFAULT); - defaults.setProperty(DURATION_PROPNAME, DURATION_DEFAULT); - defaults.setProperty(UNIQUE_DESTS_PROPNAME, "false"); - defaults.setProperty(TRANSACTED_PROPNAME, "true"); - defaults.setProperty(PERSISTENT_MODE_PROPNAME, "true"); - defaults.setProperty(TX_BATCH_SIZE_PROPNAME, "10"); - defaults.setProperty(RATE_PROPNAME, "20"); - defaults.setProperty(DURABLE_DESTS_PROPNAME, "true"); - defaults.setProperty(NUM_MESSAGES_TO_ACTION_PROPNAME, NUM_MESSAGES_TO_ACTION_DEFAULT); - } - - /** Specifies the number of pings to send, if larger than 0. 0 means send until told to stop. */ - private int numMessages; - - /** Holds the number of messages to send before taking triggering the action. */ - private int numMessagesToAction; - - /** Sepcifies how long to ping for, if larger than 0. 0 means send until told to stop. */ - private long duration; - - /** Used to indciate that this application should terminate. Set by the shutdown hook. */ - private boolean terminate = false; - - /** - * @throws Exception Any exceptions are allowed to fall through. - */ - public PingDurableClient(Properties overrides) throws Exception - { - super(overrides); - log.debug("public PingDurableClient(Properties overrides = " + overrides + "): called"); - - // Extract the additional configuration parameters. - ParsedProperties properties = new ParsedProperties(defaults); - properties.putAll(overrides); - - numMessages = properties.getPropertyAsInteger(NUM_MESSAGES_PROPNAME); - String durationSpec = properties.getProperty(DURATION_PROPNAME); - numMessagesToAction = properties.getPropertyAsInteger(NUM_MESSAGES_TO_ACTION_PROPNAME); - - if (durationSpec != null) - { - duration = MathUtils.parseDuration(durationSpec) * 1000000; - } - } - - /** - * Starts the ping/wait/receive process. - * - * @param args The command line arguments. - */ - public static void main(String[] args) - { - try - { - // Create a ping producer overriding its defaults with all options passed on the command line. - Properties options = - CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties()); - PingDurableClient pingProducer = new PingDurableClient(options); - - // Create a shutdown hook to terminate the ping-pong producer. - Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook()); - - // Ensure that the ping pong producer is registered to listen for exceptions on the connection too. - // pingProducer.getConnection().setExceptionListener(pingProducer); - - // Run the test procedure. - int sent = pingProducer.send(); - pingProducer.closeConnection(); - pingProducer.waitForUser("Press return to begin receiving the pings."); - pingProducer.receive(sent); - - System.exit(0); - } - catch (Exception e) - { - System.err.println(e.getMessage()); - log.error("Top level handler caught execption.", e); - System.exit(1); - } - } - - /** - * Performs the main test procedure implemented by this ping client. See the class level comment for details. - */ - protected int send() throws Exception - { - log.debug("public void sendWaitReceive(): called"); - - log.debug("duration = " + duration); - log.debug("numMessages = " + numMessages); - - if (duration > 0) - { - System.out.println("Sending for up to " + (duration / 1000000000f) + " seconds."); - } - - if (_rate > 0) - { - System.out.println("Sending at " + _rate + " messages per second."); - } - - if (numMessages > 0) - { - System.out.println("Sending up to " + numMessages + " messages."); - } - - // Establish the connection and the message producer. - establishConnection(true, false); - _connection.start(); - - Message message = getTestMessage(getReplyDestinations().get(0), _messageSize, _persistent); - - // Send pings until a terminating condition is received. - boolean endCondition = false; - int messagesSent = 0; - int messagesCommitted = 0; - int messagesNotCommitted = 0; - long start = System.nanoTime(); - - // Clear console in. - clearConsole(); - - while (!endCondition) - { - boolean committed = false; - - try - { - committed = sendMessage(messagesSent, message) && _transacted; - - messagesSent++; - messagesNotCommitted++; - - // Keep count of the number of messsages currently committed and pending commit. - if (committed) - { - log.debug("Adding " + messagesNotCommitted + " messages to the committed count."); - messagesCommitted += messagesNotCommitted; - messagesNotCommitted = 0; - - System.out.println("Commited: " + messagesCommitted); - } - } - catch (JMSException e) - { - log.debug("Got JMSException whilst sending."); - _publish = false; - } - - // Perform the arbitrary action if the number of messages sent has reached the right number. - if (messagesSent == numMessagesToAction) - { - System.out.println("At action point, Messages sent = " + messagesSent + ", Messages Committed = " - + messagesCommitted + ", Messages not Committed = " + messagesNotCommitted); - takeAction(); - } - - // Determine if the end condition has been met, based on the number of messages, time passed, errors on - // the connection or user input. - long now = System.nanoTime(); - - if ((duration != 0) && ((now - start) > duration)) - { - System.out.println("Send halted because duration expired."); - endCondition = true; - } - else if ((numMessages != 0) && (messagesSent >= numMessages)) - { - System.out.println("Send halted because # messages completed."); - endCondition = true; - } - else if (System.in.available() > 0) - { - System.out.println("Send halted by user input."); - endCondition = true; - - clearConsole(); - } - else if (!_publish) - { - System.out.println("Send halted by error on the connection."); - endCondition = true; - } - } - - log.debug("messagesSent = " + messagesSent); - log.debug("messagesCommitted = " + messagesCommitted); - log.debug("messagesNotCommitted = " + messagesNotCommitted); - - System.out.println("Messages sent: " + messagesSent + ", Messages Committed = " + messagesCommitted - + ", Messages not Committed = " + messagesNotCommitted); - - return messagesSent; - } - - protected void closeConnection() - { - // Clean up the connection. - try - { - close(); - } - catch (JMSException e) - { - log.debug("There was an error whilst closing the connection: " + e, e); - System.out.println("There was an error whilst closing the connection."); - - // Ignore as did best could manage to clean up. - } - } - - protected void receive(int messagesSent) throws Exception - { - // Re-establish the connection and the message consumer. - _queueJVMSequenceID = new AtomicInteger(); - _queueSharedID = new AtomicInteger(); - - establishConnection(false, true); - _consumer[0].setMessageListener(null); - _consumerConnection[0].start(); - - // Try to receive all of the pings that were successfully sent. - int messagesReceived = 0; - boolean endCondition = false; - - while (!endCondition) - { - // Message received = _consumer.receiveNoWait(); - Message received = _consumer[0].receive(TIME_OUT); - log.debug("received = " + received); - - if (received != null) - { - messagesReceived++; - } - - // Determine if the end condition has been met, based on the number of messages and time passed since last - // receiving a message. - if (received == null) - { - System.out.println("Timed out."); - endCondition = true; - } - else if (messagesReceived >= messagesSent) - { - System.out.println("Got all messages."); - endCondition = true; - } - } - - // Ensure messages received are committed. - if (_consTransacted) - { - try - { - _consumerSession[0].commit(); - System.out.println("Committed for all messages received."); - } - catch (JMSException e) - { - log.debug("Error during commit: " + e, e); - System.out.println("Error during commit."); - try - { - _consumerSession[0].rollback(); - System.out.println("Rolled back on all messages received."); - } - catch (JMSException e2) - { - log.debug("Error during rollback: " + e, e); - System.out.println("Error on roll back of all messages received."); - } - - } - } - - log.debug("messagesReceived = " + messagesReceived); - - System.out.println("Messages received: " + messagesReceived); - - // Clean up the connection. - close(); - } - - /** - * Clears any pending input from the console. - */ - private void clearConsole() - { - try - { - BufferedReader bis = new BufferedReader(new InputStreamReader(System.in)); - - // System.in.skip(System.in.available()); - while (bis.ready()) - { - bis.readLine(); - } - } - catch (IOException e) - { } - } - - /** - * Returns the ping destinations themselves as the reply destinations for this pinger to listen to. This has the - * effect of making this pinger listen to its own pings. - * - * @return The ping destinations. - */ - public List<Destination> getReplyDestinations() - { - return _pingDestinations; - } - - /** - * Gets a shutdown hook that will cleanly shut this down when it is running the ping loop. This can be registered with - * the runtime system as a shutdown hook. This shutdown hook sets an additional terminate flag, compared with the - * shutdown hook in {@link PingPongProducer}, because the publish flag is used to indicate that sending or receiving - * message should stop, not that the application should termiante. - * - * @return A shutdown hook for the ping loop. - */ - public Thread getShutdownHook() - { - return new Thread(new Runnable() - { - public void run() - { - stop(); - terminate = true; - } - }); - } - - /** - * Performs an aribtrary action once the 'numMesagesToAction' count is reached on sending messages. This default - * implementation does nothing. - */ - public void takeAction() - { } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java b/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java deleted file mode 100644 index 5ba4004c56..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java +++ /dev/null @@ -1,311 +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.ping; - -import junit.framework.Test; -import junit.framework.TestSuite; - -import org.apache.log4j.Logger; - -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.requestreply.PingPongProducer; - -import org.apache.qpid.junit.extensions.TimingController; -import org.apache.qpid.junit.extensions.TimingControllerAware; -import org.apache.qpid.junit.extensions.util.ParsedProperties; - -import javax.jms.JMSException; -import javax.jms.Message; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; - -/** - * PingLatencyTestPerf is a performance test that outputs multiple timings from its test method, using the timing - * controller interface supplied by the test runner from a seperate listener thread. It outputs round trip timings for - * individual ping messages rather than for how long a complete batch of messages took to process. It also differs from - * the {@link PingTestPerf} test that it extends because it can output timings as replies are received, rather than - * waiting until all expected replies are received. - * - * <p/>This test does not output timings for every single ping message, as when running at high volume, writing the test - * log for a vast number of messages would slow the testing down. Instead samples ping latency occasionally. The - * frequency of ping sampling is set using the {@link #TEST_RESULTS_BATCH_SIZE_PROPNAME} property, to override the - * default of every {@link #DEFAULT_TEST_RESULTS_BATCH_SIZE}. - * - * <p/>The size parameter logged for each individual ping is set to the size of the batch of messages that the - * individual timed ping was taken from, rather than 1 for a single message. This is so that the total throughput - * (messages / time) can be calculated in order to examine the relationship between throughput and latency. - * - * <p/><table id="crc"><caption>CRC Card</caption> <tr><td> Responsibilities <th> Collaborations <tr><td> Send many ping - * messages and output timings for sampled individual pings. </table> - */ -public class PingLatencyTestPerf extends PingTestPerf implements TimingControllerAware -{ - private static Logger _logger = Logger.getLogger(PingLatencyTestPerf.class); - - /** Holds the name of the property to get the test results logging batch size. */ - public static final String TEST_RESULTS_BATCH_SIZE_PROPNAME = "batchSize"; - - /** Holds the default test results logging batch size. */ - public static final int DEFAULT_TEST_RESULTS_BATCH_SIZE = 1000; - - /** Used to hold the timing controller passed from the test runner. */ - private TimingController _timingController; - - /** Used to generate unique correlation ids for each test run. */ - private AtomicLong corellationIdGenerator = new AtomicLong(); - - /** - * Holds test specifics by correlation id. This consists of the expected number of messages and the timing - * controler. - */ - private Map<String, PerCorrelationId> perCorrelationIds = - Collections.synchronizedMap(new HashMap<String, PerCorrelationId>()); - - /** Holds the batched results listener, that does logging on batch boundaries. */ - private BatchedResultsListener batchedResultsListener = null; - - /** - * Creates a new asynchronous ping performance test with the specified name. - * - * @param name The test name. - */ - public PingLatencyTestPerf(String name) - { - super(name); - - // Sets up the test parameters with defaults. - ParsedProperties.setSysPropertyIfNull(TEST_RESULTS_BATCH_SIZE_PROPNAME, - Integer.toString(DEFAULT_TEST_RESULTS_BATCH_SIZE)); - } - - /** Compile all the tests into a test suite. */ - public static Test suite() - { - // Build a new test suite - TestSuite suite = new TestSuite("Ping Latency Tests"); - - // Run performance tests in read committed mode. - suite.addTest(new PingLatencyTestPerf("testPingLatency")); - - return suite; - } - - /** - * Accepts a timing controller from the test runner. - * - * @param timingController The timing controller to register mutliple timings with. - */ - public void setTimingController(TimingController timingController) - { - _timingController = timingController; - } - - /** - * Gets the timing controller passed in by the test runner. - * - * @return The timing controller passed in by the test runner. - */ - public TimingController getTimingController() - { - return _timingController; - } - - /** - * Sends the specified number of pings, asynchronously outputs timings on every batch boundary, and waits until all - * replies have been received or a time out occurs before exiting this method. - * - * @param numPings The number of pings to send. - */ - public void testPingLatency(int numPings) throws Exception - { - _logger.debug("public void testPingLatency(int numPings): called"); - - // Ensure that at least one ping was requeusted. - if (numPings == 0) - { - _logger.error("Number of pings requested was zero."); - } - - // Get the per thread test setup to run the test through. - PerThreadSetup perThreadSetup = threadSetup.get(); - PingClient pingClient = perThreadSetup._pingClient; - - // Advance the correlation id of messages to send, to make it unique for this run. - String messageCorrelationId = Long.toString(corellationIdGenerator.incrementAndGet()); - _logger.debug("messageCorrelationId = " + messageCorrelationId); - - // Initialize the count and timing controller for the new correlation id. - PerCorrelationId perCorrelationId = new PerCorrelationId(); - TimingController tc = getTimingController().getControllerForCurrentThread(); - perCorrelationId._tc = tc; - perCorrelationId._expectedCount = numPings; - perCorrelationIds.put(messageCorrelationId, perCorrelationId); - - // Attach the chained message listener to the ping producer to listen asynchronously for the replies to these - // messages. - pingClient.setChainedMessageListener(batchedResultsListener); - - // Generate a sample message of the specified size. - Message msg = - pingClient.getTestMessage(perThreadSetup._pingClient.getReplyDestinations().get(0), - testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME), - testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME)); - - // Send the requested number of messages, and wait until they have all been received. - long timeout = Long.parseLong(testParameters.getProperty(PingPongProducer.TIMEOUT_PROPNAME)); - int numReplies = pingClient.pingAndWaitForReply(msg, numPings, timeout, null); - - // Check that all the replies were received and log a fail if they were not. - if (numReplies < numPings) - { - tc.completeTest(false, 0); - } - - // Remove the chained message listener from the ping producer. - pingClient.removeChainedMessageListener(); - - // Remove the expected count and timing controller for the message correlation id, to ensure they are cleaned up. - perCorrelationIds.remove(messageCorrelationId); - } - - /** Performs test fixture creation on a per thread basis. This will only be called once for each test thread. */ - public void threadSetUp() - { - _logger.debug("public void threadSetUp(): called"); - - try - { - // Call the set up method in the super class. This creates a PingClient pinger. - super.threadSetUp(); - - // Create the chained message listener, only if it has not already been created. This is set up with the - // batch size property, to tell it what batch size to output results on. A synchronized block is used to - // ensure that only one thread creates this. - synchronized (this) - { - if (batchedResultsListener == null) - { - int batchSize = Integer.parseInt(testParameters.getProperty(TEST_RESULTS_BATCH_SIZE_PROPNAME)); - batchedResultsListener = new BatchedResultsListener(batchSize); - } - } - - // Get the set up that the super class created. - PerThreadSetup perThreadSetup = threadSetup.get(); - - // Register the chained message listener on the pinger to do its asynchronous test timings from. - perThreadSetup._pingClient.setChainedMessageListener(batchedResultsListener); - } - catch (Exception e) - { - _logger.warn("There was an exception during per thread setup.", e); - } - } - - /** - * BatchedResultsListener is a {@link org.apache.qpid.requestreply.PingPongProducer.ChainedMessageListener} that can - * be attached to the pinger, in order to receive notifications about every message received and the number - * remaining to be received. Whenever the number remaining crosses a batch size boundary this results listener - * outputs a test timing for the actual number of messages received in the current batch. - */ - private class BatchedResultsListener implements PingPongProducer.ChainedMessageListener - { - /** The test results logging batch size. */ - int _batchSize; - private boolean _strictAMQP; - - /** - * Creates a results listener on the specified batch size. - * - * @param batchSize The batch size to use. - */ - public BatchedResultsListener(int batchSize) - { - _batchSize = batchSize; - _strictAMQP = - Boolean.parseBoolean(System.getProperties().getProperty(AMQSession.STRICT_AMQP, - AMQSession.STRICT_AMQP_DEFAULT)); - } - - /** - * This callback method is called from all of the pingers that this test creates. It uses the correlation id - * from the message to identify the timing controller for the test thread that was responsible for sending those - * messages. - * - * @param message The message. - * @param remainingCount The count of messages remaining to be received with a particular correlation id. - * - * @throws javax.jms.JMSException Any underlying JMSException is allowed to fall through. - */ - public void onMessage(Message message, int remainingCount, long latency) throws JMSException - { - _logger.debug("public void onMessage(Message message, int remainingCount = " + remainingCount + "): called"); - - // Check if a batch boundary has been crossed. - if ((remainingCount % _batchSize) == 0) - { - // Extract the correlation id from the message. - String correlationId = message.getJMSCorrelationID(); - - // Get the details for the correlation id and check that they are not null. They can become null - // if a test times out. - PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationId); - if (perCorrelationId != null) - { - // Get the timing controller and expected count for this correlation id. - TimingController tc = perCorrelationId._tc; - int expected = perCorrelationId._expectedCount; - - // Calculate how many messages were actually received in the last batch. This will be the batch size - // except where the number expected is not a multiple of the batch size and this is the first remaining - // count to cross a batch size boundary, in which case it will be the number expected modulo the batch - // size. - int receivedInBatch = ((expected - remainingCount) < _batchSize) ? (expected % _batchSize) : _batchSize; - - // Register a test result for the correlation id. - try - { - tc.completeTest(true, receivedInBatch, latency); - } - catch (InterruptedException e) - { - // Ignore this. It means the test runner wants to stop as soon as possible. - _logger.warn("Got InterruptedException.", e); - } - } - // Else ignore, test timed out. Should log a fail here? - } - } - } - - /** - * Holds state specific to each correlation id, needed to output test results. This consists of the count of the - * total expected number of messages, and the timing controller for the thread sending those message ids. - */ - private static class PerCorrelationId - { - public int _expectedCount; - public TimingController _tc; - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/ping/PingSendOnlyClient.java b/java/perftests/src/main/java/org/apache/qpid/ping/PingSendOnlyClient.java deleted file mode 100644 index 2fe852af77..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/ping/PingSendOnlyClient.java +++ /dev/null @@ -1,93 +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.ping; - -import java.util.Properties; - -import javax.jms.Destination; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.ObjectMessage; - -import org.apache.log4j.Logger; - -import org.apache.qpid.client.message.TestMessageFactory; -import org.apache.qpid.util.CommandLineParser; - -/** - * <p><table id="crc"><caption>CRC Card</caption> - * <tr><th> Responsibilities <th> Collaborations - * </table> - */ -public class PingSendOnlyClient extends PingDurableClient -{ - private static final Logger log = Logger.getLogger(PingSendOnlyClient.class); - - public PingSendOnlyClient(Properties overrides) throws Exception - { - super(overrides); - } - - /** - * Starts the ping/wait/receive process. - * - * @param args The command line arguments. - */ - public static void main(String[] args) - { - try - { - // Create a ping producer overriding its defaults with all options passed on the command line. - Properties options = CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties()); - PingSendOnlyClient pingProducer = new PingSendOnlyClient(options); - - // Create a shutdown hook to terminate the ping-pong producer. - Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook()); - - // Ensure that the ping pong producer is registered to listen for exceptions on the connection too. - // pingProducer.getConnection().setExceptionListener(pingProducer); - - // Run the test procedure. - pingProducer.send(); - pingProducer.waitForUser("Press return to close connection and quit."); - pingProducer.closeConnection(); - - System.exit(0); - } - catch (Exception e) - { - System.err.println(e.getMessage()); - log.error("Top level handler caught execption.", e); - System.exit(1); - } - } - - public Message getTestMessage(Destination replyQueue, int messageSize, boolean persistent) throws JMSException - { - Message msg = TestMessageFactory.newTextMessage(_producerSession, messageSize); - - // Timestamp the message in nanoseconds. - msg.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime()); - - return msg; - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java b/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java deleted file mode 100644 index cf16abc596..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java +++ /dev/null @@ -1,281 +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.ping; - -import junit.framework.Assert; -import junit.framework.Test; -import junit.framework.TestSuite; - -import org.apache.log4j.Logger; - -import org.apache.qpid.requestreply.PingPongProducer; - -import org.apache.qpid.junit.extensions.AsymptoticTestCase; -import org.apache.qpid.junit.extensions.TestThreadAware; -import org.apache.qpid.junit.extensions.util.ParsedProperties; -import org.apache.qpid.junit.extensions.util.TestContextProperties; - -import javax.jms.*; - -/** - * PingTestPerf is a ping test, that has been written with the intention of being scaled up to run many times - * simultaneously to simluate many clients/producers/connections. - * - * <p/>A single run of the test using the default JUnit test runner will result in the sending and timing of a single - * full round trip ping. This test may be scaled up using a suitable JUnit test runner. - * - * <p/>The setup/teardown cycle establishes a connection to a broker and sets up a queue to send ping messages to and a - * temporary queue for replies. This setup is only established once for all the test repeats/threads that may be run, - * except if the connection is lost in which case an attempt to re-establish the setup is made. - * - * <p/>The test cycle is: Connects to a queue, creates a temporary queue, creates messages containing a property that - * is the name of the temporary queue, fires off a message on the original queue and waits for a response on the - * temporary queue. - * - * <p/>Configurable test properties: message size, transacted or not, persistent or not. Broker connection details. - * - * <p><table id="crc"><caption>CRC Card</caption> - * <tr><th> Responsibilities <th> Collaborations - * </table> - */ -public class PingTestPerf extends AsymptoticTestCase implements TestThreadAware -{ - private static Logger _logger = Logger.getLogger(PingTestPerf.class); - - /** Thread local to hold the per-thread test setup fields. */ - ThreadLocal<PerThreadSetup> threadSetup = new ThreadLocal<PerThreadSetup>(); - - /** Holds a property reader to extract the test parameters from. */ - protected ParsedProperties testParameters = - TestContextProperties.getInstance(PingPongProducer.defaults /*System.getProperties()*/); - - public PingTestPerf(String name) - { - super(name); - - _logger.debug("testParameters = " + testParameters); - } - - /** - * Compile all the tests into a test suite. - * @return The test method testPingOk. - */ - public static Test suite() - { - // Build a new test suite - TestSuite suite = new TestSuite("Ping Performance Tests"); - - // Run performance tests in read committed mode. - suite.addTest(new PingTestPerf("testPingOk")); - - return suite; - } - - public void testPingOk(int numPings) throws Exception - { - if (numPings == 0) - { - Assert.fail("Number of pings requested was zero."); - } - - // Get the per thread test setup to run the test through. - PerThreadSetup perThreadSetup = threadSetup.get(); - - if (perThreadSetup == null) - { - Assert.fail("Could not get per thread test setup, it was null."); - } - - // Generate a sample message. This message is already time stamped and has its reply-to destination set. - Message msg = - perThreadSetup._pingClient.getTestMessage(perThreadSetup._pingClient.getReplyDestinations().get(0), - testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME), - testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME)); - - // start the test - long timeout = Long.parseLong(testParameters.getProperty(PingPongProducer.TIMEOUT_PROPNAME)); - int numReplies = perThreadSetup._pingClient.pingAndWaitForReply(msg, numPings, timeout, null); - - // Fail the test if the timeout was exceeded. - if (numReplies != perThreadSetup._pingClient.getExpectedNumPings(numPings)) - { - Assert.fail("The ping timed out after " + timeout + " ms. Messages Sent = " + numPings + ", MessagesReceived = " - + numReplies); - } - } - - /** Performs test fixture creation on a per thread basis. This will only be called once for each test thread. */ - public void threadSetUp() - { - _logger.debug("public void threadSetUp(): called"); - - try - { - PerThreadSetup perThreadSetup = new PerThreadSetup(); - - // This is synchronized because there is a race condition, which causes one connection to sleep if - // all threads try to create connection concurrently. - synchronized (this) - { - // Establish a client to ping a Destination and listen the reply back from same Destination - perThreadSetup._pingClient = new PingClient(testParameters); - perThreadSetup._pingClient.establishConnection(true, true); - } - - // Attach the per-thread set to the thread. - threadSetup.set(perThreadSetup); - } - catch (Exception e) - { - _logger.warn("There was an exception during per thread setup.", e); - } - } - - /** - * Called after all threads have completed their setup. - */ - public void postThreadSetUp() - { - _logger.debug("public void postThreadSetUp(): called"); - - PerThreadSetup perThreadSetup = threadSetup.get(); - // Prefill the broker unless we are in consume only mode. - int preFill = testParameters.getPropertyAsInteger(PingPongProducer.PREFILL_PROPNAME); - if (!testParameters.getPropertyAsBoolean(PingPongProducer.CONSUME_ONLY_PROPNAME) && preFill > 0) - { - try - { - // Manually set the correlation ID to 1. This is not ideal but it is the - // value that the main test loop will use. - perThreadSetup._pingClient.pingNoWaitForReply(null, preFill, String.valueOf(perThreadSetup._pingClient.getClientCount())); - - // Note with a large preFill and non-tx session the messages will be - // rapidly pushed in to the mina buffers. OOM's are a real risk here. - // Should perhaps consider using a TX session for the prefill. - - long delayBeforeConsume = testParameters.getPropertyAsLong(PingPongProducer.DELAY_BEFORE_CONSUME_PROPNAME); - - // Only delay if we are - // not doing send only - // and we have consumers - // and a delayBeforeConsume - if (!(testParameters.getPropertyAsBoolean(PingPongProducer.SEND_ONLY_PROPNAME)) - && (testParameters.getPropertyAsInteger(PingPongProducer.NUM_CONSUMERS_PROPNAME) > 0) - && delayBeforeConsume > 0) - { - - boolean verbose = testParameters.getPropertyAsBoolean(PingPongProducer.VERBOSE_PROPNAME); - // Only do logging if in verbose mode. - if (verbose) - { - if (delayBeforeConsume > 60000) - { - long minutes = delayBeforeConsume / 60000; - long seconds = (delayBeforeConsume - (minutes * 60000)) / 1000; - long ms = delayBeforeConsume - (minutes * 60000) - (seconds * 1000); - _logger.info("Delaying for " + minutes + "m " + seconds + "s " + ms + "ms before starting test."); - } - else - { - _logger.info("Delaying for " + delayBeforeConsume + "ms before starting test."); - } - } - - Thread.sleep(delayBeforeConsume); - - if (verbose) - { - _logger.info("Starting Test."); - } - } - - // We can't start the client's here as the test client has not yet been configured to receieve messages. - // only when the test method is executed will the correlationID map be set up and ready to consume - // the messages we have sent here. - } - catch (Exception e) - { - _logger.warn("There was an exception during per thread setup.", e); - } - } - else //Only start the consumer if we are not preFilling. - { - // Start the consumers, unless we have data on the broker - // already this is signified by being in consume_only, we will - // start the clients after setting up the correlation IDs. - // We should also not start the clients if we are in Send only - if (!testParameters.getPropertyAsBoolean(PingPongProducer.CONSUME_ONLY_PROPNAME) && - !(testParameters.getPropertyAsBoolean(PingPongProducer.SEND_ONLY_PROPNAME))) - { - // Start the client connection - try - { - perThreadSetup._pingClient.start(); - } - catch (JMSException e) - { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. - } - } - } - } - - /** - * Performs test fixture clean - */ - public void threadTearDown() - { - _logger.debug("public void threadTearDown(): called"); - - try - { - // Get the per thread test fixture. - PerThreadSetup perThreadSetup = threadSetup.get(); - - // Close the pingers so that it cleans up its connection cleanly. - synchronized (this) - { - if ((perThreadSetup != null) && (perThreadSetup._pingClient != null)) - { - perThreadSetup._pingClient.close(); - } - } - } - catch (JMSException e) - { - _logger.warn("There was an exception during per thread tear down."); - } - finally - { - // Ensure the per thread fixture is reclaimed. - threadSetup.remove(); - } - } - - protected static class PerThreadSetup - { - /** - * Holds the test ping client. - */ - protected PingClient _pingClient; - protected String _correlationId; - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java b/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java deleted file mode 100644 index 8e010ccf07..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java +++ /dev/null @@ -1,453 +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.requestreply; - -import java.io.IOException; -import java.net.InetAddress; -import java.text.SimpleDateFormat; -import java.util.Date; - -import javax.jms.*; - -import org.apache.log4j.Logger; - -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.AMQQueue; -import org.apache.qpid.client.AMQTopic; -import org.apache.qpid.jms.ConnectionListener; -import org.apache.qpid.jms.Session; -import org.apache.qpid.topic.Config; -import org.apache.qpid.exchange.ExchangeDefaults; - -/** - * PingPongBouncer is a message listener the bounces back messages to their reply to destination. This is used to return - * ping messages generated by {@link org.apache.qpid.requestreply.PingPongProducer} but could be used for other purposes - * too. - * - * <p/>The correlation id from the received message is extracted, and placed into the reply as the correlation id. Messages - * are bounced back to their reply-to destination. The original sender of the message has the option to use either a unique - * temporary queue or the correlation id to correlate the original message to the reply. - * - * <p/>There is a verbose mode flag which causes information about each ping to be output to the console - * (info level logging, so usually console). This can be helpfull to check the bounce backs are happening but should - * be disabled for real timing tests as writing to the console will slow things down. - * - * <p><table id="crc"><caption>CRC Card</caption> - * <tr><th> Responsibilities <th> Collaborations - * <tr><td> Bounce back messages to their reply to destination. - * <tr><td> Provide command line invocation to start the bounce back on a configurable broker url. - * </table> - * - * @todo Replace the command line parsing with a neater tool. - * - * @todo Make verbose accept a number of messages, only prints to console every X messages. - */ -public class PingPongBouncer implements MessageListener -{ - private static final Logger _logger = Logger.getLogger(PingPongBouncer.class); - - /** The default prefetch size for the message consumer. */ - private static final int PREFETCH = 1; - - /** The default no local flag for the message consumer. */ - private static final boolean NO_LOCAL = true; - - private static final String DEFAULT_DESTINATION_NAME = "ping"; - - /** The default exclusive flag for the message consumer. */ - private static final boolean EXCLUSIVE = false; - - /** A convenient formatter to use when time stamping output. */ - protected static final SimpleDateFormat timestampFormatter = new SimpleDateFormat("hh:mm:ss:SS"); - - /** Used to indicate that the reply generator should log timing info to the console (logger info level). */ - private boolean _verbose = false; - - /** Determines whether this bounce back client bounces back messages persistently. */ - private boolean _persistent = false; - - private Destination _consumerDestination; - - /** Keeps track of the response destination of the previous message for the last reply to producer cache. */ - private Destination _lastResponseDest; - - /** The producer for sending replies with. */ - private MessageProducer _replyProducer; - - /** The consumer controlSession. */ - private Session _consumerSession; - - /** The producer controlSession. */ - private Session _producerSession; - - /** Holds the connection to the broker. */ - private AMQConnection _connection; - - /** Flag used to indicate if this is a point to point or pub/sub ping client. */ - private boolean _isPubSub = false; - - /** - * This flag is used to indicate that the user should be prompted to kill a broker, in order to test - * failover, immediately before committing a transaction. - */ - protected boolean _failBeforeCommit = false; - - /** - * This flag is used to indicate that the user should be prompted to a kill a broker, in order to test - * failover, immediate after committing a transaction. - */ - protected boolean _failAfterCommit = false; - - /** - * Creates a PingPongBouncer on the specified producer and consumer sessions. - * - * @param brokerDetails The addresses of the brokers to connect to. - * @param username The broker username. - * @param password The broker password. - * @param virtualpath The virtual host name within the broker. - * @param destinationName The name of the queue to receive pings on - * (or root of the queue name where many queues are generated). - * @param persistent A flag to indicate that persistent message should be used. - * @param transacted A flag to indicate that pings should be sent within transactions. - * @param selector A message selector to filter received pings with. - * @param verbose A flag to indicate that message timings should be sent to the console. - * - * @throws Exception All underlying exceptions allowed to fall through. This is only test code... - */ - public PingPongBouncer(String brokerDetails, String username, String password, String virtualpath, - String destinationName, boolean persistent, boolean transacted, String selector, boolean verbose, - boolean pubsub) throws Exception - { - // Create a client id to uniquely identify this client. - InetAddress address = InetAddress.getLocalHost(); - String clientId = address.getHostName() + System.currentTimeMillis(); - _verbose = verbose; - _persistent = persistent; - setPubSub(pubsub); - // Connect to the broker. - setConnection(new AMQConnection(brokerDetails, username, password, clientId, virtualpath)); - _logger.info("Connected with URL:" + getConnection().toURL()); - - // Set up the failover notifier. - getConnection().setConnectionListener(new FailoverNotifier()); - - // Create a controlSession to listen for messages on and one to send replies on, transactional depending on the - // command line option. - _consumerSession = (Session) getConnection().createSession(transacted, Session.AUTO_ACKNOWLEDGE); - _producerSession = (Session) getConnection().createSession(transacted, Session.AUTO_ACKNOWLEDGE); - - // Create the queue to listen for message on. - createConsumerDestination(destinationName); - MessageConsumer consumer = - _consumerSession.createConsumer(_consumerDestination, PREFETCH, NO_LOCAL, EXCLUSIVE, selector); - - // Create a producer for the replies, without a default destination. - _replyProducer = _producerSession.createProducer(null); - _replyProducer.setDisableMessageTimestamp(true); - _replyProducer.setDeliveryMode(_persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT); - - // Set this up to listen for messages on the queue. - consumer.setMessageListener(this); - } - - /** - * Starts a stand alone ping-pong client running in verbose mode. - * - * @param args - */ - public static void main(String[] args) throws Exception - { - System.out.println("Starting..."); - - // Display help on the command line. - if (args.length == 0) - { - _logger.info("Running test with default values..."); - //usage(); - //System.exit(0); - } - - // Extract all command line parameters. - Config config = new Config(); - config.setOptions(args); - String brokerDetails = config.getHost() + ":" + config.getPort(); - String virtualpath = "test"; - String destinationName = config.getDestination(); - if (destinationName == null) - { - destinationName = DEFAULT_DESTINATION_NAME; - } - - String selector = config.getSelector(); - boolean transacted = config.isTransacted(); - boolean persistent = config.usePersistentMessages(); - boolean pubsub = config.isPubSub(); - boolean verbose = true; - - //String selector = null; - - // Instantiate the ping pong client with the command line options and start it running. - PingPongBouncer pingBouncer = - new PingPongBouncer(brokerDetails, "guest", "guest", virtualpath, destinationName, persistent, transacted, - selector, verbose, pubsub); - pingBouncer.getConnection().start(); - - System.out.println("Waiting..."); - } - - private static void usage() - { - System.err.println("Usage: PingPongBouncer \n" + "-host : broker host\n" + "-port : broker port\n" - + "-destinationname : queue/topic name\n" + "-transacted : (true/false). Default is false\n" - + "-persistent : (true/false). Default is false\n" - + "-pubsub : (true/false). Default is false\n" + "-selector : selector string\n"); - } - - /** - * This is a callback method that is notified of all messages for which this has been registered as a message - * listener on a message consumer. It sends a reply (pong) to all messages it receieves on the reply to - * destination of the message. - * - * @param message The message that triggered this callback. - */ - public void onMessage(Message message) - { - try - { - String messageCorrelationId = message.getJMSCorrelationID(); - if (_verbose) - { - _logger.info(timestampFormatter.format(new Date()) + ": Got ping with correlation id, " - + messageCorrelationId); - } - - // Get the reply to destination from the message and check it is set. - Destination responseDest = message.getJMSReplyTo(); - - if (responseDest == null) - { - _logger.debug("Cannot send reply because reply-to destination is null."); - - return; - } - - // Spew out some timing information if verbose mode is on. - if (_verbose) - { - Long timestamp = message.getLongProperty("timestamp"); - - if (timestamp != null) - { - long diff = System.currentTimeMillis() - timestamp; - _logger.info("Time to bounce point: " + diff); - } - } - - // Correlate the reply to the original. - message.setJMSCorrelationID(messageCorrelationId); - - // Send the receieved message as the pong reply. - _replyProducer.send(responseDest, message); - - if (_verbose) - { - _logger.info(timestampFormatter.format(new Date()) + ": Sent reply with correlation id, " - + messageCorrelationId); - } - - // Commit the transaction if running in transactional mode. - commitTx(_producerSession); - } - catch (JMSException e) - { - _logger.debug("There was a JMSException: " + e.getMessage(), e); - } - } - - /** - * Gets the underlying connection that this ping client is running on. - * - * @return The underlying connection that this ping client is running on. - */ - public AMQConnection getConnection() - { - return _connection; - } - - /** - * Sets the connection that this ping client is using. - * - * @param connection The ping connection. - */ - public void setConnection(AMQConnection connection) - { - this._connection = connection; - } - - /** - * Sets or clears the pub/sub flag to indiciate whether this client is pinging a queue or a topic. - * - * @param pubsub <tt>true</tt> if this client is pinging a topic, <tt>false</tt> if it is pinging a queue. - */ - public void setPubSub(boolean pubsub) - { - _isPubSub = pubsub; - } - - /** - * Checks whether this client is a p2p or pub/sub ping client. - * - * @return <tt>true</tt> if this client is pinging a topic, <tt>false</tt> if it is pinging a queue. - */ - public boolean isPubSub() - { - return _isPubSub; - } - - /** - * Convenience method to commit the transaction on the specified controlSession. If the controlSession to commit on is not - * a transactional controlSession, this method does nothing. - * - * <p/>If the {@link #_failBeforeCommit} flag is set, this will prompt the user to kill the broker before the - * commit is applied. If the {@link #_failAfterCommit} flag is set, this will prompt the user to kill the broker - * after the commit is applied. - * - * @throws javax.jms.JMSException If the commit fails and then the rollback fails. - */ - protected void commitTx(Session session) throws JMSException - { - if (session.getTransacted()) - { - try - { - if (_failBeforeCommit) - { - _logger.debug("Failing Before Commit"); - doFailover(); - } - - session.commit(); - - if (_failAfterCommit) - { - _logger.debug("Failing After Commit"); - doFailover(); - } - - _logger.debug("Session Commited."); - } - catch (JMSException e) - { - _logger.trace("JMSException on commit:" + e.getMessage(), e); - - try - { - session.rollback(); - _logger.debug("Message rolled back."); - } - catch (JMSException jmse) - { - _logger.trace("JMSE on rollback:" + jmse.getMessage(), jmse); - - // Both commit and rollback failed. Throw the rollback exception. - throw jmse; - } - } - } - } - - /** - * Prompts the user to terminate the named broker, in order to test failover functionality. This method will block - * until the user supplied some input on the terminal. - * - * @param broker The name of the broker to terminate. - */ - protected void doFailover(String broker) - { - System.out.println("Kill Broker " + broker + " now."); - try - { - System.in.read(); - } - catch (IOException e) - { } - - System.out.println("Continuing."); - } - - /** - * Prompts the user to terminate the broker, in order to test failover functionality. This method will block - * until the user supplied some input on the terminal. - */ - protected void doFailover() - { - System.out.println("Kill Broker now."); - try - { - System.in.read(); - } - catch (IOException e) - { } - - System.out.println("Continuing."); - - } - - private void createConsumerDestination(String name) - { - if (isPubSub()) - { - _consumerDestination = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, name); - } - else - { - _consumerDestination = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, name); - } - } - - /** - * A connection listener that logs out any failover complete events. Could do more interesting things with this - * at some point... - */ - public static class FailoverNotifier implements ConnectionListener - { - public void bytesSent(long count) - { } - - public void bytesReceived(long count) - { } - - public boolean preFailover(boolean redirect) - { - return true; - } - - public boolean preResubscribe() - { - return true; - } - - public void failoverComplete() - { - _logger.info("App got failover complete callback."); - } - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java b/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java deleted file mode 100644 index 639b0c72ba..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java +++ /dev/null @@ -1,1818 +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.requestreply; - -import org.apache.log4j.Logger; -import org.apache.log4j.NDC; - -import org.apache.qpid.test.framework.TestUtils; - -import org.apache.qpid.junit.extensions.BatchedThrottle; -import org.apache.qpid.junit.extensions.Throttle; -import org.apache.qpid.junit.extensions.util.CommandLineParser; -import org.apache.qpid.junit.extensions.util.ParsedProperties; - -import javax.jms.*; -import javax.naming.Context; -import javax.naming.InitialContext; -import javax.naming.NamingException; - -import java.io.*; -import java.net.InetAddress; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.SynchronousQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; - -/** - * PingPongProducer is a client that sends test messages, and waits for replies to these messages. The replies may - * either be generated by another client (see {@link PingPongBouncer}, or an extension of it may be used that listens - * to its own messages and does not send replies (see {@link org.apache.qpid.ping.PingClient}). The intention of ping - * pong producer is that it is a swiss-army knife test client that makes almost every aspect of its behaviour - * configurable. - * - * <p/>The pings are sent with a reply-to field set to a single temporary queue, which is the same for all pings. This - * means that this class has to do some work to correlate pings with pongs; it expectes the original message correlation - * id in the ping to be bounced back in the reply correlation id. - * - * <p/>This ping tool accepts a vast number of configuration options, all of which are passed in to the constructor. It - * can ping topics or queues; ping multiple destinations; do persistent pings; send messages of any size; do pings within - * transactions; control the number of pings to send in each transaction; limit its sending rate; and perform failover - * testing. A complete list of accepted parameters, default values and comments on their usage is provided here: - * - * <p/><table><caption>Parameters</caption> - * <tr><th> Parameter <th> Default <th> Comments - * <tr><td> messageSize <td> 0 <td> Message size in bytes. Not including any headers. - * <tr><td> destinationName <td> ping <td> The root name to use to generate destination names to ping. - * <tr><td> persistent <td> false <td> Determines whether peristent delivery is used. - * <tr><td> transacted <td> false <td> Determines whether messages are sent/received in transactions. - * <tr><td> broker <td> tcp://localhost:5672 <td> Determines the broker to connect to. - * <tr><td> virtualHost <td> test <td> Determines the virtual host to send all ping over. - * <tr><td> rate <td> 0 <td> The maximum rate (in hertz) to send messages at. 0 means no limit. - * <tr><td> verbose <td> false <td> The verbose flag for debugging. Prints to console on every message. - * <tr><td> pubsub <td> false <td> Whether to ping topics or queues. Uses p2p by default. - * <tr><td> failAfterCommit <td> false <td> Whether to prompt user to kill broker after a commit batch. - * <tr><td> failBeforeCommit <td> false <td> Whether to prompt user to kill broker before a commit batch. - * <tr><td> failAfterSend <td> false <td> Whether to prompt user to kill broker after a send. - * <tr><td> failBeforeSend <td> false <td> Whether to prompt user to kill broker before a send. - * <tr><td> failOnce <td> true <td> Whether to prompt for failover only once. - * <tr><td> username <td> guest <td> The username to access the broker with. - * <tr><td> password <td> guest <td> The password to access the broker with. - * <tr><td> selector <td> null <td> Not used. Defines a message selector to filter pings with. - * <tr><td> destinationCount <td> 1 <td> The number of destinations to send pings to. - * <tr><td> numConsumers <td> 1 <td> The number of consumers on each destination. - * <tr><td> timeout <td> 30000 <td> In milliseconds. The timeout to stop waiting for replies. - * <tr><td> commitBatchSize <td> 1 <td> The number of messages per transaction in transactional mode. - * <tr><td> uniqueDests <td> true <td> Whether each receivers only listens to one ping destination or all. - * <tr><td> durableDests <td> false <td> Whether or not durable destinations are used. - * <tr><td> ackMode <td> AUTO_ACK <td> The message acknowledgement mode. Possible values are: - * 0 - SESSION_TRANSACTED - * 1 - AUTO_ACKNOWLEDGE - * 2 - CLIENT_ACKNOWLEDGE - * 3 - DUPS_OK_ACKNOWLEDGE - * 257 - NO_ACKNOWLEDGE - * 258 - PRE_ACKNOWLEDGE - * <tr><td> consTransacted <td> false <td> Whether or not consumers use transactions. Defaults to the same value - * as the 'transacted' option if not seperately defined. - * <tr><td> consAckMode <td> AUTO_ACK <td> The message acknowledgement mode for consumers. Defaults to the same - * value as 'ackMode' if not seperately defined. - * <tr><td> maxPending <td> 0 <td> The maximum size in bytes, of messages sent but not yet received. - * Limits the volume of messages currently buffered on the client - * or broker. Can help scale test clients by limiting amount of buffered - * data to avoid out of memory errors. - * </table> - * - * <p/>This implements the Runnable interface with a run method that implements an infinite ping loop. The ping loop - * does all its work through helper methods, so that code wishing to run a ping-pong cycle is not forced to do so by - * starting a new thread. The command line invocation does take advantage of this ping loop. A shutdown hook is also - * registered to terminate the ping-pong loop cleanly. - * - * <p/><table id="crc"><caption>CRC Card</caption> - * <tr><th> Responsibilities <th> Collaborations - * <tr><td> Provide a ping and wait for all responses cycle. - * <tr><td> Provide command line invocation to loop the ping cycle on a configurable broker url. - * </table> - * - * @todo Use read/write lock in the onmessage, not for reading writing but to make use of a shared and exlcusive lock pair. - * Obtain read lock on all messages, before decrementing the message count. At the end of the on message method add a - * block that obtains the write lock for the very last message, releases any waiting producer. Means that the last - * message waits until all other messages have been handled before releasing producers but allows messages to be - * processed concurrently, unlike the current synchronized block. - */ -public class PingPongProducer implements Runnable, ExceptionListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(PingPongProducer.class); - - /** Holds the name of the property to determine whether of not client id is overridden at connection time. */ - public static final String OVERRIDE_CLIENT_ID_PROPNAME = "overrideClientId"; - - /** Holds the default value of the override client id flag. */ - public static final String OVERRIDE_CLIENT_ID_DEAFULT = "false"; - - /** Holds the name of the property to define the JNDI factory name with. */ - public static final String FACTORY_NAME_PROPNAME = "factoryName"; - - /** Holds the default JNDI name of the connection factory. */ - public static final String FACTORY_NAME_DEAFULT = "local"; - - /** Holds the name of the property to set the JNDI initial context properties with. */ - public static final String FILE_PROPERTIES_PROPNAME = "properties"; - - /** Holds the default file name of the JNDI initial context properties. */ - public static final String FILE_PROPERTIES_DEAFULT = "perftests.properties"; - - /** Holds the name of the property to get the test message size from. */ - public static final String MESSAGE_SIZE_PROPNAME = "messageSize"; - - /** Used to set up a default message size. */ - public static final int MESSAGE_SIZE_DEAFULT = 0; - - /** Holds the name of the property to get the ping queue name from. */ - public static final String PING_QUEUE_NAME_PROPNAME = "destinationName"; - - /** Holds the name of the default destination to send pings on. */ - public static final String PING_QUEUE_NAME_DEFAULT = "ping"; - - /** Holds the name of the property to get the queue name postfix from. */ - public static final String QUEUE_NAME_POSTFIX_PROPNAME = "queueNamePostfix"; - - /** Holds the default queue name postfix value. */ - public static final String QUEUE_NAME_POSTFIX_DEFAULT = ""; - - /** Holds the name of the property to get the test delivery mode from. */ - public static final String PERSISTENT_MODE_PROPNAME = "persistent"; - - /** Holds the message delivery mode to use for the test. */ - public static final boolean PERSISTENT_MODE_DEFAULT = false; - - /** Holds the name of the property to get the test transactional mode from. */ - public static final String TRANSACTED_PROPNAME = "transacted"; - - /** Holds the transactional mode to use for the test. */ - public static final boolean TRANSACTED_DEFAULT = false; - - /** Holds the name of the property to get the test consumer transacted mode from. */ - public static final String CONSUMER_TRANSACTED_PROPNAME = "consTransacted"; - - /** Holds the consumer transactional mode default setting. */ - public static final boolean CONSUMER_TRANSACTED_DEFAULT = false; - - /** Holds the name of the property to get the test broker url from. */ - public static final String BROKER_PROPNAME = "broker"; - - /** Holds the default broker url for the test. */ - public static final String BROKER_DEFAULT = "tcp://localhost:5672"; - - /** Holds the name of the property to get the test broker virtual path. */ - public static final String VIRTUAL_HOST_PROPNAME = "virtualHost"; - - /** Holds the default virtual path for the test. */ - public static final String VIRTUAL_HOST_DEFAULT = ""; - - /** Holds the name of the property to get the message rate from. */ - public static final String RATE_PROPNAME = "rate"; - - /** Defines the default rate (in pings per second) to send pings at. 0 means as fast as possible, no restriction. */ - public static final int RATE_DEFAULT = 0; - - /** Holds the name of the property to get the verbose mode proeprty from. */ - public static final String VERBOSE_PROPNAME = "verbose"; - - /** Holds the default verbose mode. */ - public static final boolean VERBOSE_DEFAULT = false; - - /** Holds the name of the property to get the p2p or pub/sub messaging mode from. */ - public static final String PUBSUB_PROPNAME = "pubsub"; - - /** Holds the pub/sub mode default, true means ping a topic, false means ping a queue. */ - public static final boolean PUBSUB_DEFAULT = false; - - /** Holds the name of the property to get the fail after commit flag from. */ - public static final String FAIL_AFTER_COMMIT_PROPNAME = "failAfterCommit"; - - /** Holds the default failover after commit test flag. */ - public static final boolean FAIL_AFTER_COMMIT_DEFAULT = false; - - /** Holds the name of the proeprty to get the fail before commit flag from. */ - public static final String FAIL_BEFORE_COMMIT_PROPNAME = "failBeforeCommit"; - - /** Holds the default failover before commit test flag. */ - public static final boolean FAIL_BEFORE_COMMIT_DEFAULT = false; - - /** Holds the name of the proeprty to get the fail after send flag from. */ - public static final String FAIL_AFTER_SEND_PROPNAME = "failAfterSend"; - - /** Holds the default failover after send test flag. */ - public static final boolean FAIL_AFTER_SEND_DEFAULT = false; - - /** Holds the name of the property to get the fail before send flag from. */ - public static final String FAIL_BEFORE_SEND_PROPNAME = "failBeforeSend"; - - /** Holds the default failover before send test flag. */ - public static final boolean FAIL_BEFORE_SEND_DEFAULT = false; - - /** Holds the name of the property to get the fail once flag from. */ - public static final String FAIL_ONCE_PROPNAME = "failOnce"; - - /** The default failover once flag, true means only do one failover, false means failover on every commit cycle. */ - public static final boolean FAIL_ONCE_DEFAULT = true; - - /** Holds the name of the property to get the broker access username from. */ - public static final String USERNAME_PROPNAME = "username"; - - /** Holds the default broker log on username. */ - public static final String USERNAME_DEFAULT = "guest"; - - /** Holds the name of the property to get the broker access password from. */ - public static final String PASSWORD_PROPNAME = "password"; - - /** Holds the default broker log on password. */ - public static final String PASSWORD_DEFAULT = "guest"; - - /** Holds the name of the proeprty to get the. */ - public static final String SELECTOR_PROPNAME = "selector"; - - /** Holds the default message selector. */ - public static final String SELECTOR_DEFAULT = ""; - - /** Holds the name of the property to get the destination count from. */ - public static final String DESTINATION_COUNT_PROPNAME = "destinationCount"; - - /** Defines the default number of destinations to ping. */ - public static final int DESTINATION_COUNT_DEFAULT = 1; - - /** Holds the name of the property to get the number of consumers per destination from. */ - public static final String NUM_CONSUMERS_PROPNAME = "numConsumers"; - - /** Defines the default number consumers per destination. */ - public static final int NUM_CONSUMERS_DEFAULT = 1; - - /** Holds the name of the property to get the waiting timeout for response messages. */ - public static final String TIMEOUT_PROPNAME = "timeout"; - - /** Default time to wait before assuming that a ping has timed out. */ - public static final long TIMEOUT_DEFAULT = 30000; - - /** Holds the name of the property to get the commit batch size from. */ - public static final String TX_BATCH_SIZE_PROPNAME = "commitBatchSize"; - - /** Defines the default number of pings to send in each transaction when running transactionally. */ - public static final int TX_BATCH_SIZE_DEFAULT = 1; - - /** Holds the name of the property to get the unique destinations flag from. */ - public static final String UNIQUE_DESTS_PROPNAME = "uniqueDests"; - - /** Defines the default value for the unique destinations property. */ - public static final boolean UNIQUE_DESTS_DEFAULT = true; - - /** Holds the name of the property to get the durable destinations flag from. */ - public static final String DURABLE_DESTS_PROPNAME = "durableDests"; - - /** Defines the default value of the durable destinations flag. */ - public static final boolean DURABLE_DESTS_DEFAULT = false; - - /** Holds the name of the proeprty to get the message acknowledgement mode from. */ - public static final String ACK_MODE_PROPNAME = "ackMode"; - - /** Defines the default message acknowledgement mode. */ - public static final int ACK_MODE_DEFAULT = Session.AUTO_ACKNOWLEDGE; - - /** Holds the name of the property to get the consumers message acknowledgement mode from. */ - public static final String CONSUMER_ACK_MODE_PROPNAME = "consAckMode"; - - /** Defines the default consumers message acknowledgement mode. */ - public static final int CONSUMER_ACK_MODE_DEFAULT = Session.AUTO_ACKNOWLEDGE; - - /** Holds the name of the property to get the maximum pending message size setting from. */ - public static final String MAX_PENDING_PROPNAME = "maxPending"; - - /** Defines the default value for the maximum pending message size setting. 0 means no limit. */ - public static final int MAX_PENDING_DEFAULT = 0; - - /** Defines the default prefetch size to use when consuming messages. */ - public static final int PREFETCH_DEFAULT = 100; - - /** Defines the default value of the no local flag to use when consuming messages. */ - public static final boolean NO_LOCAL_DEFAULT = false; - - /** Defines the default value of the exclusive flag to use when consuming messages. */ - public static final boolean EXCLUSIVE_DEFAULT = false; - - /** Holds the name of the property to store nanosecond timestamps in ping messages with. */ - public static final String MESSAGE_TIMESTAMP_PROPNAME = "timestamp"; - - /** Holds the name of the property to get the number of message to prefill the broker with before starting the main test. */ - public static final String PREFILL_PROPNAME = "preFill"; - - /** Defines the default value for the number of messages to prefill. 0,default, no messages. */ - public static final int PREFILL_DEFAULT = 0; - - /** Holds the name of the property to get the delay to wait in ms before starting the main test after having prefilled. */ - public static final String DELAY_BEFORE_CONSUME_PROPNAME = "delayBeforeConsume"; - - /** Defines the default value for delay in ms to wait before starting thet test run. 0,default, no delay. */ - public static final long DELAY_BEFORE_CONSUME = 0; - - /** Holds the name of the property to get when no messasges should be sent. */ - public static final String CONSUME_ONLY_PROPNAME = "consumeOnly"; - - /** Defines the default value of the consumeOnly flag to use when publishing messages is not desired. */ - public static final boolean CONSUME_ONLY_DEFAULT = false; - - /** Holds the name of the property to get when no messasges should be sent. */ - public static final String SEND_ONLY_PROPNAME = "sendOnly"; - - /** Defines the default value of the consumeOnly flag to use when publishing messages is not desired. */ - public static final boolean SEND_ONLY_DEFAULT = false; - - /** Holds the default configuration properties. */ - public static ParsedProperties defaults = new ParsedProperties(); - - static - { - defaults.setPropertyIfNull(OVERRIDE_CLIENT_ID_PROPNAME, OVERRIDE_CLIENT_ID_DEAFULT); - defaults.setPropertyIfNull(FILE_PROPERTIES_PROPNAME, FILE_PROPERTIES_DEAFULT); - defaults.setPropertyIfNull(FACTORY_NAME_PROPNAME, FACTORY_NAME_DEAFULT); - defaults.setPropertyIfNull(BROKER_PROPNAME, BROKER_DEFAULT); - defaults.setPropertyIfNull(USERNAME_PROPNAME, USERNAME_DEFAULT); - defaults.setPropertyIfNull(PASSWORD_PROPNAME, PASSWORD_DEFAULT); - defaults.setPropertyIfNull(VIRTUAL_HOST_PROPNAME, VIRTUAL_HOST_DEFAULT); - defaults.setPropertyIfNull(PING_QUEUE_NAME_PROPNAME, PING_QUEUE_NAME_DEFAULT); - defaults.setPropertyIfNull(QUEUE_NAME_POSTFIX_PROPNAME, QUEUE_NAME_POSTFIX_DEFAULT); - defaults.setPropertyIfNull(SELECTOR_PROPNAME, SELECTOR_DEFAULT); - defaults.setPropertyIfNull(TRANSACTED_PROPNAME, TRANSACTED_DEFAULT); - defaults.setPropertyIfNull(CONSUMER_TRANSACTED_PROPNAME, CONSUMER_TRANSACTED_DEFAULT); - defaults.setPropertyIfNull(PERSISTENT_MODE_PROPNAME, PERSISTENT_MODE_DEFAULT); - defaults.setPropertyIfNull(ACK_MODE_PROPNAME, ACK_MODE_DEFAULT); - defaults.setPropertyIfNull(CONSUMER_ACK_MODE_PROPNAME, CONSUMER_ACK_MODE_DEFAULT); - defaults.setPropertyIfNull(MESSAGE_SIZE_PROPNAME, MESSAGE_SIZE_DEAFULT); - defaults.setPropertyIfNull(VERBOSE_PROPNAME, VERBOSE_DEFAULT); - defaults.setPropertyIfNull(PUBSUB_PROPNAME, PUBSUB_DEFAULT); - defaults.setPropertyIfNull(UNIQUE_DESTS_PROPNAME, UNIQUE_DESTS_DEFAULT); - defaults.setPropertyIfNull(DURABLE_DESTS_PROPNAME, DURABLE_DESTS_DEFAULT); - defaults.setPropertyIfNull(FAIL_BEFORE_COMMIT_PROPNAME, FAIL_BEFORE_COMMIT_DEFAULT); - defaults.setPropertyIfNull(FAIL_AFTER_COMMIT_PROPNAME, FAIL_AFTER_COMMIT_DEFAULT); - defaults.setPropertyIfNull(FAIL_BEFORE_SEND_PROPNAME, FAIL_BEFORE_SEND_DEFAULT); - defaults.setPropertyIfNull(FAIL_AFTER_SEND_PROPNAME, FAIL_AFTER_SEND_DEFAULT); - defaults.setPropertyIfNull(FAIL_ONCE_PROPNAME, FAIL_ONCE_DEFAULT); - defaults.setPropertyIfNull(TX_BATCH_SIZE_PROPNAME, TX_BATCH_SIZE_DEFAULT); - defaults.setPropertyIfNull(DESTINATION_COUNT_PROPNAME, DESTINATION_COUNT_DEFAULT); - defaults.setPropertyIfNull(NUM_CONSUMERS_PROPNAME, NUM_CONSUMERS_DEFAULT); - defaults.setPropertyIfNull(RATE_PROPNAME, RATE_DEFAULT); - defaults.setPropertyIfNull(TIMEOUT_PROPNAME, TIMEOUT_DEFAULT); - defaults.setPropertyIfNull(MAX_PENDING_PROPNAME, MAX_PENDING_DEFAULT); - defaults.setPropertyIfNull(PREFILL_PROPNAME, PREFILL_DEFAULT); - defaults.setPropertyIfNull(DELAY_BEFORE_CONSUME_PROPNAME, DELAY_BEFORE_CONSUME); - defaults.setPropertyIfNull(CONSUME_ONLY_PROPNAME, CONSUME_ONLY_DEFAULT); - defaults.setPropertyIfNull(SEND_ONLY_PROPNAME, SEND_ONLY_DEFAULT); - } - - /** Allows setting of client ID on the connection, rather than through the connection URL. */ - protected boolean _overrideClientId; - - /** Holds the JNDI name of the JMS connection factory. */ - protected String _factoryName; - - /** Holds the name of the properties file to configure JNDI with. */ - protected String _fileProperties; - - /** Holds the broker url. */ - protected String _brokerDetails; - - /** Holds the username to access the broker with. */ - protected String _username; - - /** Holds the password to access the broker with. */ - protected String _password; - - /** Holds the virtual host on the broker to run the tests through. */ - protected String _virtualpath; - - /** Holds the root name from which to generate test destination names. */ - protected String _destinationName; - - /** Holds the default queue name postfix value. */ - protected String _queueNamePostfix; - - /** Holds the message selector to filter the pings with. */ - protected String _selector; - - /** Holds the producers transactional mode flag. */ - protected boolean _transacted; - - /** Holds the consumers transactional mode flag. */ - protected boolean _consTransacted; - - /** Determines whether this producer sends persistent messages. */ - protected boolean _persistent; - - /** Holds the acknowledgement mode used for the producers. */ - protected int _ackMode; - - /** Holds the acknowledgement mode setting for the consumers. */ - protected int _consAckMode; - - /** Determines what size of messages this producer sends. */ - protected int _messageSize; - - /** Used to indicate that the ping loop should print out whenever it pings. */ - protected boolean _verbose; - - /** Flag used to indicate if this is a point to point or pub/sub ping client. */ - protected boolean _isPubSub; - - /** Flag used to indicate if the destinations should be unique client. */ - protected boolean _isUnique; - - /** Flag used to indicate that durable destination should be used. */ - protected boolean _isDurable; - - /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover before a commit. */ - protected boolean _failBeforeCommit; - - /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover after a commit. */ - protected boolean _failAfterCommit; - - /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover before a send. */ - protected boolean _failBeforeSend; - - /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover after a send. */ - protected boolean _failAfterSend; - - /** Flag used to indicate that failover prompting should only be done on the first commit, not on every commit. */ - protected boolean _failOnce; - - /** Holds the number of sends that should be performed in every transaction when using transactions. */ - protected int _txBatchSize; - - /** Holds the number of destinations to ping. */ - protected int _noOfDestinations; - - /** Holds the number of consumers per destination. */ - protected int _noOfConsumers; - - private int[] _consumerBatchCounts; - - /** Holds the maximum send rate in herz. */ - protected int _rate; - - /** - * Holds the size of the maximum amount of pending data that the client should buffer, sending is suspended - * if this limit is breached. - */ - protected int _maxPendingSize; - - /** - * Holds the number of messages to send during the setup phase, before the clients start consuming. - */ - private Integer _preFill; - - /** - * Holds the time in ms to wait after preFilling before starting thet test. - */ - private Long _delayBeforeConsume; - - /** - * Holds a boolean value of wither this test should just consume, i.e. skips - * sending messages, but still expects to receive the specified number. - */ - private boolean _consumeOnly; - - /** - * Holds a boolean value of wither this test should just send, i.e. skips - * consuming messages, but still creates clients just doesn't start them. - */ - private boolean _sendOnly; - - - /** A source for providing sequential unique correlation ids. These will be unique within the same JVM. */ - private static AtomicLong _correlationIdGenerator = new AtomicLong(0L); - - /** A source for providing sequential unqiue ids for instances of this class to be identifed with. */ - private static AtomicInteger _instanceIdGenerator = new AtomicInteger(0); - - /** Holds this instances unique id. */ - private int instanceId; - - /** - * Holds a map from message ids to latches on which threads wait for replies. This map is shared accross multiple - * ping producers on the same JVM. - */ - private static Map<String, PerCorrelationId> perCorrelationIds = - Collections.synchronizedMap(new HashMap<String, PerCorrelationId>()); - - /** A convenient formatter to use when time stamping output. */ - protected static final DateFormat timestampFormatter = new SimpleDateFormat("hh:mm:ss:SS"); - - /** Holds the connection for the message producer. */ - protected Connection _connection; - - /** Holds the consumer connections. */ - protected Connection[] _consumerConnection; - - /** Holds the controlSession on which ping replies are received. */ - protected Session[] _consumerSession; - - /** Holds the producer controlSession, needed to create ping messages. */ - protected Session _producerSession; - - /** Holds the destination where the response messages will arrive. */ - protected Destination _replyDestination; - - /** Holds the set of destinations that this ping producer pings. */ - protected List<Destination> _pingDestinations; - - /** Used to restrict the sending rate to a specified limit. */ - protected Throttle _rateLimiter; - - /** Holds a message listener that this message listener chains all its messages to. */ - protected ChainedMessageListener _chainedMessageListener = null; - - /** - * This id generator is used to generate ids to append to the queue name to ensure that queues can be unique when - * creating multiple ping producers in the same JVM. - */ - protected static AtomicInteger _queueJVMSequenceID = new AtomicInteger(); - - /** - * This id generator is used to generates ids that are only unique within this pinger. Creating multiple pingers - * on the same JVM using this id generator will allow them to ping on the same queues. - */ - protected AtomicInteger _queueSharedID = new AtomicInteger(); - - /** Used to tell the ping loop when to terminate, it only runs while this is true. */ - protected boolean _publish = true; - - /** Holds the message producer to send the pings through. */ - protected MessageProducer _producer; - - /** Holds the message consumer to receive the ping replies through. */ - protected MessageConsumer[] _consumer; - - /** The prompt to display when asking the user to kill the broker for failover testing. */ - private static final String KILL_BROKER_PROMPT = "Kill broker now, then press Return."; - - /** Holds the name for this test client to be identified to the broker with. */ - private String _clientID; - - /** Keeps count of the total messages sent purely for debugging purposes. */ - private static AtomicInteger numSent = new AtomicInteger(); - - /** - * Holds a monitor which is used to synchronize sender and receivers threads, where the sender has elected - * to wait until the number of unreceived message is reduced before continuing to send. This monitor is a - * fair SynchronousQueue becuase that provides fair scheduling, to ensure that all producer threads get an - * equal chance to produce messages. - */ - static final SynchronousQueue _sendPauseMonitor = new SynchronousQueue(true); - - /** Keeps a count of the number of message currently sent but not received. */ - static AtomicInteger _unreceived = new AtomicInteger(0); - - /** - * Creates a ping producer with the specified parameters, of which there are many. See the class level comments - * for details. This constructor creates a connection to the broker and creates producer and consumer sessions on - * it, to send and recieve its pings and replies on. - * - * @param overrides Properties containing any desired overrides to the defaults. - * - * @throws Exception Any exceptions are allowed to fall through. - */ - public PingPongProducer(Properties overrides) throws Exception - { - // log.debug("public PingPongProducer(Properties overrides = " + overrides + "): called"); - instanceId = _instanceIdGenerator.getAndIncrement(); - - // Create a set of parsed properties from the defaults overriden by the passed in values. - ParsedProperties properties = new ParsedProperties(defaults); - properties.putAll(overrides); - - // Extract the configuration properties to set the pinger up with. - _overrideClientId = properties.getPropertyAsBoolean(OVERRIDE_CLIENT_ID_PROPNAME); - _factoryName = properties.getProperty(FACTORY_NAME_PROPNAME); - _fileProperties = properties.getProperty(FILE_PROPERTIES_PROPNAME); - _brokerDetails = properties.getProperty(BROKER_PROPNAME); - _username = properties.getProperty(USERNAME_PROPNAME); - _password = properties.getProperty(PASSWORD_PROPNAME); - _virtualpath = properties.getProperty(VIRTUAL_HOST_PROPNAME); - _destinationName = properties.getProperty(PING_QUEUE_NAME_PROPNAME); - _queueNamePostfix = properties.getProperty(QUEUE_NAME_POSTFIX_PROPNAME); - _selector = properties.getProperty(SELECTOR_PROPNAME); - _transacted = properties.getPropertyAsBoolean(TRANSACTED_PROPNAME); - _consTransacted = properties.getPropertyAsBoolean(CONSUMER_TRANSACTED_PROPNAME); - _persistent = properties.getPropertyAsBoolean(PERSISTENT_MODE_PROPNAME); - _messageSize = properties.getPropertyAsInteger(MESSAGE_SIZE_PROPNAME); - _verbose = properties.getPropertyAsBoolean(VERBOSE_PROPNAME); - _failAfterCommit = properties.getPropertyAsBoolean(FAIL_AFTER_COMMIT_PROPNAME); - _failBeforeCommit = properties.getPropertyAsBoolean(FAIL_BEFORE_COMMIT_PROPNAME); - _failAfterSend = properties.getPropertyAsBoolean(FAIL_AFTER_SEND_PROPNAME); - _failBeforeSend = properties.getPropertyAsBoolean(FAIL_BEFORE_SEND_PROPNAME); - _failOnce = properties.getPropertyAsBoolean(FAIL_ONCE_PROPNAME); - _txBatchSize = properties.getPropertyAsInteger(TX_BATCH_SIZE_PROPNAME); - _noOfDestinations = properties.getPropertyAsInteger(DESTINATION_COUNT_PROPNAME); - _noOfConsumers = properties.getPropertyAsInteger(NUM_CONSUMERS_PROPNAME); - _consumerBatchCounts = new int[_noOfConsumers]; - _rate = properties.getPropertyAsInteger(RATE_PROPNAME); - _isPubSub = properties.getPropertyAsBoolean(PUBSUB_PROPNAME); - _isUnique = properties.getPropertyAsBoolean(UNIQUE_DESTS_PROPNAME); - _isDurable = properties.getPropertyAsBoolean(DURABLE_DESTS_PROPNAME); - _ackMode = _transacted ? 0 : properties.getPropertyAsInteger(ACK_MODE_PROPNAME); - _consAckMode = _consTransacted ? 0 : properties.getPropertyAsInteger(CONSUMER_ACK_MODE_PROPNAME); - _maxPendingSize = properties.getPropertyAsInteger(MAX_PENDING_PROPNAME); - _preFill = properties.getPropertyAsInteger(PREFILL_PROPNAME); - _delayBeforeConsume = properties.getPropertyAsLong(DELAY_BEFORE_CONSUME_PROPNAME); - _consumeOnly = properties.getPropertyAsBoolean(CONSUME_ONLY_PROPNAME); - _sendOnly = properties.getPropertyAsBoolean(SEND_ONLY_PROPNAME); - - // Check that one or more destinations were specified. - if (_noOfDestinations < 1) - { - throw new IllegalArgumentException("There must be at least one destination."); - } - - // Set up a throttle to control the send rate, if a rate > 0 is specified. - if (_rate > 0) - { - _rateLimiter = new BatchedThrottle(); - _rateLimiter.setRate(_rate); - } - - // Create the connection and message producers/consumers. - // establishConnection(true, true); - } - - /** - * Establishes a connection to the broker and creates message consumers and producers based on the parameters - * that this ping client was created with. - * - * @param producer Flag to indicate whether or not the producer should be set up. - * @param consumer Flag to indicate whether or not the consumers should be set up. - * - * @throws Exception Any exceptions are allowed to fall through. - */ - public void establishConnection(boolean producer, boolean consumer) throws Exception - { - // log.debug("public void establishConnection(): called"); - - // Generate a unique identifying name for this client, based on it ip address and the current time. - InetAddress address = InetAddress.getLocalHost(); - // _clientID = address.getHostName() + System.currentTimeMillis(); - _clientID = "perftest_" + instanceId; - - // Create a connection to the broker. - createConnection(_clientID); - - // Create transactional or non-transactional sessions, based on the command line arguments. - _producerSession = _connection.createSession(_transacted, _ackMode); - - _consumerSession = new Session[_noOfConsumers]; - - for (int i = 0; i < _noOfConsumers; i++) - { - _consumerSession[i] = _consumerConnection[i].createSession(_consTransacted, _consAckMode); - } - - // Create the destinations to send pings to and receive replies from. - if (_noOfConsumers > 0) - { - _replyDestination = _consumerSession[0].createTemporaryQueue(); - } - createPingDestinations(_noOfDestinations, _selector, _destinationName, _isUnique, _isDurable); - - // Create the message producer only if instructed to. - if (producer) - { - createProducer(); - } - - // Create the message consumer only if instructed to. - if (consumer) - { - createReplyConsumers(getReplyDestinations(), _selector); - } - } - - /** - * Establishes a connection to the broker, based on the configuration parameters that this ping client was - * created with. - * - * @param clientID The clients identifier. - * - * @throws JMSException Underlying exceptions allowed to fall through. - * @throws NamingException Underlying exceptions allowed to fall through. - * @throws IOException Underlying exceptions allowed to fall through. - */ - protected void createConnection(String clientID) throws JMSException, NamingException, IOException - { - // _log.debug("protected void createConnection(String clientID = " + clientID + "): called"); - - // _log.debug("Creating a connection for the message producer."); - File propsFile = new File(_fileProperties); - InputStream is = new FileInputStream(propsFile); - Properties properties = new Properties(); - properties.load(is); - - Context context = new InitialContext(properties); - ConnectionFactory factory = (ConnectionFactory) context.lookup(_factoryName); - _connection = factory.createConnection(_username, _password); - - if (_overrideClientId) - { - _connection.setClientID(clientID); - } - - // _log.debug("Creating " + _noOfConsumers + " connections for the consumers."); - - _consumerConnection = new Connection[_noOfConsumers]; - - for (int i = 0; i < _noOfConsumers; i++) - { - _consumerConnection[i] = factory.createConnection(_username, _password); - // _consumerConnection[i].setClientID(clientID); - } - } - - /** - * Starts a ping-pong loop running from the command line. The bounce back client {@link PingPongBouncer} also needs - * to be started to bounce the pings back again. - * - * @param args The command line arguments. - */ - public static void main(String[] args) - { - try - { - Properties options = - CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties()); - - // Create a ping producer overriding its defaults with all options passed on the command line. - PingPongProducer pingProducer = new PingPongProducer(options); - pingProducer.establishConnection(true, true); - - // Start the ping producers dispatch thread running. - pingProducer._connection.start(); - - // Create a shutdown hook to terminate the ping-pong producer. - Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook()); - - // Ensure that the ping pong producer is registered to listen for exceptions on the connection too. - pingProducer._connection.setExceptionListener(pingProducer); - - // Create the ping loop thread and run it until it is terminated by the shutdown hook or exception. - Thread pingThread = new Thread(pingProducer); - pingThread.run(); - pingThread.join(); - } - catch (Exception e) - { - System.err.println(e.getMessage()); - log.error("Top level handler caught execption.", e); - System.exit(1); - } - } - - /** - * Convenience method for a short pause. - * - * @param sleepTime The time in milliseconds to pause for. - */ - public static void pause(long sleepTime) - { - if (sleepTime > 0) - { - try - { - Thread.sleep(sleepTime); - } - catch (InterruptedException ie) - { } - } - } - - /** - * Gets all the reply destinations (to listen for replies on). In this case this will just be the single reply to - * destination of this pinger. - * - * @return The single reply to destination of this pinger, wrapped in a list. - */ - public List<Destination> getReplyDestinations() - { - // log.debug("public List<Destination> getReplyDestinations(): called"); - - List<Destination> replyDestinations = new ArrayList<Destination>(); - replyDestinations.add(_replyDestination); - - // log.debug("replyDestinations = " + replyDestinations); - - return replyDestinations; - } - - /** - * Creates the producer to send the pings on. This is created without a default destination. Its persistent delivery - * flag is set accoring the ping producer creation options. - * - * @throws JMSException Any JMSExceptions are allowed to fall through. - */ - public void createProducer() throws JMSException - { - // log.debug("public void createProducer(): called"); - - _producer = (MessageProducer) _producerSession.createProducer(null); - _producer.setDeliveryMode(_persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT); - - // log.debug("Created producer for " + (_persistent ? "persistent" : "non-persistent") + " messages."); - } - - /** - * Creates consumers for the specified number of destinations. The destinations themselves are also created by this - * method. - * - * @param noOfDestinations The number of destinations to create consumers for. - * @param selector The message selector to filter the consumers with. - * @param rootName The root of the name, or actual name if only one is being created. - * @param unique <tt>true</tt> to make the destinations unique to this pinger, <tt>false</tt> to share the - * numbering with all pingers on the same JVM. - * @param durable If the destinations are durable topics. - * - * @throws JMSException Any JMSExceptions are allowed to fall through. - */ - public void createPingDestinations(int noOfDestinations, String selector, String rootName, boolean unique, - boolean durable) throws JMSException - { - /*log.debug("public void createPingDestinations(int noOfDestinations = " + noOfDestinations + ", String selector = " - + selector + ", String rootName = " + rootName + ", boolean unique = " + unique + ", boolean durable = " - + durable + "): called");*/ - - _pingDestinations = new ArrayList<Destination>(); - - // Create the desired number of ping destinations and consumers for them. - // log.debug("Creating " + noOfDestinations + " destinations to ping."); - - for (int i = 0; i < noOfDestinations; i++) - { - Destination destination; - String id; - - // Generate an id, unique within this pinger or to the whole JVM depending on the unique flag. - if (unique) - { - // log.debug("Creating unique destinations."); - id = "_" + _queueJVMSequenceID.incrementAndGet() + "_" + _connection.getClientID(); - } - else - { - // log.debug("Creating shared destinations."); - id = "_" + _queueSharedID.incrementAndGet(); - } - - // Check if this is a pub/sub pinger, in which case create topics. - if (_isPubSub) - { - destination = _producerSession.createTopic(rootName + id); - // log.debug("Created non-durable topic " + destination); - - if (durable) - { - _producerSession.createDurableSubscriber((Topic) destination, _connection.getClientID()); - } - } - // Otherwise this is a p2p pinger, in which case create queues. - else - { - destination = _producerSession.createQueue(rootName + id + _queueNamePostfix); - // log.debug("Created queue " + destination); - } - - // Keep the destination. - _pingDestinations.add(destination); - } - } - - /** - * Creates consumers for the specified destinations and registers this pinger to listen to their messages. - * - * @param destinations The destinations to listen to. - * @param selector A selector to filter the messages with. - * - * @throws javax.jms.JMSException Any JMSExceptions are allowed to fall through. - */ - public void createReplyConsumers(Collection<Destination> destinations, String selector) throws JMSException - { - /*log.debug("public void createReplyConsumers(Collection<Destination> destinations = " + destinations - + ", String selector = " + selector + "): called");*/ - - log.debug("There are " + destinations.size() + " destinations."); - log.debug("Creating " + _noOfConsumers + " consumers on each destination."); - log.debug("Total number of consumers is: " + (destinations.size() * _noOfConsumers)); - - for (Destination destination : destinations) - { - _consumer = new MessageConsumer[_noOfConsumers]; - - // If we don't have consumers then ensure we have created the - // destination. - if (_noOfConsumers == 0) - { - _producerSession.createConsumer(destination, selector, - NO_LOCAL_DEFAULT).close(); - } - - for (int i = 0; i < _noOfConsumers; i++) - { - // Create a consumer for the destination and set this pinger to listen to its messages. - _consumer[i] = _consumerSession[i].createConsumer(destination, selector, NO_LOCAL_DEFAULT); - - final int consumerNo = i; - - _consumer[i].setMessageListener(new MessageListener() - { - public void onMessage(Message message) - { - onMessageWithConsumerNo(message, consumerNo); - } - }); - - log.debug("Set consumer " + i + " to listen to replies sent to destination: " + destination); - } - } - } - - /** - * Stores the received message in the replies map, then resets the boolean latch that a thread waiting for a - * correlating reply may be waiting on. This is only done if the reply has a correlation id that is expected in the - * replies map. - * - * @param message The received message. - * @param consumerNo The consumer number within this test pinger instance. - */ - public void onMessageWithConsumerNo(Message message, int consumerNo) - { - // log.debug("public void onMessageWithConsumerNo(Message message, int consumerNo = " + consumerNo + "): called"); - try - { - long now = System.nanoTime(); - long timestamp = getTimestamp(message); - long pingTime = now - timestamp; - - // NDC.push("id" + instanceId + "/cons" + consumerNo); - - // Extract the messages correlation id. - String correlationID = message.getJMSCorrelationID(); - // log.debug("correlationID = " + correlationID); - - // int num = message.getIntProperty("MSG_NUM"); - // log.info("Message " + num + " received."); - - boolean isRedelivered = message.getJMSRedelivered(); - // log.debug("isRedelivered = " + isRedelivered); - - if (!isRedelivered) - { - // Countdown on the traffic light if there is one for the matching correlation id. - PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationID); - - if (perCorrelationId != null) - { - CountDownLatch trafficLight = perCorrelationId.trafficLight; - - // Restart the timeout timer on every message. - perCorrelationId.timeOutStart = System.nanoTime(); - - // log.debug("Reply was expected, decrementing the latch for the id, " + correlationID); - - // Release waiting senders if there are some and using maxPending limit. - if ((_maxPendingSize > 0)) - { - // Decrement the count of sent but not yet received messages. - int unreceived = _unreceived.decrementAndGet(); - int unreceivedSize = - (unreceived * ((_messageSize == 0) ? 1 : _messageSize)) - / (_isPubSub ? getConsumersPerDestination() : 1); - - // log.debug("unreceived = " + unreceived); - // log.debug("unreceivedSize = " + unreceivedSize); - - // synchronized (_sendPauseMonitor) - // { - if (unreceivedSize < _maxPendingSize) - { - _sendPauseMonitor.poll(); - } - // } - } - - // Decrement the countdown latch. Before this point, it is possible that two threads might enter this - // method simultanesouly with the same correlation id. Decrementing the latch in a synchronized block - // ensures that each thread will get a unique value for the remaining messages. - long trueCount; - long remainingCount; - - synchronized (trafficLight) - { - trafficLight.countDown(); - - trueCount = trafficLight.getCount(); - remainingCount = trueCount - 1; - - if (++_consumerBatchCounts[consumerNo] == _txBatchSize) - { - _consumerBatchCounts[consumerNo] = 0; - if (_consAckMode == 2) - { - // log.debug("Doing client ack for consumer " + consumerNo + "."); - message.acknowledge(); - } - else - { - // log.debug("Trying commit for consumer " + consumerNo + "."); - commitTx(_consumerSession[consumerNo]); - // log.info("Tx committed on consumer " + consumerNo); - } - } - - // Forward the message and remaining count to any interested chained message listener. - if (_chainedMessageListener != null) - { - _chainedMessageListener.onMessage(message, (int) remainingCount, pingTime); - } - - // Check if this is the last message, in which case release any waiting producers. This is done - // after the transaction has been committed and any listeners notified. - if (trueCount == 1) - { - trafficLight.countDown(); - } - } - } - else - { - log.warn(consumerNo + " Got unexpected message with correlationId: " + correlationID); - log.warn(consumerNo + " Map contains:" + perCorrelationIds.entrySet()); - } - } - else - { - log.warn("Got redelivered message, ignoring."); - } - } - catch (Exception e) - { - log.warn("There was a Exception: " + e.getMessage(), e); - } - finally - { - // log.debug("public void onMessageWithConsumerNo(Message message, int consumerNo): ending"); - // NDC.clear(); - } - } - - public void setupCorrelationID(String correlationId, int expectedCount) - { - PerCorrelationId perCorrelationId = new PerCorrelationId(); - - // Create a count down latch to count the number of replies with. This is created before the messages are - // sent so that the replies cannot be received before the count down is created. - // One is added to this, so that the last reply becomes a special case. The special case is that the - // chained message listener must be called before this sender can be unblocked, but that decrementing the - // countdown needs to be done before the chained listener can be called. - perCorrelationId.trafficLight = new CountDownLatch(expectedCount + 1); - - perCorrelationIds.put(correlationId, perCorrelationId); - } - - - /** - * Sends the specified number of ping message and then waits for all correlating replies. If the wait times out - * before a reply arrives, then a null reply is returned from this method. This method allows the caller to specify - * the correlation id. - * - * Can be augmented through a pre-fill property (PingPongProducer.PREFILL_PROPNAME) that will populate the destination - * with a set number of messages so the total pings sent and therefore expected will be PREFILL + numPings. - * - * If pre-fill is specified then the consumers will start paused to allow the prefilling to occur. - * - * @param message The message to send. If this is null, one is generated. - * @param numPings The number of ping messages to send. - * @param timeout The timeout in milliseconds. - * @param messageCorrelationId The message correlation id. If this is null, one is generated. - * - * @return The number of replies received. This may be less than the number sent if the timeout terminated the wait - * for all prematurely. If we are running in noConsumer=0 so send only mode then it will return the no msgs sent. - * - * @throws JMSException All underlying JMSExceptions are allowed to fall through. - * @throws InterruptedException When interrupted by a timeout - */ - public int pingAndWaitForReply(Message message, int numPings, long timeout, String messageCorrelationId) - throws JMSException, InterruptedException - { - return pingAndWaitForReply(message, numPings, 0, timeout, messageCorrelationId); - } - - public int pingAndWaitForReply(Message message, int numPings, int preFill, long timeout, String messageCorrelationId) - throws JMSException, InterruptedException - { - /*log.debug("public int pingAndWaitForReply(Message message, int numPings = " + numPings + ", long timeout = " - + timeout + ", String messageCorrelationId = " + messageCorrelationId + "): called");*/ - - int totalPingsRequested = numPings + preFill; - - // Generate a unique correlation id to put on the messages before sending them, if one was not specified. - if (messageCorrelationId == null) - { - messageCorrelationId = Long.toString(_correlationIdGenerator.incrementAndGet()); - - setupCorrelationID(messageCorrelationId, getExpectedNumPings(totalPingsRequested)); - } - - try - { - // NDC.push("prod"); - - PerCorrelationId perCorrelationId = perCorrelationIds.get(messageCorrelationId); - - // Set up the current time as the start time for pinging on the correlation id. This is used to determine - // timeouts. - perCorrelationId.timeOutStart = System.nanoTime(); - - // Send the specifed number of messages for this test - pingNoWaitForReply(message, numPings, messageCorrelationId); - - boolean timedOut; - boolean allMessagesReceived; - int numReplies; - - // We don't have a consumer so don't try and wait for the messages. - // this does mean that if the producerSession is !TXed then we may - // get to exit before all msgs have been received. - // - // Return the number of requested messages, this will let the test - // report a pass. - if (_noOfConsumers == 0 || _sendOnly) - { - return getExpectedNumPings(totalPingsRequested); - } - - do - { - // Block the current thread until replies to all the messages are received, or it times out. - perCorrelationId.trafficLight.await(timeout, TimeUnit.MILLISECONDS); - - // Work out how many replies were receieved. - numReplies = getExpectedNumPings(totalPingsRequested) - (int) perCorrelationId.trafficLight.getCount(); - - allMessagesReceived = numReplies == getExpectedNumPings(totalPingsRequested); - - // log.debug("numReplies = " + numReplies); - // log.debug("allMessagesReceived = " + allMessagesReceived); - - // Recheck the timeout condition. - long now = System.nanoTime(); - long lastMessageReceievedAt = perCorrelationId.timeOutStart; - timedOut = (now - lastMessageReceievedAt) > (timeout * 1000000); - - // log.debug("now = " + now); - // log.debug("lastMessageReceievedAt = " + lastMessageReceievedAt); - } - while (!timedOut && !allMessagesReceived); - - if ((numReplies < getExpectedNumPings(totalPingsRequested)) && _verbose) - { - log.info("Timed out (" + timeout + " ms) before all replies received on id, " + messageCorrelationId); - } - else if (_verbose) - { - log.info("Got all replies on id, " + messageCorrelationId); - } - - // commitTx(_consumerSession); - - // log.debug("public int pingAndWaitForReply(Message message, int numPings, long timeout): ending"); - - return numReplies; - } - // Ensure that the message countdown latch is always removed from the reply map. The reply map is long lived, - // so will be a memory leak if this is not done. - finally - { - // NDC.pop(); - perCorrelationIds.remove(messageCorrelationId); - } - } - - /** - * Sends the specified number of ping messages and does not wait for correlating replies. - * - * @param message The message to send. - * @param numPings The number of pings to send. - * @param messageCorrelationId A correlation id to place on all messages sent. - * - * @throws JMSException All underlying JMSExceptions are allowed to fall through. - */ - public void pingNoWaitForReply(Message message, int numPings, String messageCorrelationId) throws JMSException - { - /*log.debug("public void pingNoWaitForReply(Message message, int numPings = " + numPings - + ", String messageCorrelationId = " + messageCorrelationId + "): called");*/ - - // If we are runnning a consumeOnly test then don't send any messages - if (_consumeOnly) - { - return; - } - - if (message == null) - { - message = getTestMessage(getReplyDestinations().get(0), _messageSize, _persistent); - } - - message.setJMSCorrelationID(messageCorrelationId); - - // Set up a committed flag to detect uncommitted messages at the end of the send loop. This may occurr if the - // transaction batch size is not a factor of the number of pings. In which case an extra commit at the end is - // needed. - boolean committed = false; - - // Send all of the ping messages. - for (int i = 0; i < numPings; i++) - { - // Re-timestamp the message. - // message.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime()); - - // Send the message, passing in the message count. - committed = sendMessage(i, message); - - // Spew out per message timings on every message sonly in verbose mode. - /*if (_verbose) - { - log.info(timestampFormatter.format(new Date()) + ": Pinged at with correlation id, " + messageCorrelationId); - }*/ - } - - // Call commit if the send loop finished before reaching a batch size boundary so there may still be uncommitted messages. - if (!committed) - { - commitTx(_producerSession); - } - } - - /** - * Sends the sepcified message, applies rate limiting and possibly commits the current transaction. The count of - * messages sent so far must be specified and is used to round robin the ping destinations (where there are more - * than one), and to determine if the transaction batch size has been reached and the sent messages should be - * committed. - * - * @param i The count of messages sent so far in a loop of multiple calls to this send method. - * @param message The message to send. - * - * @return <tt>true</tt> if the messages were committed, <tt>false</tt> otherwise. - * - * @throws JMSException All underlyiung JMSExceptions are allowed to fall through. - */ - protected boolean sendMessage(int i, Message message) throws JMSException - { - try - { - NDC.push("id" + instanceId + "/prod"); - - // log.debug("protected boolean sendMessage(int i = " + i + ", Message message): called"); - // log.debug("_txBatchSize = " + _txBatchSize); - - // Round robin the destinations as the messages are sent. - Destination destination = _pingDestinations.get(i % _pingDestinations.size()); - - // Prompt the user to kill the broker when doing failover testing. - _failBeforeSend = waitForUserToPromptOnFailure(_failBeforeSend); - - // Get the test setup for the correlation id. - String correlationID = message.getJMSCorrelationID(); - PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationID); - - // If necessary, wait until the max pending message size comes within its limit. - if (_maxPendingSize > 0) - { - synchronized (_sendPauseMonitor) - { - // Used to keep track of the number of times that send has to wait. - int numWaits = 0; - - // The maximum number of waits before the test gives up and fails. This has been chosen to correspond with - // the test timeout. - int waitLimit = (int) (TIMEOUT_DEFAULT / 10000); - - while (true) - { - // Get the size estimate of sent but not yet received messages. - int unreceived = _unreceived.get(); - int unreceivedSize = - (unreceived * ((_messageSize == 0) ? 1 : _messageSize)) - / (_isPubSub ? getConsumersPerDestination() : 1); - - // log.debug("unreceived = " + unreceived); - // log.debug("unreceivedSize = " + unreceivedSize); - // log.debug("_maxPendingSize = " + _maxPendingSize); - - if (unreceivedSize > _maxPendingSize) - { - // log.debug("unreceived size estimate over limit = " + unreceivedSize); - - // Fail the test if the send has had to wait more than the maximum allowed number of times. - if (numWaits > waitLimit) - { - String errorMessage = - "Send has had to wait for the unreceivedSize (" + unreceivedSize - + ") to come below the maxPendingSize (" + _maxPendingSize + ") more that " + waitLimit - + " times."; - log.warn(errorMessage); - throw new RuntimeException(errorMessage); - } - - // Wait on the send pause barrier for the limit to be re-established. - try - { - long start = System.nanoTime(); - // _sendPauseMonitor.wait(10000); - _sendPauseMonitor.offer(new Object(), 10000, TimeUnit.MILLISECONDS); - long end = System.nanoTime(); - - // Count the wait only if it was for > 99% of the requested wait time. - if (((float) (end - start) / (float) (10000 * 1000000L)) > 0.99) - { - numWaits++; - } - } - catch (InterruptedException e) - { - // Restore the interrupted status - Thread.currentThread().interrupt(); - throw new RuntimeException(e); - } - } - else - { - break; - } - } - } - } - - // Send the message either to its round robin destination, or its default destination. - // int num = numSent.incrementAndGet(); - // message.setIntProperty("MSG_NUM", num); - setTimestamp(message); - - if (destination == null) - { - _producer.send(message); - } - else - { - _producer.send(destination, message); - } - - // Increase the unreceived size, this may actually happen after the message is received. - // The unreceived size is incremented by the number of consumers that will get a copy of the message, - // in pub/sub mode. - if (_maxPendingSize > 0) - { - int newUnreceivedCount = _unreceived.addAndGet(_isPubSub ? getConsumersPerDestination() : 1); - // log.debug("newUnreceivedCount = " + newUnreceivedCount); - } - - // Apply message rate throttling if a rate limit has been set up. - if (_rateLimiter != null) - { - _rateLimiter.throttle(); - } - - // Call commit every time the commit batch size is reached. - boolean committed = false; - - // Commit on every transaction batch size boundary. Here i + 1 is the count of actual messages sent. - if (((i + 1) % _txBatchSize) == 0) - { - // log.debug("Trying commit on producer session."); - committed = commitTx(_producerSession); - } - - return committed; - } - finally - { - NDC.clear(); - } - } - - /** - * If the specified fail flag is set, this method waits for the user to cause a failure and then indicate to the - * test that the failure has occurred, before the method returns. - * - * @param failFlag The fail flag to test. - * - * @return The new value for the fail flag. If the {@link #_failOnce} flag is set, then each fail flag is only - * used once, then reset. - */ - private boolean waitForUserToPromptOnFailure(boolean failFlag) - { - if (failFlag) - { - if (_failOnce) - { - failFlag = false; - } - - // log.debug("Failing Before Send"); - waitForUser(KILL_BROKER_PROMPT); - } - - return failFlag; - } - - /** - * Implements a single iteration of the ping loop. This sends the number of pings specified by the transaction batch - * size property, and waits for replies to all of them. Any errors cause the publish flag to be cleared, which will - * terminate the pinger. - */ - public void pingLoop() - { - try - { - // Generate a sample message and time stamp it. - Message msg = getTestMessage(_replyDestination, _messageSize, _persistent); - // setTimestamp(msg); - - // Send the message and wait for a reply. - pingAndWaitForReply(msg, TX_BATCH_SIZE_DEFAULT, TIMEOUT_DEFAULT, null); - } - catch (JMSException e) - { - _publish = false; - // log.debug("There was a JMSException: " + e.getMessage(), e); - } - catch (InterruptedException e) - { - _publish = false; - // log.debug("There was an interruption: " + e.getMessage(), e); - } - } - - /** - * Sets a chained message listener. The message listener on this pinger, chains all its messages to the one set - * here. - * - * @param messageListener The chained message listener. - */ - public void setChainedMessageListener(ChainedMessageListener messageListener) - { - _chainedMessageListener = messageListener; - } - - /** Removes any chained message listeners from this pinger. */ - public void removeChainedMessageListener() - { - _chainedMessageListener = null; - } - - /** - * Generates a test message of the specified size, with the specified reply-to destination and persistence flag. - * - * @param replyQueue The reply-to destination for the message. - * @param messageSize The desired size of the message in bytes. - * @param persistent <tt>true</tt> if the message should use persistent delivery, <tt>false</tt> otherwise. - * - * @return A freshly generated test message. - * - * @throws javax.jms.JMSException All underlying JMSException are allowed to fall through. - */ - public Message getTestMessage(Destination replyQueue, int messageSize, boolean persistent) throws JMSException - { - // return TestMessageFactory.newObjectMessage(_producerSession, replyQueue, messageSize, persistent); - return TestUtils.createTestMessageOfSize(_producerSession, messageSize); - } - - /** - * Sets the current time in nanoseconds as the timestamp on the message. - * - * @param msg The message to timestamp. - * - * @throws JMSException Any JMSExceptions are allowed to fall through. - */ - protected void setTimestamp(Message msg) throws JMSException - { - /*if (((AMQSession)_producerSession).isStrictAMQP()) - { - ((AMQMessage)msg).setTimestampProperty(new AMQShortString(MESSAGE_TIMESTAMP_PROPNAME), System.nanoTime()); - } - else - {*/ - msg.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime()); - // } - } - - /** - * Extracts the nanosecond timestamp from a message. - * - * @param msg The message to extract the time stamp from. - * - * @return The timestamp in nanos. - * - * @throws JMSException Any JMSExceptions are allowed to fall through. - */ - protected long getTimestamp(Message msg) throws JMSException - { - /*if (((AMQSession)_producerSession).isStrictAMQP()) - { - Long value = ((AMQMessage)msg).getTimestampProperty(new AMQShortString(MESSAGE_TIMESTAMP_PROPNAME)); - - return (value == null) ? 0L : value; - } - else - {*/ - return msg.getLongProperty(PingPongProducer.MESSAGE_TIMESTAMP_PROPNAME); - // } - } - - /** - * Stops the ping loop by clearing the publish flag. The current loop will complete when it notices that this flag - * has been cleared. - */ - public void stop() - { - _publish = false; - } - - /** - * Starts the producer and consumer connections. - * - * @throws JMSException Any JMSExceptions are allowed to fall through. - */ - public void start() throws JMSException - { - // log.debug("public void start(): called"); - - _connection.start(); - // log.debug("Producer started."); - - for (int i = 0; i < _noOfConsumers; i++) - { - _consumerConnection[i].start(); - // log.debug("Consumer " + i + " started."); - } - } - - /** Implements a ping loop that repeatedly pings until the publish flag becomes false. */ - public void run() - { - // Keep running until the publish flag is cleared. - while (_publish) - { - pingLoop(); - } - } - - /** - * Callback method, implementing ExceptionListener. This should be registered to listen for exceptions on the - * connection, this clears the publish flag which in turn will halt the ping loop. - * - * @param e The exception that triggered this callback method. - */ - public void onException(JMSException e) - { - // log.debug("public void onException(JMSException e = " + e + "): called", e); - _publish = false; - } - - /** - * Gets a shutdown hook that will cleanly shut this down when it is running the ping loop. This can be registered - * with the runtime system as a shutdown hook. - * - * @return A shutdown hook for the ping loop. - */ - public Thread getShutdownHook() - { - return new Thread(new Runnable() - { - public void run() - { - stop(); - } - }); - } - - /** - * Closes all of the producer and consumer connections. - * - * @throws JMSException All JMSException are allowed to fall through. - */ - public void close() throws JMSException - { - // log.debug("public void close(): called"); - - try - { - if (_connection != null) - { - // log.debug("Before close producer connection."); - _connection.close(); - // log.debug("Closed producer connection."); - } - - for (int i = 0; i < _noOfConsumers; i++) - { - if (_consumerConnection[i] != null) - { - // log.debug("Before close consumer connection " + i + "."); - _consumerConnection[i].close(); - // log.debug("Closed consumer connection " + i + "."); - } - } - } - finally - { - _connection = null; - _producerSession = null; - _consumerSession = null; - _consumerConnection = null; - _producer = null; - _consumer = null; - _pingDestinations = null; - _replyDestination = null; - } - } - - /** - * Convenience method to commit the transaction on the specified controlSession. If the controlSession to commit on is not a - * transactional controlSession, this method does nothing (unless the failover after send flag is set). - * - * <p/>If the {@link #_failAfterSend} flag is set, this will prompt the user to kill the broker before the commit is - * applied. This flag applies whether the pinger is transactional or not. - * - * <p/>If the {@link #_failBeforeCommit} flag is set, this will prompt the user to kill the broker before the commit - * is applied. If the {@link #_failAfterCommit} flag is set, this will prompt the user to kill the broker after the - * commit is applied. These flags will only apply if using a transactional pinger. - * - * @param session The controlSession to commit - * - * @return <tt>true</tt> if the controlSession was committed, <tt>false</tt> if it was not. - * - * @throws javax.jms.JMSException If the commit fails and then the rollback fails. - * - * @todo Consider moving the fail after send logic into the send method. It is confusing to have it in this commit - * method, because commits only apply to transactional pingers, but fail after send applied to transactional and - * non-transactional alike. - */ - protected boolean commitTx(Session session) throws JMSException - { - // log.debug("protected void commitTx(Session session): called"); - - boolean committed = false; - - _failAfterSend = waitForUserToPromptOnFailure(_failAfterSend); - - if (session.getTransacted()) - { - // log.debug("Session is transacted."); - - try - { - _failBeforeCommit = waitForUserToPromptOnFailure(_failBeforeCommit); - - long start = System.nanoTime(); - session.commit(); - committed = true; - // log.debug("Time taken to commit :" + ((System.nanoTime() - start) / 1000000f) + " ms"); - - _failAfterCommit = waitForUserToPromptOnFailure(_failAfterCommit); - - // log.debug("Session Commited."); - } - catch (JMSException e) - { - // log.debug("JMSException on commit:" + e.getMessage(), e); - - try - { - session.rollback(); - // log.debug("Message rolled back."); - } - catch (JMSException jmse) - { - // log.debug("JMSE on rollback:" + jmse.getMessage(), jmse); - - // Both commit and rollback failed. Throw the rollback exception. - throw jmse; - } - } - } - - return committed; - } - - /** - * Outputs a prompt to the console and waits for the user to press return. - * - * @param prompt The prompt to display on the console. - */ - public void waitForUser(String prompt) - { - System.out.println(prompt); - - try - { - System.in.read(); - } - catch (IOException e) - { - // Ignored. - } - - System.out.println("Continuing."); - } - - /** - * Gets the number of consumers that are listening to each destination in the test. - * - * @return int The number of consumers subscribing to each topic. - */ - public int getConsumersPerDestination() - { - return _noOfConsumers; - } - - /** - * Calculates how many pings are expected to be received for the given number sent. - * - * Note : that if you have set noConsumers to 0 then this will also return 0 - * in the case of PubSub testing. This is correct as without consumers there - * will be no-one to receive the sent messages so they will be unable to respond. - * - * @param numpings The number of pings that will be sent. - * - * @return The number that should be received, for the test to pass. - */ - public int getExpectedNumPings(int numpings) - { - // Wow, I'm freaking sorry about this return here... - return ((_failAfterSend || _failBeforeCommit) ? numpings - 1: numpings) * - (_isPubSub ? getConsumersPerDestination() : 1); - } - - /** - * Defines a chained message listener interface that can be attached to this pinger. Whenever this pinger's {@link - * PingPongProducer#onMessageWithConsumerNo} method is called, the chained listener set through the {@link - * PingPongProducer#setChainedMessageListener} method is passed the message, and the remaining expected count of - * messages with that correlation id. - * - * <p/>Provided only one pinger is producing messages with that correlation id, the chained listener will always be - * given unique message counts. It will always be called while the producer waiting for all messages to arrive is - * still blocked. - */ - public static interface ChainedMessageListener - { - /** - * Notifies interested listeners about message arrival and important test stats, the number of messages - * remaining in the test, and the messages send timestamp. - * - * @param message The newly arrived message. - * @param remainingCount The number of messages left to complete the test. - * @param latency The nanosecond latency of the message. - * - * @throws JMSException Any JMS exceptions is allowed to fall through. - */ - public void onMessage(Message message, int remainingCount, long latency) throws JMSException; - } - - /** - * Holds information on each correlation id. The countdown latch, the current timeout timer... More stuff to be - * added to this: read/write lock to make onMessage more concurrent as described in class header comment. - */ - protected static class PerCorrelationId - { - /** Holds a countdown on number of expected messages. */ - CountDownLatch trafficLight; - - /** Holds the last timestamp that the timeout was reset to. */ - Long timeOutStart; - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java b/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java deleted file mode 100644 index 009254c612..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java +++ /dev/null @@ -1,251 +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.requestreply; - -import junit.framework.Assert; -import junit.framework.Test; -import junit.framework.TestSuite; - -import org.apache.log4j.Logger; - -import org.apache.qpid.junit.extensions.AsymptoticTestCase; -import org.apache.qpid.junit.extensions.util.ParsedProperties; -import org.apache.qpid.junit.extensions.util.TestContextProperties; - -import javax.jms.*; - -/** - * PingPongTestPerf is a full round trip ping test, that has been written with the intention of being scaled up to run - * many times simultaneously to simluate many clients/producer/connections. A full round trip ping sends a message from - * a producer to a conumer, then the consumer replies to the message on a temporary queue. - * - * <p/>A single run of the test using the default JUnit test runner will result in the sending and timing of the number - * of pings specified by the test size and time how long it takes for all of these to complete. This test may be scaled - * up using a suitable JUnit test runner. See {@link org.apache.qpid.junit.extensions.TKTestRunner} for more - * information on how to do this. - * - * <p/>The setup/teardown cycle establishes a connection to a broker and sets up a queue to send ping messages to and a - * temporary queue for replies. This setup is only established once for all the test repeats, but each test threads - * gets its own connection/producer/consumer, this is only re-established if the connection is lost. - * - * <p/>The test cycle is: Connects to a queue, creates a temporary queue, creates messages containing a property that - * is the name of the temporary queue, fires off many messages on the original queue and waits for them all to come - * back on the temporary queue. - * - * <p/>Configurable test properties: message size, transacted or not, persistent or not. Broker connection details. - * - * <p><table id="crc"><caption>CRC Card</caption> - * <tr><th> Responsibilities <th> Collaborations - * </table> - */ -public class PingPongTestPerf extends AsymptoticTestCase -{ - private static Logger _logger = Logger.getLogger(PingPongTestPerf.class); - - /** Thread local to hold the per-thread test setup fields. */ - ThreadLocal<PerThreadSetup> threadSetup = new ThreadLocal<PerThreadSetup>(); - - // Set up a property reader to extract the test parameters from. Once ContextualProperties is available in - // the project dependencies, use it to get property overrides for configurable tests and to notify the test runner - // of the test parameters to log with the results. It also providers some basic type parsing convenience methods. - // private Properties testParameters = System.getProperties(); - private ParsedProperties testParameters = - TestContextProperties.getInstance(PingPongProducer.defaults /*System.getProperties()*/); - - public PingPongTestPerf(String name) - { - super(name); - - _logger.debug(testParameters); - - // Sets up the test parameters with defaults. - /*testParameters.setPropertyIfNull(PingPongProducer.TX_BATCH_SIZE_PROPNAME, - Integer.toString(PingPongProducer.TX_BATCH_SIZE_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.MESSAGE_SIZE_PROPNAME, - Integer.toString(PingPongProducer.MESSAGE_SIZE_DEAFULT)); - testParameters.setPropertyIfNull(PingPongProducer.PING_QUEUE_NAME_PROPNAME, - PingPongProducer.PING_QUEUE_NAME_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.PERSISTENT_MODE_PROPNAME, - Boolean.toString(PingPongProducer.PERSISTENT_MODE_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.TRANSACTED_PROPNAME, - Boolean.toString(PingPongProducer.TRANSACTED_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.BROKER_PROPNAME, PingPongProducer.BROKER_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.USERNAME_PROPNAME, PingPongProducer.USERNAME_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.PASSWORD_PROPNAME, PingPongProducer.PASSWORD_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.VIRTUAL_HOST_PROPNAME, PingPongProducer.VIRTUAL_HOST_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.VERBOSE_PROPNAME, - Boolean.toString(PingPongProducer.VERBOSE_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.RATE_PROPNAME, Integer.toString(PingPongProducer.RATE_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.PUBSUB_PROPNAME, - Boolean.toString(PingPongProducer.PUBSUB_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.TX_BATCH_SIZE_PROPNAME, - Integer.toString(PingPongProducer.TX_BATCH_SIZE_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.TIMEOUT_PROPNAME, Long.toString(PingPongProducer.TIMEOUT_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.DESTINATION_COUNT_PROPNAME, - Integer.toString(PingPongProducer.DESTINATION_COUNT_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.FAIL_AFTER_COMMIT_PROPNAME, - PingPongProducer.FAIL_AFTER_COMMIT_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.FAIL_BEFORE_COMMIT_PROPNAME, - PingPongProducer.FAIL_BEFORE_COMMIT_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.FAIL_AFTER_SEND_PROPNAME, - PingPongProducer.FAIL_AFTER_SEND_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.FAIL_BEFORE_SEND_PROPNAME, - PingPongProducer.FAIL_BEFORE_SEND_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.FAIL_ONCE_PROPNAME, PingPongProducer.FAIL_ONCE_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.UNIQUE_DESTS_PROPNAME, - Boolean.toString(PingPongProducer.UNIQUE_DESTS_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.ACK_MODE_PROPNAME, - Integer.toString(PingPongProducer.ACK_MODE_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.PAUSE_AFTER_BATCH_PROPNAME, - PingPongProducer.PAUSE_AFTER_BATCH_DEFAULT);*/ - } - - /** - * Compile all the tests into a test suite. - */ - public static Test suite() - { - // Build a new test suite - TestSuite suite = new TestSuite("Ping-Pong Performance Tests"); - - // Run performance tests in read committed mode. - suite.addTest(new PingPongTestPerf("testPingPongOk")); - - return suite; - } - - private static void setSystemPropertyIfNull(String propName, String propValue) - { - if (System.getProperty(propName) == null) - { - System.setProperty(propName, propValue); - } - } - - public void testPingPongOk(int numPings) throws Exception - { - // Get the per thread test setup to run the test through. - PerThreadSetup perThreadSetup = threadSetup.get(); - - // Generate a sample message. This message is already time stamped and has its reply-to destination set. - Message msg = - perThreadSetup._testPingProducer.getTestMessage(perThreadSetup._testPingProducer.getReplyDestinations().get(0), - testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME), - testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME)); - - // Send the message and wait for a reply. - int numReplies = - perThreadSetup._testPingProducer.pingAndWaitForReply(msg, numPings, PingPongProducer.TIMEOUT_DEFAULT, null); - - // Fail the test if the timeout was exceeded. - if (numReplies != numPings) - { - Assert.fail("The ping timed out, got " + numReplies + " out of " + numPings); - } - } - - /** - * Performs test fixture creation on a per thread basis. This will only be called once for each test thread. - */ - public void threadSetUp() - { - try - { - PerThreadSetup perThreadSetup = new PerThreadSetup(); - - // Extract the test set up paramaeters. - String brokerDetails = testParameters.getProperty(PingPongProducer.BROKER_PROPNAME); - String username = testParameters.getProperty(PingPongProducer.USERNAME_PROPNAME); - String password = testParameters.getProperty(PingPongProducer.PASSWORD_PROPNAME); - String virtualPath = testParameters.getProperty(PingPongProducer.VIRTUAL_HOST_PROPNAME); - String destinationName = testParameters.getProperty(PingPongProducer.PING_QUEUE_NAME_PROPNAME); - boolean persistent = testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME); - boolean transacted = testParameters.getPropertyAsBoolean(PingPongProducer.TRANSACTED_PROPNAME); - String selector = testParameters.getProperty(PingPongProducer.SELECTOR_PROPNAME); - boolean verbose = testParameters.getPropertyAsBoolean(PingPongProducer.VERBOSE_PROPNAME); - boolean pubsub = testParameters.getPropertyAsBoolean(PingPongProducer.PUBSUB_PROPNAME); - - synchronized (this) - { - // Establish a bounce back client on the ping queue to bounce back the pings. - perThreadSetup._testPingBouncer = - new PingPongBouncer(brokerDetails, username, password, virtualPath, destinationName, persistent, - transacted, selector, verbose, pubsub); - - // Start the connections for client and producer running. - perThreadSetup._testPingBouncer.getConnection().start(); - - // Establish a ping-pong client on the ping queue to send the pings and receive replies with. - perThreadSetup._testPingProducer = new PingPongProducer(testParameters); - perThreadSetup._testPingProducer.establishConnection(true, true); - perThreadSetup._testPingProducer.start(); - } - - // Attach the per-thread set to the thread. - threadSetup.set(perThreadSetup); - } - catch (Exception e) - { - _logger.warn("There was an exception during per thread setup.", e); - } - } - - /** - * Performs test fixture clean - */ - public void threadTearDown() - { - _logger.debug("public void threadTearDown(): called"); - - try - { - // Get the per thread test fixture. - PerThreadSetup perThreadSetup = threadSetup.get(); - - // Close the pingers so that it cleans up its connection cleanly. - synchronized (this) - { - perThreadSetup._testPingProducer.close(); - // perThreadSetup._testPingBouncer.close(); - } - - // Ensure the per thread fixture is reclaimed. - threadSetup.remove(); - } - catch (JMSException e) - { - _logger.warn("There was an exception during per thread tear down."); - } - } - - protected static class PerThreadSetup - { - /** - * Holds the test ping-pong producer. - */ - private PingPongProducer _testPingProducer; - - /** - * Holds the test ping client. - */ - private PingPongBouncer _testPingBouncer; - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/topic/Config.java b/java/perftests/src/main/java/org/apache/qpid/topic/Config.java deleted file mode 100644 index d5c0979399..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/topic/Config.java +++ /dev/null @@ -1,326 +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.topic; - -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.config.ConnectorConfig; -import org.apache.qpid.config.Connector; -import org.apache.qpid.config.AbstractConfig; - -import javax.jms.Connection; - -public class Config extends AbstractConfig implements ConnectorConfig -{ - - private String host = "localhost"; - private int port = 5672; - private String factory = null; - - private int payload = 256; - private int messages = 1000; - private int clients = 1; - private int batch = 1; - private long delay = 1; - private int warmup; - private int ackMode= AMQSession.NO_ACKNOWLEDGE; - private String clientId; - private String subscriptionId; - private String selector; - private String destinationName; - private boolean persistent; - private boolean transacted; - private int destinationsCount; - private int batchSize; - private int rate; - private boolean ispubsub; - private long timeout; - - public Config() - { - } - - public int getAckMode() - { - return ackMode; - } - - public void setPayload(int payload) - { - this.payload = payload; - } - - public int getPayload() - { - return payload; - } - - void setClients(int clients) - { - this.clients = clients; - } - - int getClients() - { - return clients; - } - - void setMessages(int messages) - { - this.messages = messages; - } - - public int getMessages() - { - return messages; - } - - public int getBatchSize() - { - return batchSize; - } - - public int getRate() - { - return rate; - } - - public int getDestinationsCount() - { - return destinationsCount; - } - - public String getHost() - { - return host; - } - - public void setHost(String host) - { - this.host = host; - } - - public int getPort() - { - return port; - } - - public String getFactory() - { - return factory; - } - - public void setPort(int port) - { - this.port = port; - } - - int getBatch() - { - return batch; - } - - void setBatch(int batch) - { - this.batch = batch; - } - - int getWarmup() - { - return warmup; - } - - void setWarmup(int warmup) - { - this.warmup = warmup; - } - - public long getDelay() - { - return delay; - } - - public void setDelay(long delay) - { - this.delay = delay; - } - - public long getTimeout() - { - return timeout; - } - - public void setTimeout(long time) - { - this.timeout = time; - } - - public String getClientId() - { - return clientId; - } - - public String getSubscriptionId() - { - return subscriptionId; - } - - public String getSelector() - { - return selector; - } - - public String getDestination() - { - return destinationName; - } - - public boolean usePersistentMessages() - { - return persistent; - } - - public boolean isTransacted() - { - return transacted; - } - - public boolean isPubSub() - { - return ispubsub; - } - - public void setOption(String key, String value) - { - if("-host".equalsIgnoreCase(key)) - { - setHost(value); - } - else if("-port".equalsIgnoreCase(key)) - { - try - { - setPort(Integer.parseInt(value)); - } - catch(NumberFormatException e) - { - throw new RuntimeException("Bad port number: " + value, e); - } - } - else if("-payload".equalsIgnoreCase(key)) - { - setPayload(parseInt("Bad payload size", value)); - } - else if("-messages".equalsIgnoreCase(key)) - { - setMessages(parseInt("Bad message count", value)); - } - else if("-clients".equalsIgnoreCase(key)) - { - setClients(parseInt("Bad client count", value)); - } - else if("-batch".equalsIgnoreCase(key)) - { - setBatch(parseInt("Bad batch count", value)); - } - else if("-delay".equalsIgnoreCase(key)) - { - setDelay(parseLong("Bad batch delay", value)); - } - else if("-warmup".equalsIgnoreCase(key)) - { - setWarmup(parseInt("Bad warmup count", value)); - } - else if("-ack".equalsIgnoreCase(key)) - { - ackMode = parseInt("Bad ack mode", value); - } - else if("-factory".equalsIgnoreCase(key)) - { - factory = value; - } - else if("-clientId".equalsIgnoreCase(key)) - { - clientId = value; - } - else if("-subscriptionId".equalsIgnoreCase(key)) - { - subscriptionId = value; - } - else if("-persistent".equalsIgnoreCase(key)) - { - persistent = "true".equalsIgnoreCase(value); - } - else if("-transacted".equalsIgnoreCase(key)) - { - transacted = "true".equalsIgnoreCase(value); - } - else if ("-destinationscount".equalsIgnoreCase(key)) - { - destinationsCount = parseInt("Bad destinations count", value); - } - else if ("-batchsize".equalsIgnoreCase(key)) - { - batchSize = parseInt("Bad batch size", value); - } - else if ("-rate".equalsIgnoreCase(key)) - { - rate = parseInt("MEssage rate", value); - } - else if("-pubsub".equalsIgnoreCase(key)) - { - ispubsub = "true".equalsIgnoreCase(value); - } - else if("-selector".equalsIgnoreCase(key)) - { - selector = value; - } - else if("-destinationname".equalsIgnoreCase(key)) - { - destinationName = value; - } - else if("-timeout".equalsIgnoreCase(key)) - { - setTimeout(parseLong("Bad timeout data", value)); - } - else - { - System.out.println("Ignoring unrecognised option: " + key); - } - } - - static String getAckModeDescription(int ackMode) - { - switch(ackMode) - { - case AMQSession.NO_ACKNOWLEDGE: return "NO_ACKNOWLEDGE"; - case AMQSession.AUTO_ACKNOWLEDGE: return "AUTO_ACKNOWLEDGE"; - case AMQSession.CLIENT_ACKNOWLEDGE: return "CLIENT_ACKNOWLEDGE"; - case AMQSession.DUPS_OK_ACKNOWLEDGE: return "DUPS_OK_ACKNOWELDGE"; - case AMQSession.PRE_ACKNOWLEDGE: return "PRE_ACKNOWLEDGE"; - } - return "AckMode=" + ackMode; - } - - public Connection createConnection() throws Exception - { - return new Connector().createConnection(this); - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/topic/Listener.java b/java/perftests/src/main/java/org/apache/qpid/topic/Listener.java deleted file mode 100644 index 6dcea42bfe..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/topic/Listener.java +++ /dev/null @@ -1,303 +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.topic; - -import java.util.Random; - -import javax.jms.*; - -import org.apache.log4j.Logger; -import org.apache.log4j.NDC; - -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; - -/** - * This class has not kept up to date with the topic_listener in the cpp tests. It should provide identical behaviour for - * cross testing the java and cpp clients. - * - * <p/>How the cpp topic_publisher operates: - * It publishes text messages to the default topic exchange, on virtual host "/test", on the topic "topic_control", for - * the specified number of test messages to be sent. - * It publishes a report request message (on same topic), with the header text field "TYPE", value "REPORT_REQUEST", - * optionally within a transaction, and waits for the specified number of consumers to reply to this request. The - * listeners should reply to this message on a queue named "response", on virtual host "/test", with some sort of message - * about the number of messages received and how long it took, although the publisher never looks at the message content. - * The publisher then send a message (on the same topic), with the header text field "TYPE", value "TERMINATION_REQUEST", - * which the listener should close its connection and terminate upon receipt of. - * - * @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 #createReportResponseMessage(String)} once a better test exists. - */ -public class Listener implements MessageListener -{ - private static Logger log = Logger.getLogger(Listener.class); - - public static final String CONTROL_TOPIC = "topic_control"; - public static final String RESPONSE_QUEUE = "response"; - - private final Topic _topic; - //private final Topic _control; - - 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; - private static String clientId; - - Listener(Connection connection, int ackMode, String name) throws Exception - { - log.debug("Listener(Connection connection = " + connection + ", int ackMode = " + ackMode + ", String name = " + name - + "): called"); - - _connection = connection; - _session = connection.createSession(false, ackMode); - - if (_session instanceof AMQSession) - { - _topic = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, CONTROL_TOPIC); - //_control = new AMQTopic(CONTROL_TOPIC); - _response = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, RESPONSE_QUEUE); - } - else - { - _topic = _session.createTopic(CONTROL_TOPIC); - //_control = _session.createTopic(CONTROL_TOPIC); - _response = _session.createQueue(RESPONSE_QUEUE); - } - - //register for events - if (name == null) - { - log.debug("Calling _factory.createTopicConsumer().setMessageListener(this)"); - createTopicConsumer().setMessageListener(this); - } - else - { - log.debug("Calling createDurableTopicConsumer(name).setMessageListener(this)"); - createDurableTopicConsumer(name).setMessageListener(this); - } - - _connection.start(); - - _controller = createControlPublisher(); - System.out.println("Waiting for messages " + Config.getAckModeDescription(ackMode) - + - ((name == null) - ? "" : (" (subscribed with name " + name + " and client id " + connection.getClientID() + ")")) - + "..."); - } - - public static void main(String[] argv) throws Exception - { - clientId = "Listener-" + System.currentTimeMillis(); - - NDC.push(clientId); - - Config config = new Config(); - config.setOptions(argv); - - //Connection con = config.createConnection(); - Connection con = - new AMQConnection("amqp://guest:guest@testid/test?brokerlist='" + config.getHost() + ":" + config.getPort() - + "'"); - - if (config.getClientId() != null) - { - con.setClientID(config.getClientId()); - } - - new Listener(con, config.getAckMode(), config.getSubscriptionId()); - - NDC.pop(); - NDC.remove(); - } - - /** - * Checks whether or not a text field on a message has the specified value. - * - * @param m The message to check. - * @param fieldName The name of the field to check. - * @param value The expected value of the field to compare with. - * - * @return <tt>true</tt>If the specified field has the specified value, <tt>fals</tt> otherwise. - * - * @throws JMSException Any JMSExceptions are allowed to fall through. - */ - private static boolean checkTextField(Message m, String fieldName, String value) throws JMSException - { - log.debug("private static boolean checkTextField(Message m = " + m + ", String fieldName = " + fieldName - + ", String value = " + value + "): called"); - - String comp = m.getStringProperty(fieldName); - log.debug("comp = " + comp); - - boolean result = (comp != null) && comp.equals(value); - log.debug("result = " + result); - - return result; - } - - public void onMessage(Message message) - { - NDC.push(clientId); - - log.debug("public void onMessage(Message message = " + message + "): called"); - - if (!init) - { - start = System.nanoTime() / 1000000; - count = 0; - init = true; - } - - try - { - if (isShutdown(message)) - { - log.debug("Got a shutdown message."); - shutdown(); - } - else if (isReport(message)) - { - log.debug("Got a report request message."); - - // Send the report. - report(); - init = false; - } - } - catch (JMSException e) - { - log.warn("There was a JMSException during onMessage.", e); - } - finally - { - NDC.pop(); - } - } - - Message createReportResponseMessage(String msg) throws JMSException - { - Message message = _session.createTextMessage(msg); - - // Shove some more field table type 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"); - - return message; - } - - boolean isShutdown(Message m) throws JMSException - { - boolean result = checkTextField(m, "TYPE", "TERMINATION_REQUEST"); - - //log.debug("isShutdown = " + result); - - return result; - } - - boolean isReport(Message m) throws JMSException - { - boolean result = checkTextField(m, "TYPE", "REPORT_REQUEST"); - - //log.debug("isReport = " + result); - - return result; - } - - MessageConsumer createTopicConsumer() throws Exception - { - return _session.createConsumer(_topic); - } - - MessageConsumer createDurableTopicConsumer(String name) throws Exception - { - return _session.createDurableSubscriber(_topic, name); - } - - MessageProducer createControlPublisher() throws Exception - { - return _session.createProducer(_response); - } - - private void shutdown() - { - try - { - _session.close(); - _connection.stop(); - _connection.close(); - } - catch (Exception e) - { - e.printStackTrace(System.out); - } - } - - private void report() - { - log.debug("private void report(): called"); - - try - { - String msg = getReport(); - _controller.send(createReportResponseMessage(msg)); - log.debug("Sent report: " + msg); - } - catch (Exception e) - { - e.printStackTrace(System.out); - } - } - - private String getReport() - { - long time = ((System.nanoTime() / 1000000) - start); - - return "Received " + count + " in " + time + "ms"; - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/topic/MessageFactory.java b/java/perftests/src/main/java/org/apache/qpid/topic/MessageFactory.java deleted file mode 100644 index 4efdc1cb56..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/topic/MessageFactory.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.topic; - -import javax.jms.*; - -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.client.AMQTopic; -import org.apache.qpid.exchange.ExchangeDefaults; - -/** - */ -class MessageFactory -{ - private static final char[] DATA = "abcdefghijklmnopqrstuvwxyz".toCharArray(); - - private final Session _session; - private final Topic _topic; - private final Topic _control; - private final byte[] _payload; - - MessageFactory(Session session) throws JMSException - { - this(session, 256); - } - - MessageFactory(Session session, int size) throws JMSException - { - _session = session; - if (session instanceof AMQSession) - { - _topic = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, "topic_control"); - _control = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, "topictest.control"); - } - else - { - _topic = session.createTopic("topic_control"); - _control = session.createTopic("topictest.control"); - } - - _payload = new byte[size]; - - for (int i = 0; i < size; i++) - { - _payload[i] = (byte) DATA[i % DATA.length]; - } - } - - private static boolean checkText(Message m, String s) - { - try - { - return (m instanceof TextMessage) && ((TextMessage) m).getText().equals(s); - } - catch (JMSException e) - { - e.printStackTrace(System.out); - - return false; - } - } - - Topic getTopic() - { - return _topic; - } - - Message createEventMessage() throws JMSException - { - BytesMessage msg = _session.createBytesMessage(); - msg.writeBytes(_payload); - - return msg; - } - - Message createShutdownMessage() throws JMSException - { - return _session.createTextMessage("SHUTDOWN"); - } - - Message createReportRequestMessage() throws JMSException - { - return _session.createTextMessage("REPORT"); - } - - Message createReportResponseMessage(String msg) throws JMSException - { - return _session.createTextMessage(msg); - } - - boolean isShutdown(Message m) - { - return checkText(m, "SHUTDOWN"); - } - - boolean isReport(Message m) - { - return checkText(m, "REPORT"); - } - - Object getReport(Message m) - { - try - { - return ((TextMessage) m).getText(); - } - catch (JMSException e) - { - e.printStackTrace(System.out); - - return e.toString(); - } - } - - MessageConsumer createTopicConsumer() throws Exception - { - return _session.createConsumer(_topic); - } - - MessageConsumer createDurableTopicConsumer(String name) throws Exception - { - return _session.createDurableSubscriber(_topic, name); - } - - MessageConsumer createControlConsumer() throws Exception - { - return _session.createConsumer(_control); - } - - MessageProducer createTopicPublisher() throws Exception - { - return _session.createProducer(_topic); - } - - MessageProducer createControlPublisher() throws Exception - { - return _session.createProducer(_control); - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/topic/Publisher.java b/java/perftests/src/main/java/org/apache/qpid/topic/Publisher.java deleted file mode 100644 index c3b19b558a..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/topic/Publisher.java +++ /dev/null @@ -1,186 +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.topic; - -import javax.jms.*; - -public class Publisher implements MessageListener -{ - private final Object _lock = new Object(); - private final Connection _connection; - private final Session _session; - private final MessageFactory _factory; - private final MessageProducer _publisher; - private int _count; - - Publisher(Connection connection, int size, int ackMode, boolean persistent) throws Exception - { - _connection = connection; - _session = _connection.createSession(false, ackMode); - _factory = new MessageFactory(_session, size); - _publisher = _factory.createTopicPublisher(); - _publisher.setDeliveryMode(persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT); - System.out.println("Publishing " + (persistent ? "persistent" : "non-persistent") + " messages of " + size + " bytes, " + Config.getAckModeDescription(ackMode) + "."); - } - - private void test(Config config) throws Exception - { - test(config.getBatch(), config.getDelay(), config.getMessages(), config.getClients(), config.getWarmup()); - } - - private void test(int batches, long delay, int msgCount, int consumerCount, int warmup) throws Exception - { - _factory.createControlConsumer().setMessageListener(this); - _connection.start(); - - if (warmup > 0) - { - System.out.println("Runing warmup (" + warmup + " msgs)"); - long time = batch(warmup, consumerCount); - System.out.println("Warmup completed in " + time + "ms"); - } - - long[] times = new long[batches]; - for (int i = 0; i < batches; i++) - { - if (i > 0) - { - Thread.sleep(delay * 1000); - } - times[i] = batch(msgCount, consumerCount); - System.out.println("Batch " + (i + 1) + " of " + batches + " completed in " + times[i] + " ms."); - } - - long min = min(times); - long max = max(times); - System.out.println("min: " + min + ", max: " + max + " avg: " + avg(times, min, max)); - - //request shutdown - _publisher.send(_factory.createShutdownMessage()); - - _connection.stop(); - _connection.close(); - } - - private long batch(int msgCount, int consumerCount) throws Exception - { - _count = consumerCount; - long start = System.currentTimeMillis(); - publish(msgCount); - waitForCompletion(consumerCount); - return System.currentTimeMillis() - start; - } - - private void publish(int count) throws Exception - { - - //send events - for (int i = 0; i < count; i++) - { - _publisher.send(_factory.createEventMessage()); - if ((i + 1) % 100 == 0) - { - System.out.println("Sent " + (i + 1) + " messages"); - } - } - - //request report - _publisher.send(_factory.createReportRequestMessage()); - } - - private void waitForCompletion(int consumers) throws Exception - { - System.out.println("Waiting for completion..."); - synchronized (_lock) - { - while (_count > 0) - { - _lock.wait(); - } - } - } - - - public void onMessage(Message message) - { - System.out.println("Received report " + _factory.getReport(message) + " " + --_count + " remaining"); - if (_count == 0) - { - synchronized (_lock) - { - _lock.notify(); - } - } - } - - static long min(long[] times) - { - long min = times.length > 0 ? times[0] : 0; - for (int i = 0; i < times.length; i++) - { - min = Math.min(min, times[i]); - } - return min; - } - - static long max(long[] times) - { - long max = times.length > 0 ? times[0] : 0; - for (int i = 0; i < times.length; i++) - { - max = Math.max(max, times[i]); - } - return max; - } - - static long avg(long[] times, long min, long max) - { - long sum = 0; - for (int i = 0; i < times.length; i++) - { - sum += times[i]; - } - - int adjustment = 0; - - // Remove min and max if we have run enough batches. - if (times.length > 2) - { - sum -= min; - sum -= max; - adjustment = 2; - } - - return (sum / (times.length - adjustment)); - } - - public static void main(String[] argv) throws Exception - { - Config config = new Config(); - config.setOptions(argv); - - Connection con = config.createConnection(); - int size = config.getPayload(); - int ackMode = config.getAckMode(); - boolean persistent = config.usePersistentMessages(); - new Publisher(con, size, ackMode, persistent).test(config); - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/topic/TopicWithSelectorsTransientVolumeTest.java b/java/perftests/src/main/java/org/apache/qpid/topic/TopicWithSelectorsTransientVolumeTest.java deleted file mode 100644 index 1d6ec07bb0..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/topic/TopicWithSelectorsTransientVolumeTest.java +++ /dev/null @@ -1,344 +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.topic; - -import java.util.ArrayList; -import java.util.HashMap; - -import javax.jms.BytesMessage; -import javax.jms.DeliveryMode; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.Session; -import javax.jms.Topic; -import javax.jms.TopicConnection; -import javax.jms.TopicPublisher; -import javax.jms.TopicSession; -import javax.jms.TopicSubscriber; -import javax.naming.NamingException; - -import org.apache.qpid.AMQException; -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.test.utils.QpidBrokerTestCase; - -public class TopicWithSelectorsTransientVolumeTest extends QpidBrokerTestCase -{ - private static final int NUM_MSG_PER_ITERATION = 50;//must be a multiple of 10 - private static final int NUM_ITERATIONS = 1000; - - private static final int NUM_CONSUMERS = 50; - private static final int MSG_SIZE = 1024; - private static final byte[] BYTE_ARRAY = new byte[MSG_SIZE]; - - private ArrayList<MyMessageSubscriber> _subscribers = new ArrayList<MyMessageSubscriber>(); - private HashMap<String,Long> _queueMsgCounts = new HashMap<String,Long>(); - - private final static Object _lock=new Object(); - private boolean _producerFailed; - private static int _finishedCount; - private static int _failedCount; - - protected void setUp() throws Exception - { - super.setUp(); - init(); - } - - protected void tearDown() throws Exception - { - super.tearDown(); - } - - private void init() - { - _finishedCount = 0; - _failedCount = 0; - _producerFailed = false; - _subscribers.clear(); - _queueMsgCounts.clear(); - } - - - private Message createMessage(Session session) throws JMSException - { - BytesMessage message = session.createBytesMessage(); - message.writeBytes(BYTE_ARRAY); - - return message; - } - - /** - * 1 Topic with 50 subscribers using a selector, and 1 producer sending 50,000 1K messages with 90% selector success ratio. - */ - public void test50SubscribersWith90PercentMatched() throws Exception - { - Topic topic = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, "test50ConsumersWith10PercentUnmatched"); - - System.out.println("Creating consumers"); - - MyMessageSubscriber sub; - - for(int i=1; i <= NUM_CONSUMERS; i++) - { - sub = new MyMessageSubscriber(topic, "consumer" + i, ((9 * NUM_MSG_PER_ITERATION * NUM_ITERATIONS) / 10)); - _subscribers.add(sub); - } - - System.out.println("Starting consumers"); - for(MyMessageSubscriber s: _subscribers) - { - Thread consumer = new Thread(s); - consumer.start(); - } - - System.out.println("Creating producer"); - MyMessageProducer prod = new MyMessageProducer(topic); - - long startTime = System.currentTimeMillis(); - - System.out.println("Starting producer"); - Thread producer = new Thread(prod); - producer.start(); - - - // Wait for all the messageConsumers to have finished or failed - synchronized (_lock) - { - while (_finishedCount + _failedCount < NUM_CONSUMERS) - { - try - { - _lock.wait(); - } - catch (InterruptedException e) - { - //ignore - } - } - } - - long endTime = System.currentTimeMillis(); - System.out.println("Elapsed time for messaging: " + (endTime-startTime) + "ms"); - - assertFalse("Producer failed to send all messages", _producerFailed); - - //check if all messages received by consumers, or if there were failures - if (_finishedCount != NUM_CONSUMERS) - { - fail(_failedCount + " consumers did not recieve all their expected messages"); - } - - //check if all queue depths were 0 - for(String consumer: _queueMsgCounts.keySet()) - { - long depth = _queueMsgCounts.get(consumer); - assertEquals(consumer + " subscription queue msg count was not 0", 0, depth); - } - - } - - private class MyMessageProducer implements Runnable - { - private TopicConnection _connection; - private TopicSession _session; - private TopicPublisher _messagePublisher; - - public MyMessageProducer(Topic topic) throws JMSException, NamingException - { - _connection = (TopicConnection) getConnection(); - _session = (TopicSession) _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - _messagePublisher = _session.createPublisher(topic); - } - - public void run() - { - try - { - for(int iter = 0; iter < NUM_ITERATIONS; iter++) - { - int i = 0; - - //send 90% matching messages - for (; i < (9 * NUM_MSG_PER_ITERATION)/10; i++) - { - Message message = createMessage(_session); - message.setStringProperty("testprop", "true"); - - _messagePublisher.publish(message, DeliveryMode.NON_PERSISTENT, - Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE); - - Thread.yield(); - } - - //send remaining 10% non-matching messages - for (; i < NUM_MSG_PER_ITERATION; i++) - { - Message message = _session.createMessage(); - message.setStringProperty("testprop", "false"); - - _messagePublisher.publish(message, DeliveryMode.NON_PERSISTENT, - Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE); - - Thread.yield(); - } - } - - } - catch (Exception exp) - { - System.out.println("producer: caught an exception, probably exiting before all messages sent"); - exp.printStackTrace(); - synchronized (_lock) - { - _producerFailed=true; - _lock.notifyAll(); - } - } - } - } - - - private class MyMessageSubscriber implements Runnable - { - /* The topic this subscriber is subscribing to */ - private Topic _topic; - private String _consumerName; - private int _outstandingMsgCount; - private TopicConnection _connection; - private TopicSession _session; - private TopicSubscriber _durSub; - - public MyMessageSubscriber(Topic topic, String consumerName, int messageCount) throws JMSException, NamingException - { - _outstandingMsgCount = messageCount; - _topic=topic; - _consumerName = consumerName; - _connection = (TopicConnection) getConnection(); - _session = (TopicSession) _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - _durSub = _session.createDurableSubscriber(_topic, _consumerName,"testprop='true'", false); - _connection.start(); - } - - public void run() - { - - boolean failed = false; - do - { - Message m = null; - try - { - m = _durSub.receive(10000); - } - catch (JMSException exp) - { - System.out.println(_consumerName + ": caught an exception handling a received message"); - exp.printStackTrace(); - - failed = true; - break; - } - - Thread.yield(); - - _outstandingMsgCount--; - - if(_outstandingMsgCount % 500 == 0) - { - System.out.println(_consumerName + ": outstanding message count: " + _outstandingMsgCount); - } - - if(m == null) - { - if(_outstandingMsgCount != 0) - { - failed = true; - } - break; - } - } - while(_outstandingMsgCount > 0); - - System.out.println(_consumerName + ": outstanding message count: " + _outstandingMsgCount); - - try - { - AMQQueue subcriptionQueue = new AMQQueue(ExchangeDefaults.TOPIC_EXCHANGE_NAME,"clientid" + ":" + _consumerName); - - ((AMQSession)_session).sync(); - Long depth = ((AMQSession)_session).getQueueDepth(subcriptionQueue); - _queueMsgCounts.put(_consumerName, depth); - - System.out.println(_consumerName + ": completion queue msg count: " + depth); - } - catch (AMQException exp) - { - System.out.println(_consumerName + ": caught an exception determining completion queue depth"); - exp.printStackTrace(); - } - finally - { - try - { - _session.unsubscribe(_consumerName); - } - catch (JMSException e) - { - System.out.println(_consumerName + ": caught an exception whilst unsubscribing"); - e.printStackTrace(); - } - } - - synchronized (_lock) - { - if (_outstandingMsgCount == 0 && !failed) - { - _finishedCount++; - System.out.println(_consumerName + ": finished"); - } - else - { - _failedCount++; - System.out.println(_consumerName + ": failed"); - } - _lock.notifyAll(); - } - - } - } - - //helper method to allow easily running against an external standalone broker -// public static void main(String[] args) throws Exception -// { -// System.setProperty("broker.config", "/dev/null"); -// System.setProperty("broker", "external"); -// System.setProperty("java.naming.factory.initial", "org.apache.qpid.jndi.PropertiesFileInitialContextFactory"); -// System.setProperty("java.naming.provider.url", "test-profiles/test-provider.properties"); -// -// TopicWithSelectorsTransientVolumeTest test = new TopicWithSelectorsTransientVolumeTest(); -// test.init(); -// test.test50SubscribersWith90PercentMatched(); -// test.tearDown(); -// } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/topic/topicselectors.properties b/java/perftests/src/main/resources/perftests.properties index 1f572af58a..d8823f9dc5 100644 --- a/java/perftests/src/main/java/org/apache/qpid/topic/topicselectors.properties +++ b/java/perftests/src/main/resources/perftests.properties @@ -1,4 +1,3 @@ -# # 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 @@ -15,10 +14,7 @@ # 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 +java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory -# register some connection factories -# connectionfactory.[jndiname] = [ConnectionURL] -connectionfactory.default = amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672'
\ No newline at end of file +connectionfactory.connectionfactory = amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672' diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/ArgumentParserTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/ArgumentParserTest.java new file mode 100644 index 0000000000..3be82627fe --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/ArgumentParserTest.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.disttest; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.qpid.test.utils.QpidTestCase; + +public class ArgumentParserTest extends QpidTestCase +{ + private static final String TEST_CONFIG_FILENAME = "ControllerRunnerTest-test-config-filename.json"; + private static final String JNDI_CONFIG_FILENAME = "ControllerRunnerTest-jndi-config-filename.properties"; + private static final String DISTRIBUTED_MODE = "true"; + + public static final String TEST_CONFIG_PROP = "test-config"; + public static final String JNDI_CONFIG_PROP = "jndi-config"; + public static final String DISTRIBUTED_PROP = "distributed"; + + public static final String TEST_CONFIG_DEFAULT = "perftests-config.json"; + public static final String JNDI_CONFIG_DEFAULT = "perftests-jndi.properties"; + public static final String DISTRIBUTED_DEFAULT = "false"; + + private Map<String,String> _options = new HashMap<String, String>(); + + private ArgumentParser _parser; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + _parser = new ArgumentParser(); + + _options.clear(); + _options.put(TEST_CONFIG_PROP, TEST_CONFIG_DEFAULT); + _options.put(JNDI_CONFIG_PROP, JNDI_CONFIG_DEFAULT); + _options.put(DISTRIBUTED_PROP, DISTRIBUTED_DEFAULT); + } + + public void testInvalidArguments() + { + String[] args = new String[]{"nonExistentConfigProperty" + "=" + TEST_CONFIG_FILENAME}; + + try + { + _parser.parseArgumentsIntoConfig(_options, args); + fail("expected exception to be thrown due to provision of a non existent config property"); + } + catch(IllegalArgumentException e) + { + //expected + } + } + + public void testDefaultConfigValues() + { + String[] args = new String[0]; + + _parser.parseArgumentsIntoConfig(_options, args); + + assertEquals("unexpected config value", TEST_CONFIG_DEFAULT, _options.get(TEST_CONFIG_PROP)); + assertEquals("unexpected config value", JNDI_CONFIG_DEFAULT, _options.get(JNDI_CONFIG_PROP)); + assertEquals("unexpected config value", DISTRIBUTED_DEFAULT, _options.get(DISTRIBUTED_PROP)); + } + + public void testConfigurationParsingOverridesDefault() throws Exception + { + String[] args = new String[]{TEST_CONFIG_PROP + "=" + TEST_CONFIG_FILENAME, + JNDI_CONFIG_PROP + "=" + JNDI_CONFIG_FILENAME, + DISTRIBUTED_PROP + "=" + DISTRIBUTED_MODE}; + + _parser.parseArgumentsIntoConfig(_options, args); + + assertEquals("unexpected config value", TEST_CONFIG_FILENAME, _options.get(TEST_CONFIG_PROP)); + assertEquals("unexpected config value", JNDI_CONFIG_FILENAME, _options.get(JNDI_CONFIG_PROP)); + assertEquals("unexpected config value", DISTRIBUTED_MODE, _options.get(DISTRIBUTED_PROP)); + assertFalse("override value was the same as the default", DISTRIBUTED_MODE.equalsIgnoreCase(_options.get(DISTRIBUTED_DEFAULT))); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/ConfigFileHelper.java b/java/perftests/src/test/java/org/apache/qpid/disttest/ConfigFileHelper.java new file mode 100644 index 0000000000..12ba3b56ad --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/ConfigFileHelper.java @@ -0,0 +1,48 @@ +/* + * 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.disttest; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; + +import org.apache.qpid.disttest.controller.config.Config; +import org.apache.qpid.disttest.controller.config.ConfigReader; + +public class ConfigFileHelper +{ + public static Reader getConfigFileReader(Class<?> testClass, String resourceName) + { + InputStream inputStream = testClass.getResourceAsStream(resourceName); + if(inputStream == null) + { + throw new RuntimeException("Can't find resource " + resourceName + " using classloader of class " + testClass); + } + Reader reader = new InputStreamReader(inputStream); + return reader; + } + + public static Config getConfigFromResource(Class<?> testClass, String resourceName) + { + ConfigReader configReader = new ConfigReader(); + Config config = configReader.readConfig(getConfigFileReader(testClass, resourceName)); + return config; + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/VisitorTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/VisitorTest.java new file mode 100644 index 0000000000..320e7d8c9d --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/VisitorTest.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.disttest; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.message.Command; + + +public class VisitorTest extends TestCase +{ + + public void testStringVisited() throws Exception + { + Object argument = new String(); + + TestVisitor visitor = new TestVisitor(); + visitor.visit(argument); + + assertSame(argument, visitor._string); + } + + public void testCommandVisited() throws Exception + { + Object argument = new TestCommand(); + + TestVisitor visitor = new TestVisitor(); + visitor.visit(argument); + + assertSame(argument, visitor._testCommand); + } + + public void testNoVisitIntegerImplementatiom() throws Exception + { + Integer argument = Integer.valueOf(1); + + TestVisitor visitor = new TestVisitor(); + + try + { + visitor.visit(argument); + fail("Exception not thrown"); + } + catch (DistributedTestException e) + { + assertNotNull(e.getCause()); + assertEquals(NoSuchMethodException.class, e.getCause().getClass()); + } + } + + class TestVisitor extends Visitor + { + String _string = null; + TestCommand _testCommand = null; + + public void visit(String string) + { + _string = string; + } + + public void visit(TestCommand command) + { + _testCommand = command; + } + } + + class TestCommand extends Command + { + + public TestCommand() + { + super(null); + } + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/client/ClientCommandVisitorTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/client/ClientCommandVisitorTest.java new file mode 100644 index 0000000000..4a82f6719f --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/client/ClientCommandVisitorTest.java @@ -0,0 +1,108 @@ +/* + * 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.disttest.client; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import junit.framework.TestCase; + +import org.apache.qpid.disttest.jms.ClientJmsDelegate; +import org.apache.qpid.disttest.message.CreateConnectionCommand; +import org.apache.qpid.disttest.message.CreateConsumerCommand; +import org.apache.qpid.disttest.message.CreateMessageProviderCommand; +import org.apache.qpid.disttest.message.CreateProducerCommand; +import org.apache.qpid.disttest.message.CreateSessionCommand; +import org.apache.qpid.disttest.message.StartTestCommand; +import org.apache.qpid.disttest.message.StopClientCommand; +import org.apache.qpid.disttest.message.TearDownTestCommand; + +public class ClientCommandVisitorTest extends TestCase +{ + private Client _client; + private ClientCommandVisitor _visitor; + private ClientJmsDelegate _delegate; + + + @Override + protected void setUp() throws Exception + { + super.setUp(); + _client = mock(Client.class); + _delegate = mock(ClientJmsDelegate.class); + _visitor = new ClientCommandVisitor(_client, _delegate); + } + + public void testStopClient() + { + StopClientCommand command = new StopClientCommand(); + _visitor.visit(command); + verify(_client).stop(); + } + + public void testCreateConnection() throws Exception + { + final CreateConnectionCommand command = new CreateConnectionCommand(); + _visitor.visit(command); + verify(_delegate).createConnection(command); + } + + public void testCreateSession() throws Exception + { + final CreateSessionCommand command = new CreateSessionCommand(); + _visitor.visit(command); + verify(_delegate).createSession(command); + } + + public void testCreateProducer() throws Exception + { + final CreateProducerCommand command = new CreateProducerCommand(); + _visitor.visit(command); + verify(_delegate).createProducer(command); + } + + public void testCreateConsumer() throws Exception + { + final CreateConsumerCommand command = new CreateConsumerCommand(); + _visitor.visit(command); + verify(_delegate).createConsumer(command); + } + + public void testStartTest() throws Exception + { + final StartTestCommand command = new StartTestCommand(); + _visitor.visit(command); + verify(_client).startTest(); + } + + public void testStopTest() throws Exception + { + final TearDownTestCommand stopCommand = new TearDownTestCommand(); + _visitor.visit(stopCommand); + verify(_client).tearDownTest(); + } + + public void testCreateMessageProvider() throws Exception + { + final CreateMessageProviderCommand command = new CreateMessageProviderCommand(); + command.setProviderName("test"); + _visitor.visit(command); + verify(_delegate).createMessageProvider(command); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/client/ClientTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/client/ClientTest.java new file mode 100644 index 0000000000..198baa6ef4 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/client/ClientTest.java @@ -0,0 +1,159 @@ +/* + * 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.disttest.client; + +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.Timer; +import java.util.TimerTask; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.jms.ClientJmsDelegate; +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.disttest.message.Response; +import org.apache.qpid.disttest.message.StopClientCommand; +import org.mockito.InOrder; +import org.mockito.Mockito; + +public class ClientTest extends TestCase +{ + private Client _client; + private ClientJmsDelegate _delegate; + private ClientCommandVisitor _visitor; + private ParticipantExecutor _participant; + private ParticipantExecutorRegistry _participantRegistry; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + _delegate = mock(ClientJmsDelegate.class); + _visitor = mock(ClientCommandVisitor.class); + _client = new Client(_delegate); + _client.setClientCommandVisitor(_visitor); + _participant = mock(ParticipantExecutor.class); + when(_participant.getParticipantName()).thenReturn("testParticipantMock"); + + _participantRegistry = mock(ParticipantExecutorRegistry.class); + when(_participantRegistry.executors()).thenReturn(Collections.singletonList(_participant)); + _client.setParticipantRegistry(_participantRegistry); + } + + public void testInitialState() throws Exception + { + assertEquals("Expected client to be in CREATED state", ClientState.CREATED, _client.getState()); + } + + public void testStart() throws Exception + { + _client.start(); + final InOrder inOrder = inOrder(_delegate); + inOrder.verify(_delegate).setInstructionListener(_client); + inOrder.verify(_delegate).sendRegistrationMessage(); + assertEquals("Expected client to be in STARTED state", ClientState.READY, _client.getState()); + } + + public void testStopClient() throws Exception + { + _client.stop(); + + assertEquals("Expected client to be in STOPPED state", ClientState.STOPPED, _client.getState()); + } + + public void testProcessInstructionVisitsCommandAndResponds() throws Exception + { + // has to be declared to be of supertype Command otherwise Mockito verify() + // refers to wrong method + final Command command = new StopClientCommand(); + _client.processInstruction(command); + + verify(_visitor).visit(command); + verify(_delegate).sendResponseMessage(isA(Response.class)); + } + + public void testWaitUntilStopped() throws Exception + { + stopClientLater(500); + _client.waitUntilStopped(1000); + verify(_delegate).destroy(); + } + + public void testStartTest() throws Exception + { + _client.start(); + _client.addParticipantExecutor(_participant); + + verify(_participantRegistry).add(_participant); + + _client.startTest(); + + InOrder inOrder = Mockito.inOrder(_delegate, _participant); + inOrder.verify(_delegate).startConnections(); + inOrder.verify(_participant).start(_client); + } + + public void testTearDownTest() throws Exception + { + // before we can tear down the test the client needs to be in the "running test" state, which requires a participant + _client.start(); + _client.addParticipantExecutor(_participant); + _client.startTest(); + + _client.tearDownTest(); + + verify(_delegate).closeTestConnections(); + + verify(_participantRegistry).clear(); + } + + public void testResults() throws Exception + { + ParticipantResult testResult = mock(ParticipantResult.class); + _client.sendResults(testResult); + verify(_delegate).sendResponseMessage(testResult); + } + + private void stopClientLater(long delay) + { + doLater(new TimerTask() + { + @Override + public void run() + { + _client.stop(); + } + + }, delay); + } + + private void doLater(TimerTask task, long delayInMillis) + { + Timer timer = new Timer(); + timer.schedule(task, delayInMillis); + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/client/ConsumerParticipantTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/client/ConsumerParticipantTest.java new file mode 100644 index 0000000000..ff7cfd2b41 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/client/ConsumerParticipantTest.java @@ -0,0 +1,180 @@ +/* + * 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.disttest.client; + +import static org.apache.qpid.disttest.client.ParticipantTestHelper.assertExpectedConsumerResults; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import javax.jms.Message; +import javax.jms.Session; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.DistributedTestException; +import org.apache.qpid.disttest.jms.ClientJmsDelegate; +import org.apache.qpid.disttest.message.CreateConsumerCommand; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.mockito.InOrder; + +public class ConsumerParticipantTest extends TestCase +{ + private static final String SESSION_NAME1 = "SESSION1"; + private static final String PARTICIPANT_NAME1 = "PARTICIPANT_NAME1"; + private static final long RECEIVE_TIMEOUT = 100; + private static final String CLIENT_NAME = "CLIENT_NAME"; + private static final int PAYLOAD_SIZE_PER_MESSAGE = 1024; + + private final Message _mockMessage = mock(Message.class); + private final CreateConsumerCommand _command = new CreateConsumerCommand(); + private ClientJmsDelegate _delegate; + private ConsumerParticipant _consumerParticipant; + private InOrder _inOrder; + + /** used to check start/end time of results */ + private long _testStartTime; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + _delegate = mock(ClientJmsDelegate.class); + _inOrder = inOrder(_delegate); + + _command.setSessionName(SESSION_NAME1); + _command.setParticipantName(PARTICIPANT_NAME1); + _command.setSynchronous(true); + _command.setReceiveTimeout(RECEIVE_TIMEOUT); + + _consumerParticipant = new ConsumerParticipant(_delegate, _command); + + when(_delegate.consumeMessage(PARTICIPANT_NAME1, RECEIVE_TIMEOUT)).thenReturn(_mockMessage); + when(_delegate.calculatePayloadSizeFrom(_mockMessage)).thenReturn(PAYLOAD_SIZE_PER_MESSAGE); + when(_delegate.getAcknowledgeMode(SESSION_NAME1)).thenReturn(Session.CLIENT_ACKNOWLEDGE); + + _testStartTime = System.currentTimeMillis(); + } + + public void testNoMessagesToReceive() throws Exception + { + _command.setNumberOfMessages(0); + _command.setMaximumDuration(0); + + try + { + _consumerParticipant.doIt(CLIENT_NAME); + fail("Exception not thrown"); + } + catch(DistributedTestException e) + { + // PASS + assertEquals("number of messages and duration cannot both be zero", e.getMessage()); + + } + + verify(_delegate, never()).consumeMessage(anyString(), anyLong()); + } + + public void testReceiveOneMessageSynch() throws Exception + { + int numberOfMessages = 1; + long totalPayloadSize = PAYLOAD_SIZE_PER_MESSAGE * numberOfMessages; + _command.setNumberOfMessages(numberOfMessages); + + ParticipantResult result = _consumerParticipant.doIt(CLIENT_NAME); + + assertExpectedConsumerResults(result, PARTICIPANT_NAME1, CLIENT_NAME, _testStartTime, + Session.CLIENT_ACKNOWLEDGE, null, numberOfMessages, PAYLOAD_SIZE_PER_MESSAGE, totalPayloadSize, null); + + _inOrder.verify(_delegate).consumeMessage(PARTICIPANT_NAME1, RECEIVE_TIMEOUT); + _inOrder.verify(_delegate).calculatePayloadSizeFrom(_mockMessage); + _inOrder.verify(_delegate).commitOrAcknowledgeMessage(_mockMessage, SESSION_NAME1); + } + + public void testReceiveMessagesForDurationSynch() throws Exception + { + long duration = 100; + _command.setMaximumDuration(duration); + + ParticipantResult result = _consumerParticipant.doIt(CLIENT_NAME); + + assertExpectedConsumerResults(result, PARTICIPANT_NAME1, CLIENT_NAME, _testStartTime, + Session.CLIENT_ACKNOWLEDGE, null, null, PAYLOAD_SIZE_PER_MESSAGE, null, duration); + + verify(_delegate, atLeastOnce()).consumeMessage(PARTICIPANT_NAME1, RECEIVE_TIMEOUT); + verify(_delegate, atLeastOnce()).calculatePayloadSizeFrom(_mockMessage); + verify(_delegate, atLeastOnce()).commitOrAcknowledgeMessage(_mockMessage, SESSION_NAME1); + } + + public void testReceiveMessagesBatchedSynch() throws Exception + { + int numberOfMessages = 10; + final int batchSize = 3; + long totalPayloadSize = PAYLOAD_SIZE_PER_MESSAGE * numberOfMessages; + _command.setNumberOfMessages(numberOfMessages); + _command.setBatchSize(batchSize); + + ParticipantResult result = _consumerParticipant.doIt(CLIENT_NAME); + + assertExpectedConsumerResults(result, PARTICIPANT_NAME1, CLIENT_NAME, _testStartTime, + Session.CLIENT_ACKNOWLEDGE, batchSize, numberOfMessages, PAYLOAD_SIZE_PER_MESSAGE, totalPayloadSize, null); + + verify(_delegate, times(numberOfMessages)).consumeMessage(PARTICIPANT_NAME1, RECEIVE_TIMEOUT); + verify(_delegate, times(numberOfMessages)).calculatePayloadSizeFrom(_mockMessage); + verify(_delegate, times(4)).commitOrAcknowledgeMessage(_mockMessage, SESSION_NAME1); + } + + public void testReceiveMessagesWithVaryingPayloadSize() throws Exception + { + int numberOfMessages = 3; + + int firstPayloadSize = PAYLOAD_SIZE_PER_MESSAGE; + int secondPayloadSize = PAYLOAD_SIZE_PER_MESSAGE * 2; + int thirdPayloadSize = PAYLOAD_SIZE_PER_MESSAGE * 4; + + _command.setNumberOfMessages(numberOfMessages); + + when(_delegate.calculatePayloadSizeFrom(_mockMessage)).thenReturn(firstPayloadSize, secondPayloadSize, thirdPayloadSize); + + ParticipantResult result = _consumerParticipant.doIt(CLIENT_NAME); + + final int expectedPayloadResultPayloadSize = 0; + final long totalPayloadSize = firstPayloadSize + secondPayloadSize + thirdPayloadSize; + assertExpectedConsumerResults(result, PARTICIPANT_NAME1, CLIENT_NAME, _testStartTime, + Session.CLIENT_ACKNOWLEDGE, null, numberOfMessages, expectedPayloadResultPayloadSize, totalPayloadSize, null); + + verify(_delegate, times(numberOfMessages)).consumeMessage(PARTICIPANT_NAME1, RECEIVE_TIMEOUT); + verify(_delegate, times(numberOfMessages)).calculatePayloadSizeFrom(_mockMessage); + verify(_delegate, times(numberOfMessages)).commitOrAcknowledgeMessage(_mockMessage, SESSION_NAME1); + } + + public void testReleaseResources() + { + _consumerParticipant.releaseResources(); + verify(_delegate).closeTestConsumer(PARTICIPANT_NAME1); + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/client/MessageProviderTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/client/MessageProviderTest.java new file mode 100644 index 0000000000..ffc3733eb7 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/client/MessageProviderTest.java @@ -0,0 +1,107 @@ +/* + * 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.disttest.client; + +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.jms.Message; +import javax.jms.Session; +import javax.jms.TextMessage; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.client.property.ListPropertyValue; +import org.apache.qpid.disttest.client.property.PropertyValue; +import org.apache.qpid.disttest.client.property.SimplePropertyValue; +import org.apache.qpid.disttest.message.CreateProducerCommand; + +public class MessageProviderTest extends TestCase +{ + private Session _session; + private TextMessage _message; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + _session = mock(Session.class); + _message = mock(TextMessage.class); + when(_session.createTextMessage(isA(String.class))).thenReturn(_message); + when(_session.createTextMessage()).thenReturn(_message); + } + + public void testGetMessagePayload() throws Exception + { + MessageProvider messageProvider = new MessageProvider(null) + { + public String getMessagePayload(CreateProducerCommand command) + { + return super.getMessagePayload(command); + } + }; + CreateProducerCommand command = new CreateProducerCommand(); + command.setMessageSize(100); + String payloadValue = messageProvider.getMessagePayload(command); + assertNotNull("Mesage payload should not be null", payloadValue); + assertEquals("Unexpected payload size", 100, payloadValue.length()); + } + + public void testNextMessage() throws Exception + { + MessageProvider messageProvider = new MessageProvider(null); + CreateProducerCommand command = new CreateProducerCommand(); + command.setMessageSize(100); + Message message = messageProvider.nextMessage(_session, command); + assertNotNull("Mesage should be returned", message); + verify(_message, atLeastOnce()).setText(isA(String.class)); + } + + public void testNextMessageWithProperties() throws Exception + { + Map<String, PropertyValue> properties = new HashMap<String, PropertyValue>(); + properties.put("test1", new SimplePropertyValue("testValue1")); + properties.put("test2", new SimplePropertyValue(new Integer(1))); + properties.put("priority", new SimplePropertyValue(new Integer(2))); + List<PropertyValue> listItems = new ArrayList<PropertyValue>(); + listItems.add(new SimplePropertyValue(new Double(2.0))); + ListPropertyValue list = new ListPropertyValue(); + list.setItems(listItems); + properties.put("test3", list); + + MessageProvider messageProvider = new MessageProvider(properties); + CreateProducerCommand command = new CreateProducerCommand(); + command.setMessageSize(100); + Message message = messageProvider.nextMessage(_session, command); + assertNotNull("Mesage should be returned", message); + verify(_message, atLeastOnce()).setText(isA(String.class)); + verify(_message, atLeastOnce()).setJMSPriority(2); + verify(_message, atLeastOnce()).setStringProperty("test1", "testValue1"); + verify(_message, atLeastOnce()).setIntProperty("test2", 1); + verify(_message, atLeastOnce()).setDoubleProperty("test3", 2.0); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/client/ParticipantExecutorTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/client/ParticipantExecutorTest.java new file mode 100644 index 0000000000..f30e4664ff --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/client/ParticipantExecutorTest.java @@ -0,0 +1,183 @@ +/* + * 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.disttest.client; + +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.DistributedTestException; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.mockito.ArgumentMatcher; +import org.mockito.InOrder; + +public class ParticipantExecutorTest extends TestCase +{ + private static final ResultHasError HAS_ERROR = new ResultHasError(); + private static final String CLIENT_NAME = "CLIENT_NAME"; + private static final String PARTICIPANT_NAME = "PARTICIPANT_NAME"; + private ParticipantExecutor _participantExecutor = null; + private Client _client = null; + private Participant _participant = null; + private ParticipantResult _mockResult; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + _client = mock(Client.class); + when(_client.getClientName()).thenReturn(CLIENT_NAME); + _participant = mock(Participant.class); + + _participantExecutor = new ParticipantExecutor(_participant); + _participantExecutor.setExecutor(new SynchronousExecutor()); + + _mockResult = mock(ParticipantResult.class); + } + + public void testStart() throws Exception + { + when(_participant.doIt(CLIENT_NAME)).thenReturn(_mockResult); + + _participantExecutor.start(_client); + + InOrder inOrder = inOrder(_participant, _client); + + inOrder.verify(_participant).doIt(CLIENT_NAME); + inOrder.verify(_client).sendResults(_mockResult); + inOrder.verify(_participant).releaseResources(); + } + + public void testParticipantThrowsException() throws Exception + { + when(_participant.doIt(CLIENT_NAME)).thenThrow(DistributedTestException.class); + + _participantExecutor.start(_client); + + InOrder inOrder = inOrder(_participant, _client); + + inOrder.verify(_participant).doIt(CLIENT_NAME); + inOrder.verify(_client).sendResults(argThat(HAS_ERROR)); + inOrder.verify(_participant).releaseResources(); + } + + public void testThreadNameAndDaemonness() throws Exception + { + + ThreadPropertyReportingParticipant participant = new ThreadPropertyReportingParticipant(PARTICIPANT_NAME); + _participantExecutor = new ParticipantExecutor(participant); + + _participantExecutor.start(_client); + participant.awaitExecution(); + + assertTrue("Participant should be run in a thread named after it", participant.threadWasCalled().endsWith(PARTICIPANT_NAME)); + assertTrue("Executor should use daemon threads to avoid them preventing JVM termination", participant.wasDaemon()); + } + + private static final class ThreadPropertyReportingParticipant implements Participant + { + private final String _participantName; + private final CountDownLatch _participantExecuted = new CountDownLatch(1); + private String _threadName; + private boolean _daemon; + + public ThreadPropertyReportingParticipant(String participantName) + { + _participantName = participantName; + } + + public String threadWasCalled() + { + return _threadName; + } + + public boolean wasDaemon() + { + return _daemon; + } + + @Override + public void releaseResources() + { + } + + @Override + public String getName() + { + return _participantName; + } + + @Override + public ParticipantResult doIt(String registeredClientName) throws Exception + { + Thread currentThread = Thread.currentThread(); + _threadName = currentThread.getName(); + _daemon = currentThread.isDaemon(); + + _participantExecuted.countDown(); + + return null; // unused + } + + public void awaitExecution() + { + boolean success = false; + try + { + success = _participantExecuted.await(5, TimeUnit.SECONDS); + } + catch (InterruptedException e) + { + Thread.currentThread().interrupt(); + } + + assertTrue("Participant not executed", success); + } + } + + /** avoids our unit test needing to use multiple threads */ + private final class SynchronousExecutor implements Executor + { + @Override + public void execute(Runnable command) + { + command.run(); + } + } + + private static class ResultHasError extends ArgumentMatcher<ParticipantResult> + { + @Override + public boolean matches(Object argument) + { + ParticipantResult result = (ParticipantResult) argument; + return result.hasError(); + } + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/client/ParticipantRegistryTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/client/ParticipantRegistryTest.java new file mode 100644 index 0000000000..bd0d5a39c8 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/client/ParticipantRegistryTest.java @@ -0,0 +1,55 @@ +/* + * 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.disttest.client; + +import static org.mockito.Mockito.mock; +import junit.framework.TestCase; + +public class ParticipantRegistryTest extends TestCase +{ + private ParticipantExecutorRegistry _participantRegistry = new ParticipantExecutorRegistry(); + + private ParticipantExecutor _testParticipant1 = mock(ParticipantExecutor.class); + private ParticipantExecutor _testParticipant2 = mock(ParticipantExecutor.class); + + public void testAdd() + { + assertTrue(_participantRegistry.executors().isEmpty()); + + _participantRegistry.add(_testParticipant1); + + assertTrue(_participantRegistry.executors().contains(_testParticipant1)); + + _participantRegistry.add(_testParticipant2); + + assertTrue(_participantRegistry.executors().contains(_testParticipant2)); + } + + public void testClear() + { + _participantRegistry.add(_testParticipant1); + + assertEquals(1, _participantRegistry.executors().size()); + + _participantRegistry.clear(); + + assertTrue(_participantRegistry.executors().isEmpty()); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/client/ParticipantResultFactoryTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/client/ParticipantResultFactoryTest.java new file mode 100644 index 0000000000..3b21834a5c --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/client/ParticipantResultFactoryTest.java @@ -0,0 +1,183 @@ +/* + * 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.disttest.client; + +import java.util.Date; + +import javax.jms.DeliveryMode; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.message.ConsumerParticipantResult; +import org.apache.qpid.disttest.message.CreateConsumerCommand; +import org.apache.qpid.disttest.message.CreateParticpantCommand; +import org.apache.qpid.disttest.message.CreateProducerCommand; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.disttest.message.ProducerParticipantResult; + +public class ParticipantResultFactoryTest extends TestCase +{ + private static final String PARTICIPANT_NAME = "participantName"; + private static final String REGISTERED_CLIENT_NAME = "registeredClientName"; + + private static final int BATCH_SIZE = 10; + private static final long MAXIMUM_DURATION = 500; + private static final int NUMBER_OF_MESSAGES_PROCESSED = 100; + private static final long TIME_TAKEN = 100; + private static final long TOTAL_PAYLOAD_PROCESSED = 200; + private static final int PAYLOAD_SIZE = 300; + + private static final Date START = new Date(0); + private static final Date END = new Date(START.getTime() + TIME_TAKEN); + + private ParticipantResultFactory _participantResultFactory; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + _participantResultFactory = new ParticipantResultFactory(); + } + + public void testCreateForProducer() + { + CreateProducerCommand command = new CreateProducerCommand(); + setCommonCommandFields(command); + + long producerStartDelay = 30; + command.setStartDelay(producerStartDelay); + + int deliveryMode = DeliveryMode.PERSISTENT; + command.setDeliveryMode(deliveryMode); + + int priority = 5; + command.setPriority(priority); + + long producerInterval = 50; + command.setInterval(producerInterval); + + long timeToLive = 60; + command.setTimeToLive(timeToLive); + + int totalNumberOfConsumers = 0; + int totalNumberOfProducers = 1; + + int acknowledgeMode = 1; + + ProducerParticipantResult result = _participantResultFactory.createForProducer(PARTICIPANT_NAME, + REGISTERED_CLIENT_NAME, + command, + acknowledgeMode, + NUMBER_OF_MESSAGES_PROCESSED, + PAYLOAD_SIZE, + TOTAL_PAYLOAD_PROCESSED, + START, END); + + assertCommonResultProperties(result); + + assertEquals(deliveryMode, result.getDeliveryMode()); + assertEquals(acknowledgeMode, result.getAcknowledgeMode()); + assertEquals(priority, result.getPriority()); + assertEquals(producerInterval, result.getInterval()); + assertEquals(producerStartDelay, result.getStartDelay()); + assertEquals(timeToLive, result.getTimeToLive()); + assertEquals(totalNumberOfConsumers, result.getTotalNumberOfConsumers()); + assertEquals(totalNumberOfProducers, result.getTotalNumberOfProducers()); + } + + public void testCreateForConsumer() + { + CreateConsumerCommand command = new CreateConsumerCommand(); + setCommonCommandFields(command); + + boolean topic = true; + command.setTopic(topic); + + boolean durable = true; + command.setDurableSubscription(durable); + + boolean browsingSubscription = false; + command.setBrowsingSubscription(browsingSubscription); + + String selector = "selector"; + boolean isSelector = true; + command.setSelector(selector); + + boolean noLocal = false; + command.setNoLocal(noLocal); + + boolean synchronousConsumer = true; + command.setSynchronous(synchronousConsumer); + + int totalNumberOfConsumers = 1; + int totalNumberOfProducers = 0; + + int acknowledgeMode = 2; + + ConsumerParticipantResult result = _participantResultFactory.createForConsumer(PARTICIPANT_NAME, + REGISTERED_CLIENT_NAME, + command, + acknowledgeMode, + NUMBER_OF_MESSAGES_PROCESSED, + PAYLOAD_SIZE, + TOTAL_PAYLOAD_PROCESSED, + START, END); + + assertCommonResultProperties(result); + + assertEquals(topic, result.isTopic()); + assertEquals(durable, result.isDurableSubscription()); + assertEquals(browsingSubscription, result.isBrowsingSubscription()); + assertEquals(isSelector, result.isSelector()); + assertEquals(noLocal, result.isNoLocal()); + assertEquals(synchronousConsumer, result.isSynchronousConsumer()); + assertEquals(totalNumberOfConsumers, result.getTotalNumberOfConsumers()); + assertEquals(totalNumberOfProducers, result.getTotalNumberOfProducers()); + } + + public void testCreateForError() + { + String errorMessage = "error"; + ParticipantResult result = _participantResultFactory.createForError(PARTICIPANT_NAME, REGISTERED_CLIENT_NAME, errorMessage); + assertEquals(PARTICIPANT_NAME, result.getParticipantName()); + assertEquals(REGISTERED_CLIENT_NAME, result.getRegisteredClientName()); + } + + + private void setCommonCommandFields(CreateParticpantCommand command) + { + command.setBatchSize(BATCH_SIZE); + command.setMaximumDuration(MAXIMUM_DURATION); + } + + + private void assertCommonResultProperties(ParticipantResult result) + { + assertEquals(PARTICIPANT_NAME, result.getParticipantName()); + assertEquals(REGISTERED_CLIENT_NAME, result.getRegisteredClientName()); + assertEquals(BATCH_SIZE, result.getBatchSize()); + assertEquals(MAXIMUM_DURATION, result.getMaximumDuration()); + assertEquals(TIME_TAKEN, result.getTimeTaken()); + assertEquals(NUMBER_OF_MESSAGES_PROCESSED, result.getNumberOfMessagesProcessed()); + assertEquals(TOTAL_PAYLOAD_PROCESSED, result.getTotalPayloadProcessed()); + assertEquals(PAYLOAD_SIZE, result.getPayloadSize()); + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/client/ParticipantTestHelper.java b/java/perftests/src/test/java/org/apache/qpid/disttest/client/ParticipantTestHelper.java new file mode 100644 index 0000000000..a013cb0a06 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/client/ParticipantTestHelper.java @@ -0,0 +1,83 @@ +/* + * 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.disttest.client; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import org.apache.qpid.disttest.message.ParticipantResult; + +public class ParticipantTestHelper +{ + + public static void assertAtLeast(String message, final long minimumExpected, final long actual) + { + assertTrue(message + " " + actual, actual >= minimumExpected); + } + + public static void assertExpectedConsumerResults(ParticipantResult result, String participantName, String registeredClientName, long expectedTestStartTime, int expectedAcknowledgeMode, Integer expectedBatchSize, Integer expectedNumberOfMessages, Integer expectedPayloadSize, Long expectedTotalPayloadProcessed, Long expectedMinimumExpectedDuration) + { + assertExpectedResults(result, participantName, registeredClientName, expectedTestStartTime, + expectedAcknowledgeMode, expectedBatchSize, expectedNumberOfMessages, expectedPayloadSize, expectedTotalPayloadProcessed, expectedMinimumExpectedDuration); + assertEquals("Unexpected number of consumers", 1, result.getTotalNumberOfConsumers()); + assertEquals("Unexpected number of producers", 0, result.getTotalNumberOfProducers()); + } + + public static void assertExpectedProducerResults(ParticipantResult result, String participantName, String registeredClientName, long expectedTestStartTime, int expectedAcknowledgeMode, Integer expectedBatchSize, Integer expectedNumberOfMessages, Integer expectedPayloadSize, Long expectedTotalPayloadProcessed, Long expectedMinimumExpectedDuration) + { + assertExpectedResults(result, participantName, registeredClientName, expectedTestStartTime, expectedAcknowledgeMode, expectedBatchSize, expectedNumberOfMessages, expectedPayloadSize, expectedTotalPayloadProcessed, expectedMinimumExpectedDuration); + assertEquals("Unexpected number of producers", 1, result.getTotalNumberOfProducers()); + assertEquals("Unexpected number of consumers", 0, result.getTotalNumberOfConsumers()); + } + + private static void assertExpectedResults(ParticipantResult result, String participantName, String registeredClientName, long expectedTestStartTime, int expectedAcknowledgeMode, Integer expectedBatchSize, Integer expectedNumberOfMessages, Integer expectedPayloadSize, Long expectedTotalPayloadProcessed, Long expectedMinimumExpectedDuration) + { + assertFalse(result.hasError()); + + assertEquals("unexpected participant name", participantName, result.getParticipantName()); + assertEquals("unexpected client name", registeredClientName, result.getRegisteredClientName()); + + assertAtLeast("start time of result is too low", expectedTestStartTime, result.getStartInMillis()); + assertAtLeast("end time of result should be after start time", result.getStartInMillis(), result.getEndInMillis()); + + assertEquals("unexpected acknowledge mode", expectedAcknowledgeMode, result.getAcknowledgeMode()); + + if(expectedNumberOfMessages != null) + { + assertEquals("unexpected number of messages", expectedNumberOfMessages.intValue(), result.getNumberOfMessagesProcessed()); + } + if(expectedBatchSize != null) + { + assertEquals("unexpected batch size", expectedBatchSize.intValue(), result.getBatchSize()); + } + if (expectedPayloadSize != null) + { + assertEquals("unexpected payload size", expectedPayloadSize.intValue(), result.getPayloadSize()); + } + if (expectedTotalPayloadProcessed != null) + { + assertEquals("unexpected total payload processed", expectedTotalPayloadProcessed.longValue(), result.getTotalPayloadProcessed()); + } + if(expectedMinimumExpectedDuration != null) + { + assertAtLeast("participant did not take a sufficient length of time.", expectedMinimumExpectedDuration, result.getTimeTaken()); + } + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/client/ProducerParticipantTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/client/ProducerParticipantTest.java new file mode 100644 index 0000000000..cf05623e8f --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/client/ProducerParticipantTest.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.disttest.client; + +import static org.apache.qpid.disttest.client.ParticipantTestHelper.assertExpectedProducerResults; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import javax.jms.DeliveryMode; +import javax.jms.Message; +import javax.jms.Session; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.DistributedTestException; +import org.apache.qpid.disttest.jms.ClientJmsDelegate; +import org.apache.qpid.disttest.message.CreateProducerCommand; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.mockito.InOrder; + +public class ProducerParticipantTest extends TestCase +{ + private ProducerParticipant _producer; + + private static final String SESSION_NAME1 = "SESSION1"; + private static final String PARTICIPANT_NAME1 = "PARTICIPANT_NAME1"; + + private static final String CLIENT_NAME = "CLIENT_NAME"; + private static final int PAYLOAD_SIZE_PER_MESSAGE = 1024; + + + private final Message _mockMessage = mock(Message.class); + private final CreateProducerCommand _command = new CreateProducerCommand(); + private ClientJmsDelegate _delegate; + private InOrder _inOrder; + + /** used to check start/end time of results */ + private long _testStartTime; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + _delegate = mock(ClientJmsDelegate.class); + _inOrder = inOrder(_delegate); + + _command.setSessionName(SESSION_NAME1); + _command.setParticipantName(PARTICIPANT_NAME1); + + when(_delegate.sendNextMessage(isA(CreateProducerCommand.class))).thenReturn(_mockMessage); + when(_delegate.calculatePayloadSizeFrom(_mockMessage)).thenReturn(PAYLOAD_SIZE_PER_MESSAGE); + when(_delegate.getAcknowledgeMode(SESSION_NAME1)).thenReturn(Session.AUTO_ACKNOWLEDGE); + + _producer = new ProducerParticipant(_delegate, _command); + + _testStartTime = System.currentTimeMillis(); + } + + public void testStartDelay() throws Exception + { + final long delay = 100; + int numberOfMessages = 1; + long totalPayloadSize = PAYLOAD_SIZE_PER_MESSAGE * numberOfMessages; + + _command.setStartDelay(delay); + _command.setNumberOfMessages(numberOfMessages); + + ParticipantResult result = _producer.doIt(CLIENT_NAME); + + long expectedPublishedStartTime = _testStartTime + delay; + assertExpectedProducerResults(result, PARTICIPANT_NAME1, CLIENT_NAME, expectedPublishedStartTime, Session.AUTO_ACKNOWLEDGE, null, numberOfMessages, PAYLOAD_SIZE_PER_MESSAGE, totalPayloadSize, null); + } + + + public void testNoMessagesToSend() throws Exception + { + _command.setNumberOfMessages(0); + _command.setMaximumDuration(0); + + try + { + _producer.doIt(CLIENT_NAME); + fail("Exception not thrown"); + } + catch (DistributedTestException e) + { + // PASS + assertEquals("number of messages and duration cannot both be zero", e.getMessage()); + } + } + + public void testOneMessageToSend() throws Exception + { + int batchSize = 1; + int numberOfMessages = 1; + long totalPayloadSize = PAYLOAD_SIZE_PER_MESSAGE * numberOfMessages; + int deliveryMode = DeliveryMode.PERSISTENT; + + _command.setNumberOfMessages(numberOfMessages); + _command.setBatchSize(batchSize); + _command.setDeliveryMode(deliveryMode); + + ParticipantResult result = (ParticipantResult) _producer.doIt(CLIENT_NAME); + assertExpectedProducerResults(result, PARTICIPANT_NAME1, CLIENT_NAME, _testStartTime, + Session.AUTO_ACKNOWLEDGE, null, numberOfMessages, PAYLOAD_SIZE_PER_MESSAGE, totalPayloadSize, null); + + _inOrder.verify(_delegate).sendNextMessage(isA(CreateProducerCommand.class)); + _inOrder.verify(_delegate).calculatePayloadSizeFrom(_mockMessage); + _inOrder.verify(_delegate).commitOrAcknowledgeMessage(_mockMessage, SESSION_NAME1); + + } + + public void testSendMessagesForDuration() throws Exception + { + final long duration = 100; + _command.setMaximumDuration(duration); + + ParticipantResult result = _producer.doIt(CLIENT_NAME); + assertExpectedProducerResults(result, PARTICIPANT_NAME1, CLIENT_NAME, _testStartTime, + Session.AUTO_ACKNOWLEDGE, null, null, PAYLOAD_SIZE_PER_MESSAGE, null, duration); + + verify(_delegate, atLeastOnce()).sendNextMessage(isA(CreateProducerCommand.class)); + verify(_delegate, atLeastOnce()).calculatePayloadSizeFrom(_mockMessage); + verify(_delegate, atLeastOnce()).commitOrAcknowledgeMessage(_mockMessage, SESSION_NAME1); + } + + public void testSendMessageBatches() throws Exception + { + final int batchSize = 3; + final int numberOfMessages = 10; + final int expectedNumberOfCommits = 4; // one for each batch of 3 messages, plus one more at the end of the test for the tenth msg. + long totalPayloadSize = PAYLOAD_SIZE_PER_MESSAGE * numberOfMessages; + + _command.setNumberOfMessages(numberOfMessages); + _command.setBatchSize(batchSize); + + ParticipantResult result = _producer.doIt(CLIENT_NAME); + assertExpectedProducerResults(result, PARTICIPANT_NAME1, CLIENT_NAME, _testStartTime, + Session.AUTO_ACKNOWLEDGE, batchSize, numberOfMessages, PAYLOAD_SIZE_PER_MESSAGE, totalPayloadSize, null); + + verify(_delegate, times(numberOfMessages)).sendNextMessage(isA(CreateProducerCommand.class)); + verify(_delegate, times(numberOfMessages)).calculatePayloadSizeFrom(_mockMessage); + verify(_delegate, times(expectedNumberOfCommits)).commitOrAcknowledgeMessage(_mockMessage, SESSION_NAME1); + } + + public void testSendMessageWithPublishInterval() throws Exception + { + final int batchSize = 3; + final long publishInterval = 100; + int numberOfMessages = 10; + long totalPayloadSize = PAYLOAD_SIZE_PER_MESSAGE * numberOfMessages; + + final long expectedTimeToRunTest = batchSize * publishInterval; + + _command.setNumberOfMessages(numberOfMessages); + _command.setBatchSize(batchSize); + _command.setInterval(publishInterval); + + ParticipantResult result = _producer.doIt(CLIENT_NAME); + assertExpectedProducerResults(result, PARTICIPANT_NAME1, CLIENT_NAME, _testStartTime, + Session.AUTO_ACKNOWLEDGE, null, numberOfMessages, null, totalPayloadSize, expectedTimeToRunTest); + + verify(_delegate, times(numberOfMessages)).sendNextMessage(isA(CreateProducerCommand.class)); + verify(_delegate, times(numberOfMessages)).calculatePayloadSizeFrom(_mockMessage); + verify(_delegate, times(4)).commitOrAcknowledgeMessage(_mockMessage, SESSION_NAME1); + } + + public void testSendMessageWithVaryingPayloadSize() throws Exception + { + int numberOfMessages = 3; + + int firstPayloadSize = PAYLOAD_SIZE_PER_MESSAGE; + int secondPayloadSize = PAYLOAD_SIZE_PER_MESSAGE * 2; + int thirdPayloadSize = PAYLOAD_SIZE_PER_MESSAGE * 4; + + final long totalPayloadSize = firstPayloadSize + secondPayloadSize + thirdPayloadSize; + + when(_delegate.calculatePayloadSizeFrom(_mockMessage)).thenReturn(firstPayloadSize, secondPayloadSize, thirdPayloadSize); + + _command.setNumberOfMessages(numberOfMessages); + + ParticipantResult result = _producer.doIt(CLIENT_NAME); + + final int expectedPayloadResultPayloadSize = 0; + assertExpectedProducerResults(result, PARTICIPANT_NAME1, CLIENT_NAME, _testStartTime, + Session.AUTO_ACKNOWLEDGE, null, numberOfMessages, expectedPayloadResultPayloadSize, totalPayloadSize, null); + + verify(_delegate, times(numberOfMessages)).sendNextMessage(isA(CreateProducerCommand.class)); + verify(_delegate, times(numberOfMessages)).calculatePayloadSizeFrom(_mockMessage); + verify(_delegate, times(numberOfMessages)).commitOrAcknowledgeMessage(_mockMessage, SESSION_NAME1); + } + + public void testReleaseResources() + { + _producer.releaseResources(); + verify(_delegate).closeTestProducer(PARTICIPANT_NAME1); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/client/property/ListPropertyValueTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/client/property/ListPropertyValueTest.java new file mode 100644 index 0000000000..75a634ba54 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/client/property/ListPropertyValueTest.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.disttest.client.property; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.client.property.ListPropertyValue; +import org.apache.qpid.disttest.client.property.PropertyValue; +import org.apache.qpid.disttest.client.property.SimplePropertyValue; + +public class ListPropertyValueTest extends TestCase +{ + private ListPropertyValue _generator; + private List<PropertyValue> _items; + + public void setUp() throws Exception + { + super.setUp(); + _generator = new ListPropertyValue(); + _items = new ArrayList<PropertyValue>(); + _items.add(new SimplePropertyValue(new Integer(1))); + _items.add(new SimplePropertyValue(new Double(2.1))); + _items.add(new SimplePropertyValue(new Boolean(true))); + ListPropertyValue innerList = new ListPropertyValue(); + List<PropertyValue> innerListItems = new ArrayList<PropertyValue>(); + innerListItems.add(new SimplePropertyValue("test")); + innerListItems.add(new SimplePropertyValue(new Integer(2))); + innerList.setItems(innerListItems); + _items.add(innerList); + _generator.setItems(_items); + } + + public void testGetItems() + { + List<? extends Object> items = _generator.getItems(); + assertEquals("Unexpected list items", _items, items); + } + + public void testGetValue() + { + for (int i = 0; i < 2; i++) + { + assertEquals("Unexpected first item", new Integer(1), _generator.getValue()); + assertEquals("Unexpected second item", new Double(2.1), _generator.getValue()); + assertEquals("Unexpected third item", new Boolean(true), _generator.getValue()); + if (i == 0) + { + assertEquals("Unexpected forth item", "test", _generator.getValue()); + } + else + { + assertEquals("Unexpected forth item", new Integer(2), _generator.getValue()); + } + } + } + + public void testNonCyclicGetValue() + { + _generator.setCyclic(false); + assertFalse("Generator should not be cyclic", _generator.isCyclic()); + assertEquals("Unexpected first item", new Integer(1), _generator.getValue()); + assertEquals("Unexpected second item", new Double(2.1), _generator.getValue()); + assertEquals("Unexpected third item", new Boolean(true), _generator.getValue()); + assertEquals("Unexpected forth item", "test", _generator.getValue()); + assertEquals("Unexpected fifth item", new Integer(2), _generator.getValue()); + assertEquals("Unexpected sixs item", "test", _generator.getValue()); + assertEquals("Unexpected sevens item", new Integer(2), _generator.getValue()); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/client/property/PropertyValueFactoryTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/client/property/PropertyValueFactoryTest.java new file mode 100644 index 0000000000..2d560163c2 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/client/property/PropertyValueFactoryTest.java @@ -0,0 +1,73 @@ +/* + * 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.disttest.client.property; + +import junit.framework.TestCase; + +public class PropertyValueFactoryTest extends TestCase +{ + private PropertyValueFactory _factory; + + public void setUp() throws Exception + { + super.setUp(); + _factory = new PropertyValueFactory(); + } + + public void testCreateListPropertyValue() + { + PropertyValue propertyValue = _factory.createPropertyValue("list"); + assertNotNull("List generator is not created", propertyValue); + assertTrue("Unexpected type of list generator", propertyValue instanceof ListPropertyValue); + } + + public void testCreateRangePropertyValue() + { + PropertyValue propertyValue = _factory.createPropertyValue("range"); + assertNotNull("Range generator is not created", propertyValue); + assertTrue("Unexpected type of range generator", propertyValue instanceof RangePropertyValue); + } + + public void testCreateRandomPropertyValue() + { + PropertyValue propertyValue = _factory.createPropertyValue("random"); + assertNotNull("Random generator is not created", propertyValue); + assertTrue("Unexpected type of range generator", propertyValue instanceof RandomPropertyValue); + } + + public void testCreateSimplePropertyValue() + { + PropertyValue propertyValue = _factory.createPropertyValue("simple"); + assertNotNull("Simple property value is not created", propertyValue); + assertTrue("Unexpected type of property value", propertyValue instanceof SimplePropertyValue); + } + + public void testCreateNonExistingPropertyValue() + { + try + { + _factory.createPropertyValue("nonExisting"); + fail("Non existing property value should not be created"); + } + catch (Exception e) + { + // pass + } + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/client/property/RandomPropertyValueTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/client/property/RandomPropertyValueTest.java new file mode 100644 index 0000000000..bd5de3e370 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/client/property/RandomPropertyValueTest.java @@ -0,0 +1,76 @@ +/* + * 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.disttest.client.property; + +import org.apache.qpid.disttest.client.property.RandomPropertyValue; + +import junit.framework.TestCase; + +public class RandomPropertyValueTest extends TestCase +{ + private RandomPropertyValue _generator; + + public void setUp() throws Exception + { + super.setUp(); + _generator = new RandomPropertyValue(); + _generator.setUpper(20.0); + _generator.setLower(10.0); + _generator.setType("double"); + } + + public void testGetters() + { + assertEquals("Unexpected upper boundary", new Double(20.0), _generator.getUpper()); + assertEquals("Unexpected lower boundary", new Double(10.0), _generator.getLower()); + assertEquals("Unexpected type", "double", _generator.getType()); + } + + public void testGetValue() + { + Object value = _generator.getValue(); + assertTrue("Unexpected type", value instanceof Double); + assertTrue("Unexpected value", ((Double) value).doubleValue() >= 10.0 + && ((Double) value).doubleValue() <= 20.0); + } + + public void testGetValueInt() + { + _generator.setType("int"); + Object value = _generator.getValue(); + assertTrue("Unexpected type", value instanceof Integer); + assertTrue("Unexpected value", ((Integer) value).intValue() >= 10 && ((Integer) value).intValue() <= 20); + } + + public void testGetValueLong() + { + _generator.setType("long"); + Object value = _generator.getValue(); + assertTrue("Unexpected type", value instanceof Long); + assertTrue("Unexpected value", ((Long) value).longValue() >= 10 && ((Long) value).longValue() <= 20); + } + + public void testGetValueFloat() + { + _generator.setType("float"); + Object value = _generator.getValue(); + assertTrue("Unexpected type", value instanceof Float); + assertTrue("Unexpected value", ((Float) value).floatValue() >= 10.0 && ((Float) value).floatValue() <= 20.0); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/client/property/RangePropertyValueTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/client/property/RangePropertyValueTest.java new file mode 100644 index 0000000000..91791c9d55 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/client/property/RangePropertyValueTest.java @@ -0,0 +1,153 @@ +/* + * 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.disttest.client.property; + +import org.apache.qpid.disttest.client.property.RangePropertyValue; + +import junit.framework.TestCase; + +public class RangePropertyValueTest extends TestCase +{ + private RangePropertyValue _generator; + + public void setUp() throws Exception + { + super.setUp(); + _generator = new RangePropertyValue(); + _generator.setUpper(10.0); + _generator.setLower(0.0); + _generator.setStep(2.0); + _generator.setType("double"); + } + + public void testGetters() + { + assertEquals("Unexpected upper boundary", new Double(10.0), _generator.getUpper()); + assertEquals("Unexpected lower boundary", new Double(0.0), _generator.getLower()); + assertEquals("Unexpected step", new Double(2.0), _generator.getStep()); + assertEquals("Unexpected type", "double", _generator.getType()); + assertTrue("Unexpected cyclic", _generator.isCyclic()); + } + + public void testGetValue() + { + double[] expected = { 0.0, 2.0, 4.0, 6.0, 8.0, 10.0 }; + for (int j = 0; j < 2; j++) + { + for (int i = 0; i < expected.length; i++) + { + Object value = _generator.getValue(); + assertTrue("Should be Double", value instanceof Double); + assertEquals("Unexpected value ", expected[i], value); + } + } + } + + public void testGetValueNonCyclic() + { + _generator.setCyclic(false); + double[] expected = { 0.0, 2.0, 4.0, 6.0, 8.0, 10.0 }; + for (int i = 0; i < expected.length; i++) + { + Object value = _generator.getValue(); + assertTrue("Should be Double", value instanceof Double); + assertEquals("Unexpected value ", expected[i], value); + } + for (int i = 0; i < expected.length; i++) + { + Object value = _generator.getValue(); + assertEquals("Unexpected value ", expected[expected.length - 1], value); + } + } + + public void testGetValueInt() + { + _generator.setType("int"); + int[] expected = { 0, 2, 4, 6, 8, 10 }; + for (int j = 0; j < 2; j++) + { + for (int i = 0; i < expected.length; i++) + { + Object value = _generator.getValue(); + assertTrue("Should be Double", value instanceof Integer); + assertEquals("Unexpected value ", expected[i], value); + } + } + } + + public void testGetValueByte() + { + _generator.setType("byte"); + byte[] expected = { 0, 2, 4, 6, 8, 10 }; + for (int j = 0; j < 2; j++) + { + for (int i = 0; i < expected.length; i++) + { + Object value = _generator.getValue(); + assertTrue("Should be Double", value instanceof Byte); + assertEquals("Unexpected value ", expected[i], value); + } + } + } + + public void testGetValueLong() + { + _generator.setType("long"); + long[] expected = { 0, 2, 4, 6, 8, 10 }; + for (int j = 0; j < 2; j++) + { + for (int i = 0; i < expected.length; i++) + { + Object value = _generator.getValue(); + assertTrue("Should be Double", value instanceof Long); + assertEquals("Unexpected value ", expected[i], value); + } + } + } + + public void testGetValueShort() + { + _generator.setType("short"); + short[] expected = { 0, 2, 4, 6, 8, 10 }; + for (int j = 0; j < 2; j++) + { + for (int i = 0; i < expected.length; i++) + { + Object value = _generator.getValue(); + assertTrue("Should be Double", value instanceof Short); + assertEquals("Unexpected value ", expected[i], value); + } + } + } + + public void testGetValueFloat() + { + _generator.setType("float"); + float[] expected = { 0.0f, 2.0f, 4.0f, 6.0f, 8.0f, 10.0f }; + for (int j = 0; j < 2; j++) + { + for (int i = 0; i < expected.length; i++) + { + Object value = _generator.getValue(); + assertTrue("Should be Double", value instanceof Float); + assertEquals("Unexpected value ", expected[i], value); + } + } + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/client/property/SimplePropertyValueTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/client/property/SimplePropertyValueTest.java new file mode 100644 index 0000000000..a347d866c7 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/client/property/SimplePropertyValueTest.java @@ -0,0 +1,30 @@ +/* + * 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.disttest.client.property; + +import junit.framework.TestCase; + +public class SimplePropertyValueTest extends TestCase +{ + public void testGetValue() + { + SimplePropertyValue value = new SimplePropertyValue(new Integer(1)); + assertEquals("Unexpected value", new Integer(1), value.getValue()); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/controller/ClientRegistryTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/ClientRegistryTest.java new file mode 100644 index 0000000000..cc969e1ef2 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/ClientRegistryTest.java @@ -0,0 +1,99 @@ +/* + * 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.disttest.controller; + +import java.util.Timer; +import java.util.TimerTask; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.DistributedTestException; + +public class ClientRegistryTest extends TestCase +{ + private static final String CLIENT1_REGISTERED_NAME = "CLIENT1_REGISTERED_NAME"; + private static final String CLIENT2_REGISTERED_NAME = "CLIENT2_REGISTERED_NAME"; + private static final int AWAIT_DELAY = 100; + + private ClientRegistry _clientRegistry = new ClientRegistry(); + + public void testRegisterClient() + { + assertEquals(0, _clientRegistry.getClients().size()); + + _clientRegistry.registerClient(CLIENT1_REGISTERED_NAME); + assertEquals(1, _clientRegistry.getClients().size()); + + } + + public void testRejectsDuplicateClientNames() + { + _clientRegistry.registerClient(CLIENT1_REGISTERED_NAME); + try + { + _clientRegistry.registerClient(CLIENT1_REGISTERED_NAME); + fail("Should have thrown an exception"); + } + catch (final DistributedTestException e) + { + // pass + } + } + + public void testAwaitOneClientWhenClientNotRegistered() + { + int numberOfClientsAbsent = _clientRegistry.awaitClients(1, AWAIT_DELAY); + assertEquals(1, numberOfClientsAbsent); + } + + public void testAwaitOneClientWhenClientAlreadyRegistered() + { + _clientRegistry.registerClient(CLIENT1_REGISTERED_NAME); + + int numberOfClientsAbsent = _clientRegistry.awaitClients(1, AWAIT_DELAY); + assertEquals(0, numberOfClientsAbsent); + } + + public void testAwaitTwoClientWhenClientRegistersWhilstWaiting() + { + _clientRegistry.registerClient(CLIENT1_REGISTERED_NAME); + registerClientLater(CLIENT2_REGISTERED_NAME, 50); + + int numberOfClientsAbsent = _clientRegistry.awaitClients(2, AWAIT_DELAY); + assertEquals(0, numberOfClientsAbsent); + } + + private void registerClientLater(final String clientName, long delayInMillis) + { + doLater(new TimerTask() + { + @Override + public void run() + { + _clientRegistry.registerClient(clientName); + } + }, delayInMillis); + } + + private void doLater(TimerTask task, long delayInMillis) + { + Timer timer = new Timer(); + timer.schedule(task, delayInMillis); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/controller/ControllerTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/ControllerTest.java new file mode 100644 index 0000000000..bc58ea41c5 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/ControllerTest.java @@ -0,0 +1,198 @@ +/* + * 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.disttest.controller; + +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.DistributedTestException; +import org.apache.qpid.disttest.controller.config.Config; +import org.apache.qpid.disttest.controller.config.TestInstance; +import org.apache.qpid.disttest.jms.ControllerJmsDelegate; +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.RegisterClientCommand; +import org.apache.qpid.disttest.message.Response; +import org.apache.qpid.disttest.message.StopClientCommand; +import org.apache.qpid.disttest.results.aggregation.ITestResult; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +public class ControllerTest extends TestCase +{ + private static final String CLIENT1_REGISTERED_NAME = "client-uid1"; + + private static final long COMMAND_RESPONSE_TIMEOUT = 1000; + private static final long REGISTRATION_TIMEOUT = 1000; + + private Controller _controller; + private ControllerJmsDelegate _respondingJmsDelegate; + private TestRunner _testRunner; + private ClientRegistry _clientRegistry; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + _respondingJmsDelegate = mock(ControllerJmsDelegate.class); + _controller = new Controller(_respondingJmsDelegate, REGISTRATION_TIMEOUT, COMMAND_RESPONSE_TIMEOUT); + _testRunner = mock(TestRunner.class); + _clientRegistry = mock(ClientRegistry.class); + + Config configWithOneClient = createMockConfig(1); + _controller.setConfig(configWithOneClient); + _controller.setClientRegistry(_clientRegistry); + _controller.setTestRunnerFactory(createTestFactoryReturningMock()); + + doAnswer(new Answer<Void>() + { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable + { + final String clientName = (String)invocation.getArguments()[0]; + final Command command = (Command)invocation.getArguments()[1]; + _controller.processStopClientResponse(new Response(clientName, command.getType())); + return null; + } + }).when(_respondingJmsDelegate).sendCommandToClient(anyString(), isA(Command.class)); + } + + + public void testControllerRejectsEmptyConfiguration() + { + Config configWithZeroClients = createMockConfig(0); + + try + { + _controller.setConfig(configWithZeroClients); + fail("Exception not thrown"); + } + catch (DistributedTestException e) + { + // PASS + } + } + + public void testControllerReceivesTwoExpectedClientRegistrations() + { + Config configWithTwoClients = createMockConfig(2); + _controller.setConfig(configWithTwoClients); + when(_clientRegistry.awaitClients(2, REGISTRATION_TIMEOUT)).thenReturn(0); + + _controller.awaitClientRegistrations(); + } + + public void testControllerDoesntReceiveAnyRegistrations() + { + when(_clientRegistry.awaitClients(1, REGISTRATION_TIMEOUT)).thenReturn(1); + + try + { + _controller.awaitClientRegistrations(); + fail("Exception not thrown"); + } + catch (DistributedTestException e) + { + // PASS + } + } + + public void testRegisterClient() + { + RegisterClientCommand command = new RegisterClientCommand(CLIENT1_REGISTERED_NAME, "dummy"); + _controller.registerClient(command); + + verify(_clientRegistry).registerClient(CLIENT1_REGISTERED_NAME); + verify(_respondingJmsDelegate).registerClient(command); + + } + + public void testControllerSendsClientStopCommandToClient() + { + when(_clientRegistry.getClients()).thenReturn(Collections.singleton(CLIENT1_REGISTERED_NAME)); + + _controller.stopAllRegisteredClients(); + + verify(_respondingJmsDelegate).sendCommandToClient(eq(CLIENT1_REGISTERED_NAME), isA(StopClientCommand.class)); + } + + public void testRunAllTests() + { + Config config = createSimpleConfig(); + _controller.setConfig(config); + + TestResult testResult = new TestResult("test1"); + + when(_testRunner.run()).thenReturn(testResult); + + ResultsForAllTests results = _controller.runAllTests(); + + List<ITestResult> testResults = results.getTestResults(); + assertEquals(1, testResults.size()); + assertSame(testResult, testResults.get(0)); + + verify(_testRunner).run(); + } + + private Config createSimpleConfig() + { + Config config = mock(Config.class); + TestInstance testInstance = mock(TestInstance.class); + + List<TestInstance> testInstances = Arrays.asList(testInstance); + + when(config.getTests()).thenReturn(testInstances); + when(config.getTotalNumberOfClients()).thenReturn(1); // necessary otherwise controller rejects "invalid" config + + return config; + } + + private Config createMockConfig(int numberOfClients) + { + Config config = mock(Config.class); + when(config.getTotalNumberOfClients()).thenReturn(numberOfClients); + return config; + } + + private TestRunnerFactory createTestFactoryReturningMock() + { + TestRunnerFactory testRunnerFactory = mock(TestRunnerFactory.class); + + when(testRunnerFactory.createTestRunner( + isA(ParticipatingClients.class), + isA(TestInstance.class), + isA(ControllerJmsDelegate.class), + isA(Long.class), + isA(Long.class))) + .thenReturn(_testRunner); + + return testRunnerFactory; + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/controller/ParticipatingClientsTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/ParticipatingClientsTest.java new file mode 100644 index 0000000000..284db38f44 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/ParticipatingClientsTest.java @@ -0,0 +1,144 @@ +/* + * 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.disttest.controller; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import junit.framework.TestCase; + +public class ParticipatingClientsTest extends TestCase +{ + private static final String CLIENT1_CONFIGURED_NAME = "CLIENT1_CONFIGURED_NAME"; + private static final String CLIENT2_CONFIGURED_NAME = "CLIENT2_CONFIGURED_NAME"; + + private static final String CLIENT1_REGISTERED_NAME = "CLIENT1_REGISTERED_NAME"; + private static final String CLIENT2_REGISTERED_NAME = "CLIENT2_REGISTERED_NAME"; + private static final String CLIENT3_REGISTERED_NAME = "CLIENT3_REGISTERED_NAME"; + private ClientRegistry _clientRegistry; + private List<String> _configuredClientNamesForTest; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + _clientRegistry = mock(ClientRegistry.class); + } + + public void testTooFewRegisteredClientsForTest() + { + _configuredClientNamesForTest = Arrays.asList(CLIENT1_CONFIGURED_NAME, CLIENT2_CONFIGURED_NAME); + when(_clientRegistry.getClients()).thenReturn(Arrays.asList(CLIENT1_REGISTERED_NAME)); + + try + { + new ParticipatingClients(_clientRegistry, _configuredClientNamesForTest); + fail("Exception not thrown"); + } + catch (IllegalArgumentException e) + { + // PASS + } + + } + + + public void testSelectOneClientFromPoolOfOne() + { + _configuredClientNamesForTest = Arrays.asList(CLIENT1_CONFIGURED_NAME); + when(_clientRegistry.getClients()).thenReturn(Arrays.asList(CLIENT1_REGISTERED_NAME)); + + ParticipatingClients clients = new ParticipatingClients(_clientRegistry, _configuredClientNamesForTest); + assertBothWays(clients, CLIENT1_REGISTERED_NAME, CLIENT1_CONFIGURED_NAME); + } + + public void testSelectTwoClientFromPoolOfMany() + { + _configuredClientNamesForTest = Arrays.asList(CLIENT1_CONFIGURED_NAME, CLIENT2_CONFIGURED_NAME); + when(_clientRegistry.getClients()).thenReturn(Arrays.asList(CLIENT1_REGISTERED_NAME, CLIENT2_REGISTERED_NAME, CLIENT3_REGISTERED_NAME)); + + ParticipatingClients clients = new ParticipatingClients(_clientRegistry, _configuredClientNamesForTest); + + assertBothWays(clients, CLIENT1_REGISTERED_NAME, CLIENT1_CONFIGURED_NAME); + assertBothWays(clients, CLIENT2_REGISTERED_NAME, CLIENT2_CONFIGURED_NAME); + } + + public void testGetUnrecognisedConfiguredName() + { + _configuredClientNamesForTest = Arrays.asList(CLIENT1_CONFIGURED_NAME); + when(_clientRegistry.getClients()).thenReturn(Arrays.asList(CLIENT1_REGISTERED_NAME)); + + ParticipatingClients clients = new ParticipatingClients(_clientRegistry, _configuredClientNamesForTest); + + testUnrecognisedClientConfiguredName(clients, "unknown"); + testUnrecognisedClientRegisteredName(clients, "unknown"); + } + + public void testGetRegisteredClientNames() + { + _configuredClientNamesForTest = Arrays.asList(CLIENT1_CONFIGURED_NAME); + List<String> registeredNames = Arrays.asList(CLIENT1_REGISTERED_NAME); + when(_clientRegistry.getClients()).thenReturn(registeredNames); + + ParticipatingClients clients = new ParticipatingClients(_clientRegistry, _configuredClientNamesForTest); + + Collection<String> registeredParticipatingNames = clients.getRegisteredNames(); + assertEquals(1, registeredParticipatingNames.size()); + assertTrue(registeredParticipatingNames.contains(CLIENT1_REGISTERED_NAME)); + } + + private void testUnrecognisedClientConfiguredName(ParticipatingClients clients, String unrecognisedClientConfiguredName) + { + try + { + clients.getRegisteredNameFromConfiguredName(unrecognisedClientConfiguredName); + fail("Exception not thrown"); + } + catch (IllegalArgumentException e) + { + // PASS + } + } + + private void testUnrecognisedClientRegisteredName(ParticipatingClients clients, String unrecognisedClientRegisteredName) + { + try + { + clients.getConfiguredNameFromRegisteredName(unrecognisedClientRegisteredName); + fail("Exception not thrown"); + } + catch (IllegalArgumentException e) + { + // PASS + } + } + + private void assertBothWays(ParticipatingClients clients, String registeredName, String configuredName) + { + assertEquals(registeredName, clients.getRegisteredNameFromConfiguredName(configuredName)); + assertEquals(configuredName, clients.getConfiguredNameFromRegisteredName(registeredName)); + } + + + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/controller/TestRunnerTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/TestRunnerTest.java new file mode 100644 index 0000000000..983da299b9 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/TestRunnerTest.java @@ -0,0 +1,253 @@ +/* + * 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.disttest.controller; + +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.DistributedTestException; +import org.apache.qpid.disttest.controller.config.QueueConfig; +import org.apache.qpid.disttest.controller.config.TestInstance; +import org.apache.qpid.disttest.jms.ControllerJmsDelegate; +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.CreateConnectionCommand; +import org.apache.qpid.disttest.message.NoOpCommand; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.disttest.message.Response; +import org.apache.qpid.disttest.message.StartTestCommand; +import org.apache.qpid.disttest.message.TearDownTestCommand; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +public class TestRunnerTest extends TestCase +{ + private static final String TEST_NAME = "TEST_NAME"; + private static final String PARTICIPANT_NAME = "TEST_PARTICIPANT_NAME"; + private static final int ITERATION_NUMBER = 1; + + private static final String CLIENT1_REGISTERED_NAME = "client-uid1"; + private static final String CLIENT1_CONFIGURED_NAME = "client1"; + + private static final long COMMAND_RESPONSE_TIMEOUT = 1000; + private static final long TEST_RESULT_TIMEOUT = 2000; + private static final long DELAY = 100; + + private TestRunner _testRunner; + private TestInstance _testInstance; + private ControllerJmsDelegate _respondingJmsDelegate; + private ParticipatingClients _participatingClients; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + _respondingJmsDelegate = mock(ControllerJmsDelegate.class); + + _participatingClients = mock(ParticipatingClients.class); + when(_participatingClients.getRegisteredNameFromConfiguredName(CLIENT1_CONFIGURED_NAME)).thenReturn(CLIENT1_REGISTERED_NAME); + when(_participatingClients.getConfiguredNameFromRegisteredName(CLIENT1_REGISTERED_NAME)).thenReturn(CLIENT1_CONFIGURED_NAME); + when(_participatingClients.getRegisteredNames()).thenReturn(Collections.singleton(CLIENT1_REGISTERED_NAME)); + + doAnswer(new Answer<Void>() + { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable + { + final String clientName = (String)invocation.getArguments()[0]; + final Command command = (Command)invocation.getArguments()[1]; + _testRunner.processCommandResponse(new Response(clientName, command.getType())); + return null; + } + }).when(_respondingJmsDelegate).sendCommandToClient(anyString(), isA(Command.class)); + } + + public void testSendConnectionCommandToClient() + { + _testInstance = createTestInstanceWithConnection(); + + _testRunner = new TestRunner(_participatingClients, _testInstance , _respondingJmsDelegate, COMMAND_RESPONSE_TIMEOUT, TEST_RESULT_TIMEOUT); + _testRunner.sendTestSetupCommands(); + + verify(_respondingJmsDelegate).sendCommandToClient(eq(CLIENT1_REGISTERED_NAME), isA(CreateConnectionCommand.class)); + } + + public void testSendCommandToAllParticipatingClients() + { + _testRunner = new TestRunner(_participatingClients, mock(TestInstance.class), _respondingJmsDelegate, COMMAND_RESPONSE_TIMEOUT, TEST_RESULT_TIMEOUT); + + StartTestCommand startTestCommand = new StartTestCommand(); + _testRunner.sendCommandToParticipatingClients(startTestCommand); + + verify(_respondingJmsDelegate).sendCommandToClient(CLIENT1_REGISTERED_NAME, startTestCommand); + } + + public void testWaitsForCommandResponses() + { + _testInstance = createTestInstanceWithConnection(); + _testRunner = new TestRunner(_participatingClients, _testInstance , _respondingJmsDelegate, COMMAND_RESPONSE_TIMEOUT, TEST_RESULT_TIMEOUT); + + _testRunner.sendTestSetupCommands(); + + _testRunner.awaitCommandResponses(); + } + + public void testClientFailsToSendCommandResponseWithinTimeout() + { + ControllerJmsDelegate jmsDelegate = mock(ControllerJmsDelegate.class); + + _testInstance = createTestInstanceWithConnection(); + _testRunner = new TestRunner(_participatingClients, _testInstance , jmsDelegate, COMMAND_RESPONSE_TIMEOUT, TEST_RESULT_TIMEOUT); + + _testRunner.sendTestSetupCommands(); + // we don't call sendCommandResponseLater so controller should time out + + try + { + _testRunner.awaitCommandResponses(); + fail("Exception not thrown"); + } + catch (DistributedTestException e) + { + // PASS + } + } + + public void testCreateAndDeleteQueues() + { + _testInstance = mock(TestInstance.class); + List<QueueConfig> queues = mock(List.class); + when(_testInstance.getQueues()).thenReturn(queues); + + _testRunner = new TestRunner(_participatingClients, _testInstance, _respondingJmsDelegate, COMMAND_RESPONSE_TIMEOUT, TEST_RESULT_TIMEOUT); + + _testRunner.createQueues(); + verify(_respondingJmsDelegate).createQueues(queues); + + _testRunner.deleteQueues(); + verify(_respondingJmsDelegate).deleteQueues(queues); + } + + public void testRun() + { + _testInstance = createTestInstanceWithOneParticipant(); + _testRunner = new TestRunner(_participatingClients, _testInstance , _respondingJmsDelegate, COMMAND_RESPONSE_TIMEOUT, TEST_RESULT_TIMEOUT); + + ParticipantResult incomingParticipantResult = new ParticipantResult(PARTICIPANT_NAME); + incomingParticipantResult.setRegisteredClientName(CLIENT1_REGISTERED_NAME); + sendTestResultsLater(_testRunner, incomingParticipantResult); + + TestResult results = _testRunner.run(); + + verify(_respondingJmsDelegate).addCommandListener(isA(TestRunner.TestCommandResponseListener.class)); + verify(_respondingJmsDelegate).addCommandListener(isA(TestRunner.ParticipantResultListener.class)); + + verify(_respondingJmsDelegate).createQueues(isA(List.class)); + + verify(_respondingJmsDelegate).sendCommandToClient(eq(CLIENT1_REGISTERED_NAME), isA(StartTestCommand.class)); + verify(_respondingJmsDelegate).sendCommandToClient(eq(CLIENT1_REGISTERED_NAME), isA(NoOpCommand.class)); + verify(_respondingJmsDelegate).sendCommandToClient(eq(CLIENT1_REGISTERED_NAME), isA(TearDownTestCommand.class)); + + verify(_respondingJmsDelegate).deleteQueues(isA(List.class)); + + verify(_respondingJmsDelegate).removeCommandListener(isA(TestRunner.ParticipantResultListener.class)); + verify(_respondingJmsDelegate).removeCommandListener(isA(TestRunner.TestCommandResponseListener.class)); + + List<ParticipantResult> participantResults = results.getParticipantResults(); + assertEquals(1, participantResults.size()); + ParticipantResult resultingParticipantResult = participantResults.get(0); + + assertResultHasCorrectTestDetails(resultingParticipantResult); + } + + private void assertResultHasCorrectTestDetails(ParticipantResult resultingParticipantResult) + { + assertEquals("Test runner should have set configured name when it received participant results", + CLIENT1_CONFIGURED_NAME, resultingParticipantResult.getConfiguredClientName()); + assertEquals("Test runner should have set test name when it received participant results", + TEST_NAME, resultingParticipantResult.getTestName()); + assertEquals("Test runner should have set test iteration number when it received participant results", + ITERATION_NUMBER, resultingParticipantResult.getIterationNumber()); + } + + + private TestInstance createTestInstanceWithOneParticipant() + { + TestInstance testInstance = mock(TestInstance.class); + + List<CommandForClient> commands = Arrays.asList( + new CommandForClient(CLIENT1_CONFIGURED_NAME, new NoOpCommand())); + + when(testInstance.createCommands()).thenReturn(commands); + + when(testInstance.getTotalNumberOfParticipants()).thenReturn(1); + + when(testInstance.getName()).thenReturn(TEST_NAME); + + List<QueueConfig> queues = mock(List.class); + when(testInstance.getQueues()).thenReturn(queues); + + when(testInstance.getIterationNumber()).thenReturn(ITERATION_NUMBER); + + return testInstance; + } + + private TestInstance createTestInstanceWithConnection() + { + TestInstance testInstance = mock(TestInstance.class); + + List<CommandForClient> commands = Arrays.asList( + new CommandForClient(CLIENT1_CONFIGURED_NAME, new CreateConnectionCommand("conn1", "factory"))); + + when(testInstance.createCommands()).thenReturn(commands); + + return testInstance; + } + + private void sendTestResultsLater(final TestRunner runner, final ParticipantResult result) + { + doLater(new TimerTask() + { + @Override + public void run() + { + runner.processParticipantResult(result); + } + }, DELAY); + } + + private void doLater(TimerTask task, long delayInMillis) + { + Timer timer = new Timer(); + timer.schedule(task, delayInMillis); + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ClientConfigTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ClientConfigTest.java new file mode 100644 index 0000000000..d4af439dea --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ClientConfigTest.java @@ -0,0 +1,111 @@ +/* + * 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.disttest.controller.config; + +import static org.apache.qpid.disttest.controller.config.ConfigTestUtils.assertCommandForClient; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.client.property.PropertyValue; +import org.apache.qpid.disttest.client.property.SimplePropertyValue; +import org.apache.qpid.disttest.controller.CommandForClient; +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.CreateMessageProviderCommand; +import org.apache.qpid.disttest.message.NoOpCommand; + +public class ClientConfigTest extends TestCase +{ + private static final String CLIENT1 = "client1"; + + public void testClientConfigHasZeroArgConstructorForGson() + { + ClientConfig c = new ClientConfig(); + assertNotNull(c); + } + + public void testCreateCommands() + { + ClientConfig clientConfig = createClientConfigWithConnectionConfigReturningChildCommands(); + + List<CommandForClient> commands = clientConfig.createCommands(); + assertEquals(2, commands.size()); + + assertCommandForClient(commands, 0, CLIENT1, NoOpCommand.class); + assertCommandForClient(commands, 1, CLIENT1, NoOpCommand.class); + } + + public void testCreateCommandsForMessageProvider() + { + ClientConfig clientConfig = createClientConfigWithMessageProviderConfigReturningCommands(); + + List<CommandForClient> commands = clientConfig.createCommands(); + assertEquals(1, commands.size()); + + assertCommandForClient(commands, 0, CLIENT1, CreateMessageProviderCommand.class); + } + + public void testGetTotalNumberOfParticipants() + { + ClientConfig clientConfig = createClientConfigWithTwoParticipants(); + assertEquals(2, clientConfig.getTotalNumberOfParticipants()); + } + + private ClientConfig createClientConfigWithConnectionConfigReturningChildCommands() + { + ConnectionConfig connectionConfig = mock(ConnectionConfig.class); + + List<Command> commands = Arrays.asList((Command)new NoOpCommand(), (Command)new NoOpCommand()); + when(connectionConfig.createCommands()).thenReturn(commands); + + return new ClientConfig(CLIENT1, connectionConfig); + } + + private ClientConfig createClientConfigWithMessageProviderConfigReturningCommands() + { + Map<String, PropertyValue> messageProperties = new HashMap<String, PropertyValue>(); + messageProperties.put("test", new SimplePropertyValue("testValue")); + MessageProviderConfig config = new MessageProviderConfig("test", messageProperties); + + List<MessageProviderConfig> providerConfigs = new ArrayList<MessageProviderConfig>(); + providerConfigs.add(config); + + return new ClientConfig(CLIENT1, new ArrayList<ConnectionConfig>(), providerConfigs); + } + + private ClientConfig createClientConfigWithTwoParticipants() + { + ConnectionConfig connectionConfig1 = mock(ConnectionConfig.class); + ConnectionConfig connectionConfig2 = mock(ConnectionConfig.class); + + when(connectionConfig1.getTotalNumberOfParticipants()).thenReturn(1); + when(connectionConfig2.getTotalNumberOfParticipants()).thenReturn(1); + + ClientConfig clientConfig = new ClientConfig(CLIENT1, connectionConfig1, connectionConfig2); + return clientConfig; + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ConfigReaderTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ConfigReaderTest.java new file mode 100644 index 0000000000..af9ec28db0 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ConfigReaderTest.java @@ -0,0 +1,113 @@ +/* + * 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.disttest.controller.config; + +import java.io.Reader; +import java.util.List; +import java.util.Map; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.ConfigFileHelper; +import org.apache.qpid.disttest.client.MessageProvider; +import org.apache.qpid.disttest.client.property.PropertyValue; +import org.apache.qpid.disttest.controller.CommandForClient; + +public class ConfigReaderTest extends TestCase +{ + private Config _config; + + @Override + protected void setUp() + { + ConfigReader configReader = new ConfigReader(); + Reader reader = ConfigFileHelper.getConfigFileReader(getClass(), "sampleConfig.json"); + _config = configReader.readConfig(reader); + } + + public void testReadTest() + { + List<TestConfig> tests = _config.getTestConfigs(); + assertEquals("Unexpected number of tests", 2, tests.size()); + TestConfig test1Config = tests.get(0); + assertNotNull("Test 1 configuration is expected", test1Config); + assertEquals("Unexpected test name", "Test 1", test1Config.getName()); + + TestConfig test2Config = tests.get(1); + assertNotNull("Test 2 configuration is expected", test2Config); + } + + public void testReadsTestWithQueues() + { + TestConfig test1Config = _config.getTestConfigs().get(0); + List<QueueConfig> queues = test1Config.getQueues(); + assertEquals("Unexpected number of queues", 2, queues.size()); + QueueConfig queue1Config = queues.get(0); + assertNotNull("Expected queue 1 config", queue1Config); + assertEquals("Unexpected queue name", "Json-Queue-Name", queue1Config.getName()); + assertTrue("Unexpected attributes", queue1Config.getAttributes().isEmpty()); + assertFalse("Unexpected durable", queue1Config.isDurable()); + + QueueConfig queue2Config = queues.get(1); + assertNotNull("Expected queue 2 config", queue2Config); + assertEquals("Unexpected queue name", "Json Queue Name 2", queue2Config.getName()); + assertTrue("Unexpected durable", queue2Config.isDurable()); + Map<String, Object> attributes = queue2Config.getAttributes(); + assertNotNull("Expected attributes", attributes); + assertFalse("Attributes are not loaded", attributes.isEmpty()); + assertEquals("Unexpected number of attributes", 1, attributes.size()); + assertEquals("Unexpected attribute 'x-qpid-priorities' value", 10, + ((Number)attributes.get("x-qpid-priorities")).intValue()); + } + + public void testReadsTestWithIterations() + { + TestConfig testConfig = _config.getTestConfigs().get(0); + List<IterationValue> iterationValues = testConfig.getIterationValues(); + assertEquals("Unexpected number of iterations", 2, iterationValues.size()); + + IterationValue iteration1 = iterationValues.get(0); + + String messageSizeProperty = "_messageSize"; + + assertEquals("Unexpected value for property " + messageSizeProperty, + "100", + iteration1.getIterationPropertyValuesWithUnderscores().get(messageSizeProperty)); + } + + public void testReadsMessageProviders() + { + TestConfig testConfig = _config.getTestConfigs().get(0); + ClientConfig cleintConfig = testConfig.getClients().get(0); + List<MessageProviderConfig> configs = cleintConfig.getMessageProviders(); + assertNotNull("Message provider configs should not be null", configs); + assertEquals("Unexpected number of message providers", 1, configs.size()); + MessageProviderConfig messageProvider = configs.get(0); + assertNotNull("Message provider config should not be null", messageProvider); + assertEquals("Unexpected provider name", "testProvider1", messageProvider.getName()); + Map<String, PropertyValue> properties = messageProvider.getMessageProperties(); + assertNotNull("Message properties should not be null", properties); + assertEquals("Unexpected number of message properties", 3, properties.size()); + assertNotNull("test property is not found", properties.get("test")); + assertNotNull("priority property is not found", properties.get("priority")); + assertNotNull("id property is not found", properties.get("id")); + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ConfigTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ConfigTest.java new file mode 100644 index 0000000000..88750b9737 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ConfigTest.java @@ -0,0 +1,56 @@ +/* + * 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.disttest.controller.config; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.List; + +import junit.framework.TestCase; + +public class ConfigTest extends TestCase +{ + public void testGetTestsForTestWithIteratingMessageSizes() + { + Config config = createConfigWithIteratingMessageSizes(); + List<TestInstance> testConfigs = config.getTests(); + + assertEquals("should have a test config for each message size", 2, testConfigs.size()); + + TestInstance instance0 = testConfigs.get(0); + assertEquals(0, instance0.getIterationNumber()); + + TestInstance instance1 = testConfigs.get(1); + assertEquals(1, instance1.getIterationNumber()); + } + + private Config createConfigWithIteratingMessageSizes() + { + TestConfig testConfig = mock(TestConfig.class); + + when(testConfig.getIterationValues()).thenReturn(Arrays.asList(new IterationValue(),new IterationValue())); + + Config config = new Config(testConfig); + + return config; + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ConfigTestUtils.java b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ConfigTestUtils.java new file mode 100644 index 0000000000..ce5f92724f --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ConfigTestUtils.java @@ -0,0 +1,56 @@ +/* + * 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.disttest.controller.config; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + +import java.util.List; + +import org.apache.qpid.disttest.controller.CommandForClient; +import org.apache.qpid.disttest.message.Command; + +public class ConfigTestUtils +{ + public static <C extends Command> void assertCommandForClient(final List<CommandForClient> commandsForClients, final int index, final String expectedRegisteredClientName, final Class<C> expectedCommandClass) + { + final CommandForClient commandForClient = commandsForClients.get(index); + assertEquals(expectedRegisteredClientName, commandForClient.getClientName()); + final Command command = commandForClient.getCommand(); + assertTrue("Command " + index + " is of class " + command.getClass() + " but expecting " + expectedCommandClass, + expectedCommandClass.isAssignableFrom(command.getClass())); + } + + public static <C extends Command> void assertCommandEquals(final List<Command> commands, final int index, final Class<C> expectedCommandClass) + { + @SuppressWarnings("unchecked") + C command = (C) getCommand(commands, index); //explicit cast added to get round oracle compiler bug (id 6302954) + assertTrue("Command " + index + " is of class " + command.getClass() + " but expecting " + expectedCommandClass, + expectedCommandClass.isAssignableFrom(command.getClass())); + } + + public static <C extends Command> C getCommand(final List<Command> commands, final int index) + { + @SuppressWarnings("unchecked") + C command = (C) commands.get(index); + return command; + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ConnectionConfigTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ConnectionConfigTest.java new file mode 100644 index 0000000000..7c839ed462 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ConnectionConfigTest.java @@ -0,0 +1,94 @@ +/* + * 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.disttest.controller.config; + +import static org.apache.qpid.disttest.controller.config.ConfigTestUtils.assertCommandEquals; +import static org.apache.qpid.disttest.controller.config.ConfigTestUtils.getCommand; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.List; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.CreateConnectionCommand; +import org.apache.qpid.disttest.message.NoOpCommand; + +public class ConnectionConfigTest extends TestCase +{ + private static final String CONNECTION_FACTORY_NAME = "ConnectionFactoryName"; + private static final String CONNECTION_NAME = "ConnectionName"; + + public void testConnectionConfigHasZeroArgConstructorForGson() + { + ConnectionConfig c = new ConnectionConfig(); + assertNotNull(c); + } + + public void testCreateCommandsForConnectionAndChildren() + { + ConnectionConfig connectionConfig = createConnectionConfigWithChildCommands(); + + List<Command> commands = connectionConfig.createCommands(); + assertEquals(3, commands.size()); + + assertCommandEquals(commands, 0, CreateConnectionCommand.class); + assertCommandEquals(commands, 1, NoOpCommand.class); + assertCommandEquals(commands, 2, NoOpCommand.class); + + CreateConnectionCommand createConnectionCommand = getCommand(commands, 0); + assertEquals(CONNECTION_NAME, createConnectionCommand.getConnectionName()); + assertEquals(CONNECTION_FACTORY_NAME, createConnectionCommand.getConnectionFactoryName()); + } + + public void testGetTotalNumberOfParticipants() + { + ConnectionConfig connectionConfig = createConnectionConfigWithTwoParticipants(); + assertEquals(2, connectionConfig.getTotalNumberOfParticipants()); + } + + private ConnectionConfig createConnectionConfigWithTwoParticipants() + { + SessionConfig sessionConfig1 = mock(SessionConfig.class); + SessionConfig sessionConfig2 = mock(SessionConfig.class); + + when(sessionConfig1.getTotalNumberOfParticipants()).thenReturn(1); + when(sessionConfig2.getTotalNumberOfParticipants()).thenReturn(1); + + ConnectionConfig connectionConfig = new ConnectionConfig(CONNECTION_NAME, CONNECTION_FACTORY_NAME, sessionConfig1, sessionConfig2); + + return connectionConfig; + } + + private ConnectionConfig createConnectionConfigWithChildCommands() + { + SessionConfig sessionConfig = mock(SessionConfig.class); + + NoOpCommand cmd1 = mock(NoOpCommand.class); + NoOpCommand cmd2 = mock(NoOpCommand.class); + List<Command> commands = Arrays.asList((Command)cmd1, (Command)cmd2); + when(sessionConfig.createCommands(CONNECTION_NAME)).thenReturn(commands); + + return new ConnectionConfig(CONNECTION_NAME, CONNECTION_FACTORY_NAME, sessionConfig); + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ConsumerConfigTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ConsumerConfigTest.java new file mode 100644 index 0000000000..c011ff4711 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ConsumerConfigTest.java @@ -0,0 +1,79 @@ +/* + * 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.disttest.controller.config; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.message.CreateConsumerCommand; + +public class ConsumerConfigTest extends TestCase +{ + public void testConsumerHasZeroArgConstructorForGson() + { + ConsumerConfig c = new ConsumerConfig(); + assertNotNull(c); + } + + public void testCreateConsumerCommand() + { + boolean isTopic = true; + boolean isDurableSubscription = true; + boolean isBrowsingSubscription = true; + boolean noLocal = true; + long numberOfMessages = 100; + String consumerName = "consumerName"; + String sessionName = "sessionName"; + String destinationName = "destinationName"; + String selector = "selector"; + int batchSize = 10;; + long maximumDuration = 50; + boolean isSynchronousNonDefault = false; + + ConsumerConfig consumerConfig = new ConsumerConfig( + consumerName, + destinationName, + numberOfMessages, + batchSize, + maximumDuration, + isTopic, + isDurableSubscription, + isBrowsingSubscription, + selector, + noLocal, + isSynchronousNonDefault); + + CreateConsumerCommand createConsumerCommand = consumerConfig.createCommand(sessionName); + + assertEquals(sessionName, createConsumerCommand.getSessionName()); + assertEquals(consumerName, createConsumerCommand.getParticipantName()); + assertEquals(destinationName, createConsumerCommand.getDestinationName()); + assertEquals(numberOfMessages, createConsumerCommand.getNumberOfMessages()); + assertEquals(batchSize, createConsumerCommand.getBatchSize()); + assertEquals(maximumDuration, createConsumerCommand.getMaximumDuration()); + + assertEquals(isTopic, createConsumerCommand.isTopic()); + assertEquals(isDurableSubscription, createConsumerCommand.isDurableSubscription()); + assertEquals(isBrowsingSubscription, createConsumerCommand.isBrowsingSubscription()); + assertEquals(selector, createConsumerCommand.getSelector()); + assertEquals(noLocal, createConsumerCommand.isNoLocal()); + assertEquals(isSynchronousNonDefault, createConsumerCommand.isSynchronous()); + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/IterationValueTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/IterationValueTest.java new file mode 100644 index 0000000000..7998eae37e --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/IterationValueTest.java @@ -0,0 +1,78 @@ +/* + * 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.disttest.controller.config; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +import java.util.Collections; +import java.util.Map; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.message.CreateConsumerCommand; +import org.apache.qpid.disttest.message.CreateProducerCommand; + +public class IterationValueTest extends TestCase +{ + private static final int MESSAGE_SIZE = 10; + + private CreateProducerCommand _createProducerCommand; + private CreateConsumerCommand _createConsumerCommand; + private Map<String, String> _iterationValueMap; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + _createProducerCommand = mock(CreateProducerCommand.class); + _createConsumerCommand = mock(CreateConsumerCommand.class); + + _iterationValueMap = Collections.singletonMap("_messageSize", String.valueOf(MESSAGE_SIZE)); + } + + public void testApplyPopulatedIterationValueToCommandWithMatchingProperties() throws Exception + { + IterationValue iterationValue = new IterationValue(_iterationValueMap); + + iterationValue.applyToCommand(_createProducerCommand); + + verify(_createProducerCommand).setMessageSize(MESSAGE_SIZE); + } + + public void testApplyPopulatedIterationValueToCommandWithoutMatchingProperties() throws Exception + { + IterationValue iterationValue = new IterationValue(_iterationValueMap); + + iterationValue.applyToCommand(_createConsumerCommand); + + verifyZeroInteractions(_createConsumerCommand); + } + + public void testApplyUnpopulatedIterationValueToCommand() throws Exception + { + IterationValue iterationValue = new IterationValue(); + + iterationValue.applyToCommand(_createProducerCommand); + + verifyZeroInteractions(_createProducerCommand); + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/MessageProviderConfigTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/MessageProviderConfigTest.java new file mode 100644 index 0000000000..a3b367a4b4 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/MessageProviderConfigTest.java @@ -0,0 +1,51 @@ +/* + * 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.disttest.controller.config; + +import java.util.HashMap; +import java.util.Map; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.client.property.PropertyValue; +import org.apache.qpid.disttest.client.property.SimplePropertyValue; +import org.apache.qpid.disttest.message.CreateMessageProviderCommand; + +public class MessageProviderConfigTest extends TestCase +{ + public void testCreateCommandsForMessageProvider() + { + Map<String, PropertyValue> messageProperties = new HashMap<String, PropertyValue>(); + messageProperties.put("test", new SimplePropertyValue("testValue")); + MessageProviderConfig config = new MessageProviderConfig("test", messageProperties); + CreateMessageProviderCommand command = config.createCommand(); + assertNotNull("Command should not be null", command); + assertNotNull("Unexpected name", command.getProviderName()); + assertEquals("Unexpected properties", messageProperties, command.getMessageProperties()); + } + + public void testMessageProviderConfig() + { + Map<String, PropertyValue> messageProperties = new HashMap<String, PropertyValue>(); + messageProperties.put("test", new SimplePropertyValue("testValue")); + MessageProviderConfig config = new MessageProviderConfig("test", messageProperties); + assertEquals("Unexpected name", "test", config.getName()); + assertEquals("Unexpected properties", messageProperties, config.getMessageProperties()); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ProducerConfigTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ProducerConfigTest.java new file mode 100644 index 0000000000..b9e591f113 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ProducerConfigTest.java @@ -0,0 +1,89 @@ +/* + * 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.disttest.controller.config; + +import javax.jms.DeliveryMode; +import javax.jms.Message; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.message.CreateProducerCommand; + +public class ProducerConfigTest extends TestCase +{ + public void testProducerHasZeroArgConstructorForGson() + { + ProducerConfig p = new ProducerConfig(); + assertNotNull(p); + } + + public void testConfigProvidesJmsDefaults() + { + CreateProducerCommand p = new ProducerConfig().createCommand("session1"); + assertEquals(Message.DEFAULT_DELIVERY_MODE, p.getDeliveryMode()); + assertEquals(Message.DEFAULT_PRIORITY, p.getPriority()); + assertEquals(Message.DEFAULT_TIME_TO_LIVE, p.getTimeToLive()); + } + + public void testCreateProducerCommand() + { + String destination = "url:/destination"; + int messageSize = 1000; + int numberOfMessages = 10; + int priority = 4; + long timeToLive = 10000; + int batchSize = 5; + long interval = 60; + long maximumDuration = 70; + long startDelay = 80; + String providerName = "testProvider1"; + + ProducerConfig producerConfig = new ProducerConfig( + "producer1", + destination, + numberOfMessages, + batchSize, + maximumDuration, + DeliveryMode.NON_PERSISTENT, + messageSize, + priority, + timeToLive, + interval, + startDelay, + providerName); + + CreateProducerCommand command = producerConfig.createCommand("session1"); + + assertEquals("session1", command.getSessionName()); + assertEquals("producer1", command.getParticipantName()); + assertEquals(destination, command.getDestinationName()); + assertEquals(numberOfMessages, command.getNumberOfMessages()); + assertEquals(batchSize, command.getBatchSize()); + assertEquals(maximumDuration, command.getMaximumDuration()); + + assertEquals(DeliveryMode.NON_PERSISTENT, command.getDeliveryMode()); + assertEquals(messageSize, command.getMessageSize()); + assertEquals(priority, command.getPriority()); + assertEquals(timeToLive, command.getTimeToLive()); + assertEquals(interval, command.getInterval()); + assertEquals(startDelay, command.getStartDelay()); + assertEquals(providerName, command.getMessageProviderName()); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/SessionConfigTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/SessionConfigTest.java new file mode 100644 index 0000000000..8775e4064d --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/SessionConfigTest.java @@ -0,0 +1,93 @@ +/* + * 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.disttest.controller.config; + +import static org.apache.qpid.disttest.controller.config.ConfigTestUtils.assertCommandEquals; +import static org.apache.qpid.disttest.controller.config.ConfigTestUtils.getCommand; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.List; + +import javax.jms.Session; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.CreateConsumerCommand; +import org.apache.qpid.disttest.message.CreateProducerCommand; +import org.apache.qpid.disttest.message.CreateSessionCommand; + +public class SessionConfigTest extends TestCase +{ + private static final String CONNECTION_NAME = "conn1"; + private static final String SESSION = "session1"; + + public void testSessionHasZeroArgConstructorForGson() + { + SessionConfig s = new SessionConfig(); + assertNotNull(s); + } + + public void testCreateCommandsForSessionAndChildren() + { + SessionConfig sessionConfig = createSessionConfigWithChildCommands(); + + List<Command> commands = sessionConfig.createCommands(CONNECTION_NAME); + assertEquals(3, commands.size()); + + assertCommandEquals(commands, 0, CreateSessionCommand.class); + assertCommandEquals(commands, 1, CreateProducerCommand.class); + assertCommandEquals(commands, 2, CreateConsumerCommand.class); + + CreateSessionCommand createSessionCommand = getCommand(commands, 0); + assertEquals(Session.AUTO_ACKNOWLEDGE, createSessionCommand.getAcknowledgeMode()); + assertEquals(SESSION, createSessionCommand.getSessionName()); + assertEquals(CONNECTION_NAME, createSessionCommand.getConnectionName()); + } + + public void testGetTotalNumberOfParticipants() + { + SessionConfig sessionConfig = createSessionConfigWithOneConsumerAndOneProducer(); + assertEquals(2, sessionConfig.getTotalNumberOfParticipants()); + } + + private SessionConfig createSessionConfigWithOneConsumerAndOneProducer() + { + return createSessionConfigWithChildCommands(); + } + + private SessionConfig createSessionConfigWithChildCommands() + { + ProducerConfig producerConfig = mock(ProducerConfig.class); + ConsumerConfig consumerConfig = mock(ConsumerConfig.class); + + when(producerConfig.createCommand(SESSION)).thenReturn(mock(CreateProducerCommand.class)); + when(consumerConfig.createCommand(SESSION)).thenReturn(mock(CreateConsumerCommand.class)); + + return new SessionConfig(SESSION, + Session.AUTO_ACKNOWLEDGE, + Collections.singletonList(consumerConfig), + Collections.singletonList(producerConfig)); + } + + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/TestConfigTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/TestConfigTest.java new file mode 100644 index 0000000000..1212a57606 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/TestConfigTest.java @@ -0,0 +1,113 @@ +/* + * 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.disttest.controller.config; + +import static org.apache.qpid.disttest.controller.config.ConfigTestUtils.assertCommandForClient; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.controller.CommandForClient; +import org.apache.qpid.disttest.message.NoOpCommand; + +public class TestConfigTest extends TestCase +{ + private static final QueueConfig[] EMPTY_QUEUES_ARRAY = new QueueConfig[0]; + private static final String CLIENT1 = "client1"; + private static final String CLIENT2 = "client2"; + private static final String TEST1 = "test1"; + + public void testConfigHasZeroArgConstructorForGson() + { + TestConfig c = new TestConfig(); + assertNotNull(c); + } + + public void testCreateCommandsForClient() + { + TestConfig config = createTestConfigWithClientConfigReturningChildCommands(); + + List<CommandForClient> commandsForClients = config.createCommands(); + assertEquals("Unexpected number of commands for client", 3, commandsForClients.size()); + + assertCommandForClient(commandsForClients, 0, CLIENT1, NoOpCommand.class); + assertCommandForClient(commandsForClients, 1, CLIENT1, NoOpCommand.class); + assertCommandForClient(commandsForClients, 2, CLIENT2, NoOpCommand.class); + } + + public void testGetClientNames() + { + TestConfig config = createTestConfigWithTwoClients(); + + assertEquals(2, config.getClientNames().size()); + } + + public void testGetTotalNumberOfClients() + { + TestConfig config = createTestConfigWithTwoClients(); + assertEquals(2, config.getTotalNumberOfClients()); + } + + public void testGetTotalNumberOfParticipants() + { + TestConfig config = createTestConfigWithTwoClients(); + assertEquals(2, config.getTotalNumberOfParticipants()); + } + + private TestConfig createTestConfigWithClientConfigReturningChildCommands() + { + ClientConfig clientConfig1 = createClientConfigReturningCommands(CLIENT1, 2); + ClientConfig clientConfig2 = createClientConfigReturningCommands(CLIENT2, 1); + + TestConfig config = new TestConfig(TEST1, new ClientConfig[] { clientConfig1, clientConfig2 }, EMPTY_QUEUES_ARRAY); + return config; + } + + private ClientConfig createClientConfigReturningCommands(final String clientName, int numberOfCommands) + { + ClientConfig clientConfig = mock(ClientConfig.class); + + List<CommandForClient> commandList = new ArrayList<CommandForClient>(); + + for (int i = 1 ; i <= numberOfCommands; i++) + { + commandList.add(new CommandForClient(clientName, new NoOpCommand())); + } + + when(clientConfig.createCommands()).thenReturn(commandList); + return clientConfig; + } + + private TestConfig createTestConfigWithTwoClients() + { + ClientConfig clientConfig1 = mock(ClientConfig.class); + ClientConfig clientConfig2 = mock(ClientConfig.class); + + when(clientConfig1.getTotalNumberOfParticipants()).thenReturn(1); + when(clientConfig2.getTotalNumberOfParticipants()).thenReturn(1); + + TestConfig config = new TestConfig(TEST1, new ClientConfig[] { clientConfig1, clientConfig2 }, EMPTY_QUEUES_ARRAY); + return config; + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/TestInstanceTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/TestInstanceTest.java new file mode 100644 index 0000000000..928fbe58cf --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/TestInstanceTest.java @@ -0,0 +1,102 @@ +/* + * 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.disttest.controller.config; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.controller.CommandForClient; +import org.apache.qpid.disttest.message.CreateConsumerCommand; +import org.apache.qpid.disttest.message.CreateProducerCommand; +import org.apache.qpid.disttest.message.NoOpCommand; + +public class TestInstanceTest extends TestCase +{ + private static final String CLIENT_NAME = "CLIENT_NAME"; + private static final int ITERATION_NUMBER = 0; + + private NoOpCommand _noOpCommand; + private CreateProducerCommand _createProducerCommand; + private CreateConsumerCommand _createConsumerCommand; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + _noOpCommand = mock(NoOpCommand.class); + _createProducerCommand = mock(CreateProducerCommand.class); + _createConsumerCommand = mock(CreateConsumerCommand.class); + } + + public void testCreateCommandsWithIterationValues() + { + IterationValue iterationValue = mock(IterationValue.class); + + TestConfig config = createTestConfig(); + + TestInstance testInstance = new TestInstance(config, ITERATION_NUMBER, iterationValue); + + List<CommandForClient> commandsForClients = testInstance.createCommands(); + assertEquals("Unexpected number of commands for client", 3, commandsForClients.size()); + + verify(iterationValue).applyToCommand(_noOpCommand); + verify(iterationValue).applyToCommand(_createProducerCommand); + verify(iterationValue).applyToCommand(_createConsumerCommand); + } + + public void testCreateCommandsWithoutIterationValues() + { + TestConfig config = createTestConfig(); + TestInstance testInstance = new TestInstance(config); + + List<CommandForClient> commandsForClients = testInstance.createCommands(); + assertEquals("Unexpected number of commands for client", 3, commandsForClients.size()); + } + + public void testGetConfiguredClientNames() + { + TestConfig testConfig = mock(TestConfig.class); + when(testConfig.getClientNames()).thenReturn(Collections.singletonList(CLIENT_NAME)); + TestInstance testInstance = new TestInstance(testConfig); + + List<String> clientNames = testInstance.getClientNames(); + assertEquals(1, clientNames.size()); + assertEquals(CLIENT_NAME, clientNames.get(0)); + } + + private TestConfig createTestConfig() + { + CommandForClient commandForClient1 = new CommandForClient(CLIENT_NAME, _noOpCommand); + CommandForClient commandForClient2 = new CommandForClient(CLIENT_NAME, _createProducerCommand); + CommandForClient commandForClient3 = new CommandForClient(CLIENT_NAME, _createConsumerCommand); + + TestConfig config = mock(TestConfig.class); + when(config.createCommands()).thenReturn(Arrays.asList(commandForClient1, commandForClient2, commandForClient3)); + + return config; + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/sampleConfig.json b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/sampleConfig.json new file mode 100644 index 0000000000..9e1168129b --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/sampleConfig.json @@ -0,0 +1,72 @@ +{ + "_tests": [ + { + "_name": "Test 1", + "_queues": [ + { + "_name": "Json-Queue-Name", + "_durable": false, + "_attributes": {} + }, + { + "_name": "Json Queue Name 2", + "_durable": true, + "_attributes": { + "x-qpid-priorities": 10.0 + } + } + ], + "_iterations": [ + { + "_messageSize": 100, + "_numberOfMessages": 10 + }, + { + "_messageSize": 200, + "_numberOfMessages": 5 + } + ], + "_clients": [ + { + "_connections": [ + { + "_name": "connection1", + "_sessions": [] + } + ]; + "_messageProviders":[ + { + "_name": "testProvider1"; + "_messageProperties": { + "priority": {"@def": "list"; "_items": [1,2,3,4,4]}; + "id": {"@def": "random"; "_upper": 10}; + "test": "test-value" + } + } + ] + } + ] + }, + { + "_name": "Test 2", + "_queues": [ + { + "_name": "Json-Queue-Name", + "_durable": false, + "_attributes": {} + } + ], + "_iterations": [], + "_clients": [ + { + "_connections": [ + { + "_name": "connection1", + "_sessions": [] + } + ] + } + ] + } + ] +}
\ No newline at end of file diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/jms/JmsMessageAdaptorTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/jms/JmsMessageAdaptorTest.java new file mode 100644 index 0000000000..ab0f52263b --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/jms/JmsMessageAdaptorTest.java @@ -0,0 +1,40 @@ +/* + * 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.disttest.jms; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.CommandType; + +public class JmsMessageAdaptorTest extends TestCase +{ + + public void testCheckAllCommandTypes() + { + for (CommandType commandType : CommandType.values()) + { + Class<? extends Command> clazz = JmsMessageAdaptor.getCommandClassFromType(commandType); + assertNotNull(clazz); + + } + + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/message/JsonHandlerTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/message/JsonHandlerTest.java new file mode 100644 index 0000000000..4a56fff8fe --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/message/JsonHandlerTest.java @@ -0,0 +1,183 @@ +/* + * 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.disttest.message; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import junit.framework.TestCase; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.qpid.disttest.client.property.ListPropertyValue; +import org.apache.qpid.disttest.client.property.PropertyValue; +import org.apache.qpid.disttest.json.JsonHandler; + +public class JsonHandlerTest extends TestCase +{ + private JsonHandler _jsonHandler = null; + private SendChristmasCards _testCommand = null; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + _jsonHandler = new JsonHandler(); + + _testCommand = new SendChristmasCards(CommandType.START_TEST, Collections.singletonMap(SendChristmasCards.CardType.FUNNY, 5)); + _testCommand.persons = Arrays.asList(new Person("Phil"), new Person("Andrew")); + } + + public void testMarshallUnmarshall() throws Exception + { + final String jsonString = _jsonHandler.marshall(_testCommand); + + final SendChristmasCards unmarshalledCommand = _jsonHandler.unmarshall(jsonString, SendChristmasCards.class); + + assertEquals("Unmarshalled command should be equal to the original object", _testCommand, unmarshalledCommand); + } + + public void testGeneratorDesrialization() + { + String json = "{_messageProperties: {test: 1; generator: {'@def': 'list'; _cyclic: false; _items: ['first', " + + "{'@def': 'range'; _upper:10; '_type':'int'}]}}}"; + final TestCommand unmarshalledCommand = _jsonHandler.unmarshall(json, TestCommand.class); + Map<String, PropertyValue> properties = unmarshalledCommand.getMessageProperties(); + assertNotNull("Properties should not be null", properties); + assertFalse("Properties should not be empty", properties.isEmpty()); + assertEquals("Unexpected properties size", 2, properties.size()); + PropertyValue testProperty = properties.get("test"); + assertNotNull("Unexpected property test", testProperty); + assertTrue("Unexpected property test", testProperty.getValue() instanceof Number); + assertEquals("Unexpected property value", 1, ((Number)testProperty.getValue()).intValue()); + Object generatorObject = properties.get("generator"); + assertTrue("Unexpected generator object", generatorObject instanceof ListPropertyValue); + PropertyValue generator = (PropertyValue)generatorObject; + assertEquals("Unexpected generator value", "first", generator.getValue()); + for (int i = 0; i < 10; i++) + { + assertEquals("Unexpected generator value", new Integer(i), generator.getValue()); + } + String newJson =_jsonHandler.marshall(unmarshalledCommand); + final TestCommand newUnmarshalledCommand = _jsonHandler.unmarshall(newJson, TestCommand.class); + assertEquals("Unmarshalled command should be equal to the original object", unmarshalledCommand, newUnmarshalledCommand); + } + + /** + * A {@link Command} designed to exercise {@link JsonHandler}, e.g does it handle a map of enums?. + * + * This class is non-private to avoid auto-deletion of "unused" fields/methods + */ + static class SendChristmasCards extends Command + { + enum CardType {FUNNY, TRADITIONAL} + + private Map<CardType, Integer> _cardTypes; + private List<Person> persons; + + public SendChristmasCards(final CommandType type, Map<CardType, Integer> cardTypes) + { + super(type); + _cardTypes = cardTypes; + } + + public Map<CardType, Integer> getCardTypes() + { + return _cardTypes; + } + + public List<Person> getPersons() + { + return persons; + } + + @Override + public boolean equals(final Object obj) + { + return EqualsBuilder.reflectionEquals(this, obj); + } + } + + /** + * This class is non-private to avoid auto-deletion of "unused" fields/methods + */ + static class Person + { + private String _firstName; + + public Person(final String firstName) + { + _firstName = firstName; + } + + public String getFirstName() + { + return _firstName; + } + + @Override + public boolean equals(final Object obj) + { + return EqualsBuilder.reflectionEquals(this, obj); + } + + } + + /** + * Yet another test class + */ + static class TestCommand extends Command + { + + private Map<String, PropertyValue> _messageProperties; + + public TestCommand(CommandType type) + { + super(type); + } + + public Map<String, PropertyValue> getMessageProperties() + { + return _messageProperties; + } + + public void setMessageProperties(Map<String, PropertyValue> _messageProperties) + { + this._messageProperties = _messageProperties; + } + + @Override + public boolean equals(final Object obj) + { + if (obj == null || !(obj instanceof TestCommand)) + { + return false; + } + TestCommand other = (TestCommand)obj; + if (_messageProperties == null && other._messageProperties != null ) + { + return false; + } + return _messageProperties.equals(other._messageProperties); + } + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/message/ParticipantResultTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/message/ParticipantResultTest.java new file mode 100644 index 0000000000..12731c06f4 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/message/ParticipantResultTest.java @@ -0,0 +1,162 @@ +/* + * 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.disttest.message; + +import static org.apache.qpid.disttest.message.ParticipantAttribute.*; +import static org.apache.qpid.disttest.message.ParticipantAttribute.CONFIGURED_CLIENT_NAME; +import static org.apache.qpid.disttest.message.ParticipantAttribute.DELIVERY_MODE; +import static org.apache.qpid.disttest.message.ParticipantAttribute.ERROR_MESSAGE; +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_BROWSIING_SUBSCRIPTION; +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_DURABLE_SUBSCRIPTION; +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_NO_LOCAL; +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_SELECTOR; +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_SYNCHRONOUS_CONSUMER; +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_TOPIC; +import static org.apache.qpid.disttest.message.ParticipantAttribute.ITERATION_NUMBER; +import static org.apache.qpid.disttest.message.ParticipantAttribute.MAXIMUM_DURATION; +import static org.apache.qpid.disttest.message.ParticipantAttribute.PAYLOAD_SIZE; +import static org.apache.qpid.disttest.message.ParticipantAttribute.NUMBER_OF_MESSAGES_PROCESSED; +import static org.apache.qpid.disttest.message.ParticipantAttribute.PARTICIPANT_NAME; +import static org.apache.qpid.disttest.message.ParticipantAttribute.PRIORITY; +import static org.apache.qpid.disttest.message.ParticipantAttribute.PRODUCER_INTERVAL; +import static org.apache.qpid.disttest.message.ParticipantAttribute.PRODUCER_START_DELAY; +import static org.apache.qpid.disttest.message.ParticipantAttribute.TEST_NAME; +import static org.apache.qpid.disttest.message.ParticipantAttribute.TIME_TAKEN; +import static org.apache.qpid.disttest.message.ParticipantAttribute.TIME_TO_LIVE; + +import java.util.Date; + +import javax.jms.DeliveryMode; + +import junit.framework.TestCase; + +public class ParticipantResultTest extends TestCase +{ + + public void testSharedParticipantResultAttributes() throws Exception + { + final String participantName = "PARTICIPANT_NAME1"; + final String testName = "TEST_NAME1"; + String clientConfiguredName = "CLIENT_CONFIGURED_NAME"; + String errorMessage = "errorMessage"; + int iterationNumber = 1; + + ParticipantResult result = new ParticipantResult(); + + long numberOfMessages = 500; + long timeTaken = 30; + int batchSize = 10; + + long startTime = System.currentTimeMillis(); + long endTime = startTime + timeTaken; + long maximumDuration = 1000; + + int totalNumberOfConsumers = 1; + int totalNumberOfProducers = 1; + + int acknowledgeMode = 1; + + result.setParticipantName(participantName); + result.setTestName(testName); + result.setIterationNumber(iterationNumber); + result.setConfiguredClientName(clientConfiguredName); + + result.setAcknowledgeMode(acknowledgeMode); + result.setNumberOfMessagesProcessed(numberOfMessages); + result.setConfiguredClientName(clientConfiguredName); + result.setBatchSize(batchSize); + + result.setStartDate(new Date(startTime)); + result.setEndDate(new Date(endTime)); + result.setMaximumDuration(maximumDuration); + + result.setTotalNumberOfConsumers(totalNumberOfConsumers); + result.setTotalNumberOfProducers(totalNumberOfProducers); + + result.setErrorMessage(errorMessage); + + assertEquals(participantName, result.getAttributes().get(PARTICIPANT_NAME)); + assertEquals(testName, result.getAttributes().get(TEST_NAME)); + assertEquals(iterationNumber, result.getAttributes().get(ITERATION_NUMBER)); + assertEquals(clientConfiguredName, result.getAttributes().get(CONFIGURED_CLIENT_NAME)); + assertEquals(numberOfMessages, result.getAttributes().get(NUMBER_OF_MESSAGES_PROCESSED)); + assertEquals(timeTaken, result.getAttributes().get(TIME_TAKEN)); + assertEquals(timeTaken, result.getAttributes().get(TIME_TAKEN)); + assertEquals(timeTaken, result.getAttributes().get(TIME_TAKEN)); + assertEquals(batchSize, result.getAttributes().get(BATCH_SIZE)); + assertEquals(maximumDuration, result.getAttributes().get(MAXIMUM_DURATION)); + assertEquals(totalNumberOfConsumers, result.getAttributes().get(TOTAL_NUMBER_OF_CONSUMERS)); + assertEquals(totalNumberOfProducers, result.getAttributes().get(TOTAL_NUMBER_OF_PRODUCERS)); + assertEquals(acknowledgeMode, result.getAttributes().get(ACKNOWLEDGE_MODE)); + assertEquals(errorMessage, result.getAttributes().get(ERROR_MESSAGE)); + } + + public void testConsumerParticipantResultAttributes() throws Exception + { + ConsumerParticipantResult result = new ConsumerParticipantResult(); + + boolean topic = true; + boolean durable = true; + boolean browsingSubscription = false; + boolean selector = true; + boolean noLocal = false; + boolean synchronousConsumer = true; + + result.setTopic(topic); + result.setDurableSubscription(durable); + result.setBrowsingSubscription(browsingSubscription); + result.setSelector(selector); + result.setNoLocal(noLocal); + result.setSynchronousConsumer(synchronousConsumer); + + assertEquals(topic, result.getAttributes().get(IS_TOPIC)); + assertEquals(durable, result.getAttributes().get(IS_DURABLE_SUBSCRIPTION)); + assertEquals(browsingSubscription, result.getAttributes().get(IS_BROWSIING_SUBSCRIPTION)); + assertEquals(selector, result.getAttributes().get(IS_SELECTOR)); + assertEquals(noLocal, result.getAttributes().get(IS_NO_LOCAL)); + assertEquals(synchronousConsumer, result.getAttributes().get(IS_SYNCHRONOUS_CONSUMER)); + } + + public void testProducerParticipantResultAttributes() throws Exception + { + ProducerParticipantResult result = new ProducerParticipantResult(); + + int priority = 2; + long timeToLive = 30; + long producerStartDelay = 40; + long producerInterval = 50; + int messageSize = 60; + int deliveryMode = DeliveryMode.PERSISTENT; + + result.setPriority(priority); + result.setTimeToLive(timeToLive); + result.setStartDelay(producerStartDelay); + result.setInterval(producerInterval); + result.setPayloadSize(messageSize); + result.setDeliveryMode(deliveryMode); + + + assertEquals(priority, result.getAttributes().get(PRIORITY)); + assertEquals(timeToLive, result.getAttributes().get(TIME_TO_LIVE)); + assertEquals(producerStartDelay, result.getAttributes().get(PRODUCER_START_DELAY)); + assertEquals(producerInterval, result.getAttributes().get(PRODUCER_INTERVAL)); + assertEquals(messageSize, result.getAttributes().get(PAYLOAD_SIZE)); + assertEquals(deliveryMode, result.getAttributes().get(DELIVERY_MODE)); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/results/aggregation/AggregatorTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/results/aggregation/AggregatorTest.java new file mode 100644 index 0000000000..393837b4d5 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/results/aggregation/AggregatorTest.java @@ -0,0 +1,61 @@ +/* + * 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.disttest.results.aggregation; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.controller.ResultsForAllTests; + +public class AggregatorTest extends TestCase +{ + private Aggregator _aggregator = new Aggregator(); + private TestResultAggregator _testResultAggregator = mock(TestResultAggregator.class); + + @Override + protected void setUp() throws Exception + { + super.setUp(); + _aggregator.setTestResultAggregator(_testResultAggregator); + } + + public void testAggregrateManyTestResults() throws Exception + { + ResultsForAllTests resultsForAllTests = mock(ResultsForAllTests.class); + ITestResult testResult1 = mock(ITestResult.class); + ITestResult testResult2 = mock(ITestResult.class); + + when(resultsForAllTests.getTestResults()).thenReturn(Arrays.asList(testResult1, testResult2)); + when(_testResultAggregator.aggregateTestResult(testResult1)).thenReturn(mock(AggregatedTestResult.class)); + when(_testResultAggregator.aggregateTestResult(testResult2)).thenReturn(mock(AggregatedTestResult.class)); + + ResultsForAllTests aggregatedResultsForAllTests = _aggregator.aggregateResults(resultsForAllTests); + assertEquals(2, aggregatedResultsForAllTests.getTestResults().size()); + + verify(_testResultAggregator).aggregateTestResult(testResult1); + verify(_testResultAggregator).aggregateTestResult(testResult2); + + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/results/aggregation/ParticipantResultAggregatorTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/results/aggregation/ParticipantResultAggregatorTest.java new file mode 100644 index 0000000000..72743be1d1 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/results/aggregation/ParticipantResultAggregatorTest.java @@ -0,0 +1,272 @@ +/* + * 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.disttest.results.aggregation; + +import java.util.Date; + +import javax.jms.Session; + +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.disttest.results.aggregation.ParticipantResultAggregator; + +import junit.framework.TestCase; + +public class ParticipantResultAggregatorTest extends TestCase +{ + private ParticipantResultAggregator _aggregator = new ParticipantResultAggregator(ParticipantResult.class, AGGREGATED_RESULT_NAME); + + private static final String TEST_NAME = "TEST_NAME"; + private static final String AGGREGATED_RESULT_NAME = "AGGREGATED_RESULT_NAME"; + private static final int TEST_ITERATION_NUMBER = 1; + + private static final long PARTICIPANT1_STARTDATE = 50; + private static final long PARTICIPANT1_ENDDATE = 20000; + private static final long PARTICIPANT1_TOTAL_PROCESSED = 1024; + + private static final long PARTICIPANT2_STARTDATE = 100; + private static final long PARTICIPANT2_ENDDATE = 21000; + private static final long PARTICIPANT2_TOTAL_PROCESSED = 2048; + + private static final long OVERALL_PROCESSED = PARTICIPANT1_TOTAL_PROCESSED + PARTICIPANT2_TOTAL_PROCESSED; + private static final double OVERALL_TIMETAKEN = PARTICIPANT2_ENDDATE - PARTICIPANT1_STARTDATE; + + private static final double EXPECTED_AGGREGATED_ALL_THROUGHPUT = ((OVERALL_PROCESSED)/1024)/((OVERALL_TIMETAKEN)/1000); + + public void testStartAndEndDateForOneParticipantResult() + { + ParticipantResult result = new ParticipantResult(); + result.setStartDate(new Date(PARTICIPANT1_STARTDATE)); + result.setEndDate(new Date(PARTICIPANT1_ENDDATE)); + + _aggregator.aggregate(result); + ParticipantResult aggregratedResult = _aggregator.getAggregatedResult(); + assertEquals(PARTICIPANT1_STARTDATE, aggregratedResult.getStartInMillis()); + assertEquals(PARTICIPANT1_ENDDATE, aggregratedResult.getEndInMillis()); + } + + public void testStartAndEndDateForTwoParticipantResults() + { + ParticipantResult result1 = new ParticipantResult(); + result1.setStartDate(new Date(PARTICIPANT1_STARTDATE)); + result1.setEndDate(new Date(PARTICIPANT1_ENDDATE)); + + ParticipantResult result2 = new ParticipantResult(); + result2.setStartDate(new Date(PARTICIPANT2_STARTDATE)); + result2.setEndDate(new Date(PARTICIPANT2_ENDDATE)); + + _aggregator.aggregate(result1); + _aggregator.aggregate(result2); + + ParticipantResult aggregratedResult = _aggregator.getAggregatedResult(); + assertEquals(PARTICIPANT1_STARTDATE, aggregratedResult.getStartInMillis()); + assertEquals(PARTICIPANT2_ENDDATE, aggregratedResult.getEndInMillis()); + } + + public void testComputeNumberOfMessagesProcessed() + { + ParticipantResult result1 = new ParticipantResult(); + result1.setNumberOfMessagesProcessed(10); + + ParticipantResult result2 = new ParticipantResult(); + result2.setNumberOfMessagesProcessed(15); + + _aggregator.aggregate(result1); + _aggregator.aggregate(result2); + + ParticipantResult aggregratedResult = _aggregator.getAggregatedResult(); + assertEquals(25, aggregratedResult.getNumberOfMessagesProcessed()); + } + + public void testComputeTotalPayloadProcessed() + { + ParticipantResult result1 = new ParticipantResult(); + result1.setTotalPayloadProcessed(PARTICIPANT1_TOTAL_PROCESSED); + + ParticipantResult result2 = new ParticipantResult(); + result2.setTotalPayloadProcessed(PARTICIPANT2_TOTAL_PROCESSED); + + _aggregator.aggregate(result1); + _aggregator.aggregate(result2); + + ParticipantResult aggregratedResult = _aggregator.getAggregatedResult(); + assertEquals(OVERALL_PROCESSED, aggregratedResult.getTotalPayloadProcessed()); + } + + public void testComputeThroughput() + { + ParticipantResult result1 = new ParticipantResult(); + result1.setStartDate(new Date(PARTICIPANT1_STARTDATE)); + result1.setEndDate(new Date(PARTICIPANT1_ENDDATE)); + result1.setTotalPayloadProcessed(PARTICIPANT1_TOTAL_PROCESSED); + + ParticipantResult result2 = new ParticipantResult(); + result2.setStartDate(new Date(PARTICIPANT2_STARTDATE)); + result2.setEndDate(new Date(PARTICIPANT2_ENDDATE)); + result2.setTotalPayloadProcessed(PARTICIPANT2_TOTAL_PROCESSED); + + _aggregator.aggregate(result1); + _aggregator.aggregate(result2); + + ParticipantResult aggregratedResult = _aggregator.getAggregatedResult(); + assertEquals(EXPECTED_AGGREGATED_ALL_THROUGHPUT, aggregratedResult.getThroughput(), 0.1); + } + + public void testConstantTestNameAndIterationNumberRolledUp() throws Exception + { + ParticipantResult result1 = new ParticipantResult(); + result1.setTestName(TEST_NAME); + result1.setIterationNumber(TEST_ITERATION_NUMBER); + + ParticipantResult result2 = new ParticipantResult(); + result2.setTestName(TEST_NAME); + result2.setIterationNumber(TEST_ITERATION_NUMBER); + + _aggregator.aggregate(result1); + _aggregator.aggregate(result2); + + ParticipantResult aggregratedResult = _aggregator.getAggregatedResult(); + assertEquals(TEST_ITERATION_NUMBER, aggregratedResult.getIterationNumber()); + assertEquals(TEST_NAME, aggregratedResult.getTestName()); + } + + public void testConstantPayloadSizesRolledUp() throws Exception + { + final int payloadSize = 1024; + + ParticipantResult result1 = new ParticipantResult(); + result1.setPayloadSize(payloadSize); + + ParticipantResult result2 = new ParticipantResult(); + result2.setPayloadSize(payloadSize); + + _aggregator.aggregate(result1); + _aggregator.aggregate(result2); + + ParticipantResult aggregratedResult = _aggregator.getAggregatedResult(); + assertEquals(payloadSize, aggregratedResult.getPayloadSize()); + } + + public void testDifferingPayloadSizesNotRolledUp() throws Exception + { + final int payload1Size = 1024; + final int payload2Size = 2048; + + ParticipantResult result1 = new ParticipantResult(); + result1.setPayloadSize(payload1Size); + + ParticipantResult result2 = new ParticipantResult(); + result2.setPayloadSize(payload2Size); + + _aggregator.aggregate(result1); + _aggregator.aggregate(result2); + + ParticipantResult aggregratedResult = _aggregator.getAggregatedResult(); + assertEquals(0, aggregratedResult.getPayloadSize()); + } + + public void testConstantBatchSizesRolledUp() throws Exception + { + final int batchSize = 10; + + ParticipantResult result1 = new ParticipantResult(); + result1.setBatchSize(batchSize); + + ParticipantResult result2 = new ParticipantResult(); + result2.setBatchSize(batchSize); + + _aggregator.aggregate(result1); + _aggregator.aggregate(result2); + + ParticipantResult aggregratedResult = _aggregator.getAggregatedResult(); + assertEquals(batchSize, aggregratedResult.getBatchSize()); + } + + public void testDifferingBatchSizesNotRolledUp() throws Exception + { + final int batch1Size = 10; + final int batch2Size = 20; + + ParticipantResult result1 = new ParticipantResult(); + result1.setBatchSize(batch1Size); + + ParticipantResult result2 = new ParticipantResult(); + result2.setBatchSize(batch2Size); + + _aggregator.aggregate(result1); + _aggregator.aggregate(result2); + + ParticipantResult aggregratedResult = _aggregator.getAggregatedResult(); + assertEquals(0, aggregratedResult.getBatchSize()); + } + + public void testConstantAcknowledgeModesRolledUp() throws Exception + { + ParticipantResult result1 = new ParticipantResult(); + result1.setAcknowledgeMode(Session.DUPS_OK_ACKNOWLEDGE); + + ParticipantResult result2 = new ParticipantResult(); + result2.setAcknowledgeMode(Session.DUPS_OK_ACKNOWLEDGE); + + _aggregator.aggregate(result1); + _aggregator.aggregate(result2); + + ParticipantResult aggregratedResult = _aggregator.getAggregatedResult(); + assertEquals(Session.DUPS_OK_ACKNOWLEDGE, aggregratedResult.getAcknowledgeMode()); + } + + public void testDifferingAcknowledgeModesNotRolledUp() throws Exception + { + ParticipantResult result1 = new ParticipantResult(); + result1.setBatchSize(Session.AUTO_ACKNOWLEDGE); + + ParticipantResult result2 = new ParticipantResult(); + result2.setBatchSize(Session.SESSION_TRANSACTED); + + _aggregator.aggregate(result1); + _aggregator.aggregate(result2); + + ParticipantResult aggregratedResult = _aggregator.getAggregatedResult(); + assertEquals(-1, aggregratedResult.getAcknowledgeMode()); + } + + public void testSumNumberOfConsumerAndProducers() throws Exception + { + final int expectedNumberOfProducers = 1; + final int expectedNumberOfConsumers = 2; + + ParticipantResult result1 = new ParticipantResult(); + result1.setTotalNumberOfConsumers(1); + + ParticipantResult result2 = new ParticipantResult(); + result2.setTotalNumberOfConsumers(1); + + ParticipantResult result3 = new ParticipantResult(); + result2.setTotalNumberOfProducers(1); + + _aggregator.aggregate(result1); + _aggregator.aggregate(result2); + _aggregator.aggregate(result3); + + ParticipantResult aggregratedResult = _aggregator.getAggregatedResult(); + assertEquals(expectedNumberOfConsumers, aggregratedResult.getTotalNumberOfConsumers()); + assertEquals(expectedNumberOfProducers, aggregratedResult.getTotalNumberOfProducers()); + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/results/aggregation/TestResultAggregatorTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/results/aggregation/TestResultAggregatorTest.java new file mode 100644 index 0000000000..a803120cc6 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/results/aggregation/TestResultAggregatorTest.java @@ -0,0 +1,152 @@ +/* + * 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.disttest.results.aggregation; + +import java.util.Date; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.controller.TestResult; +import org.apache.qpid.disttest.message.ConsumerParticipantResult; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.disttest.message.ProducerParticipantResult; +import org.apache.qpid.disttest.results.aggregation.AggregatedTestResult; +import org.apache.qpid.disttest.results.aggregation.TestResultAggregator; + +public class TestResultAggregatorTest extends TestCase +{ + + private static final String TEST1_NAME = "TEST1_NAME"; + private static final int TEST1_ITERATION_NUMBER = 1; + + + private static final String CONSUMER_PARTICIPANT_NAME1 = "CONSUMER_PARTICIPANT_NAME1"; + private static final String CONSUMER_PARTICIPANT_NAME2 = "CONSUMER_PARTICIPANT_NAME2"; + + private static final String PRODUCER_PARTICIPANT_NAME = "PRODUCER_PARTICIPANT_NAME"; + + + private static final long CONSUMER1_STARTDATE = 50; + private static final long CONSUMER1_ENDDATE = 20000; + + private static final long CONSUMER2_STARTDATE = 100; + private static final long CONSUMER2_ENDDATE = 21000; + + private static final long PRODUCER_STARTDATE = 0; + private static final long PRODUCER_ENDDATE = 19000; + + private static final long NUMBER_OF_MESSAGES_PROCESSED_PER_CONSUMER = 50; + private static final long NUMBER_OF_MESSAGES_CONSUMED_IN_TOTAL = NUMBER_OF_MESSAGES_PROCESSED_PER_CONSUMER * 2; + private static final long NUMBER_OF_MESSAGES_PRODUCED = NUMBER_OF_MESSAGES_PROCESSED_PER_CONSUMER * 2; + + private static final int PAYLOAD_SIZE = 1024; + private static final long TOTAL_PAYLOAD_PROCESSED_PER_CONSUMER = NUMBER_OF_MESSAGES_PROCESSED_PER_CONSUMER * PAYLOAD_SIZE; + private static final long TOTAL_PAYLOAD_PRODUCED_IN_TOTAL = TOTAL_PAYLOAD_PROCESSED_PER_CONSUMER * 2; + + private static final int EXPECTED_NUMBER_OF_AGGREGATED_RESULTS = 3; + + private static final int BATCH_SIZE = 3; + + private TestResultAggregator _aggregator = new TestResultAggregator(); + + public void testAggregateResultsForTwoConsumerAndOneProducer() throws Exception + { + TestResult originalTestResult = createResultsFromTest(); + + int numberOfOriginalParticipantResults = originalTestResult.getParticipantResults().size(); + int expectedNumberOfResults = numberOfOriginalParticipantResults + EXPECTED_NUMBER_OF_AGGREGATED_RESULTS; + + AggregatedTestResult aggregatedTestResult = _aggregator.aggregateTestResult(originalTestResult); + + aggregatedTestResult.getAllConsumerParticipantResult().getTotalPayloadProcessed(); + assertEquals(expectedNumberOfResults, aggregatedTestResult.getParticipantResults().size()); + + assertMinimalAggregatedResults( + aggregatedTestResult.getAllConsumerParticipantResult(), + TEST1_NAME, TEST1_ITERATION_NUMBER, + BATCH_SIZE, NUMBER_OF_MESSAGES_CONSUMED_IN_TOTAL, 2, 0); + + assertMinimalAggregatedResults( + aggregatedTestResult.getAllProducerParticipantResult(), + TEST1_NAME, TEST1_ITERATION_NUMBER, + BATCH_SIZE, NUMBER_OF_MESSAGES_PRODUCED, 0, 1); + + assertMinimalAggregatedResults( + aggregatedTestResult.getAllParticipantResult(), + TEST1_NAME, TEST1_ITERATION_NUMBER, + BATCH_SIZE, NUMBER_OF_MESSAGES_CONSUMED_IN_TOTAL, 2, 1); + } + + public void testAggregateResultsWhenParticipantErrored() + { + ParticipantResult failedParticipantResult = new ParticipantResult(); + failedParticipantResult.setParticipantName(PRODUCER_PARTICIPANT_NAME); + failedParticipantResult.setErrorMessage("error"); + TestResult result = new TestResult(TEST1_NAME); + result.addParticipantResult(failedParticipantResult); + + AggregatedTestResult aggregatedTestResult = _aggregator.aggregateTestResult(result); + assertEquals(TestResultAggregator.AGGREGATED_ERROR_MESSAGE, aggregatedTestResult.getAllParticipantResult().getErrorMessage()); + } + + private void assertMinimalAggregatedResults(ParticipantResult result, String expectedTestName, int expectedIterationNumber, int expectedBatchSize, long expectedNumberOfMessagesProcessed, int expectedTotalNumberOfConsumers, int expectedTotalNumberOfProducers) + { + assertEquals("Unexpected test name in " + result.getParticipantName(), expectedTestName, result.getTestName()); + assertEquals("Unexpected iteration number in " + result.getParticipantName(), expectedIterationNumber, result.getIterationNumber()); + assertEquals("Unexpected batch size " + result.getParticipantName(), expectedBatchSize, result.getBatchSize()); + assertEquals("Unexpected number of messages processed in " + result.getParticipantName(), expectedNumberOfMessagesProcessed, result.getNumberOfMessagesProcessed()); + assertEquals("Unexpected total number of consumers " + result.getParticipantName(), expectedTotalNumberOfConsumers, result.getTotalNumberOfConsumers()); + assertEquals("Unexpected total number of producers " + result.getParticipantName(), expectedTotalNumberOfProducers, result.getTotalNumberOfProducers()); + } + + private TestResult createResultsFromTest() + { + TestResult testResult = new TestResult(TEST1_NAME); + + ConsumerParticipantResult consumerResult1 = new ConsumerParticipantResult(); + setPropertiesOn(consumerResult1, TEST1_NAME, TEST1_ITERATION_NUMBER, CONSUMER_PARTICIPANT_NAME1, NUMBER_OF_MESSAGES_PROCESSED_PER_CONSUMER, BATCH_SIZE, PAYLOAD_SIZE, TOTAL_PAYLOAD_PROCESSED_PER_CONSUMER, CONSUMER1_STARTDATE, CONSUMER1_ENDDATE, 1, 0); + testResult.addParticipantResult(consumerResult1); + + ConsumerParticipantResult consumerResult2 = new ConsumerParticipantResult(); + setPropertiesOn(consumerResult2, TEST1_NAME, TEST1_ITERATION_NUMBER, CONSUMER_PARTICIPANT_NAME2, NUMBER_OF_MESSAGES_PROCESSED_PER_CONSUMER, BATCH_SIZE, PAYLOAD_SIZE, TOTAL_PAYLOAD_PROCESSED_PER_CONSUMER, CONSUMER2_STARTDATE, CONSUMER2_ENDDATE, 1, 0); + testResult.addParticipantResult(consumerResult2); + + ParticipantResult producerResult = new ProducerParticipantResult(); + setPropertiesOn(producerResult, TEST1_NAME, TEST1_ITERATION_NUMBER, PRODUCER_PARTICIPANT_NAME, NUMBER_OF_MESSAGES_PRODUCED, BATCH_SIZE, PAYLOAD_SIZE, TOTAL_PAYLOAD_PRODUCED_IN_TOTAL, PRODUCER_STARTDATE, PRODUCER_ENDDATE, 0, 1); + testResult.addParticipantResult(producerResult); + + return testResult; + } + + private void setPropertiesOn(ParticipantResult participantResult, String testName, int iterationNumber, String participantName, long numberOfMessagesProcessed, int batchSize, int payloadSize, long totalPayloadProcessed, long start, long end, int totalNumberOfConsumers, int totalNumberOfProducers) + { + participantResult.setParticipantName(participantName); + participantResult.setTestName(testName); + participantResult.setIterationNumber(iterationNumber); + participantResult.setTotalNumberOfConsumers(totalNumberOfConsumers); + participantResult.setTotalNumberOfProducers(totalNumberOfProducers); + + participantResult.setNumberOfMessagesProcessed(numberOfMessagesProcessed); + participantResult.setPayloadSize(payloadSize); + participantResult.setTotalPayloadProcessed(totalPayloadProcessed); + participantResult.setStartDate(new Date(start)); + participantResult.setEndDate(new Date(end)); + participantResult.setBatchSize(batchSize); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/results/formatting/CSVFormaterTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/results/formatting/CSVFormaterTest.java new file mode 100644 index 0000000000..088746d8cd --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/results/formatting/CSVFormaterTest.java @@ -0,0 +1,144 @@ +/* + * 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.disttest.results.formatting; + +import static org.apache.qpid.disttest.message.ParticipantAttribute.BATCH_SIZE; +import static org.apache.qpid.disttest.message.ParticipantAttribute.CONFIGURED_CLIENT_NAME; +import static org.apache.qpid.disttest.message.ParticipantAttribute.*; +import static org.apache.qpid.disttest.message.ParticipantAttribute.ERROR_MESSAGE; +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_BROWSIING_SUBSCRIPTION; +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_DURABLE_SUBSCRIPTION; +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_NO_LOCAL; +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_SELECTOR; +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_SYNCHRONOUS_CONSUMER; +import static org.apache.qpid.disttest.message.ParticipantAttribute.IS_TOPIC; +import static org.apache.qpid.disttest.message.ParticipantAttribute.ITERATION_NUMBER; +import static org.apache.qpid.disttest.message.ParticipantAttribute.MAXIMUM_DURATION; +import static org.apache.qpid.disttest.message.ParticipantAttribute.NUMBER_OF_MESSAGES_PROCESSED; +import static org.apache.qpid.disttest.message.ParticipantAttribute.PARTICIPANT_NAME; +import static org.apache.qpid.disttest.message.ParticipantAttribute.PAYLOAD_SIZE; +import static org.apache.qpid.disttest.message.ParticipantAttribute.PRIORITY; +import static org.apache.qpid.disttest.message.ParticipantAttribute.PRODUCER_INTERVAL; +import static org.apache.qpid.disttest.message.ParticipantAttribute.PRODUCER_START_DELAY; +import static org.apache.qpid.disttest.message.ParticipantAttribute.TEST_NAME; +import static org.apache.qpid.disttest.message.ParticipantAttribute.THROUGHPUT; +import static org.apache.qpid.disttest.message.ParticipantAttribute.TIME_TAKEN; +import static org.apache.qpid.disttest.message.ParticipantAttribute.TIME_TO_LIVE; +import static org.apache.qpid.disttest.message.ParticipantAttribute.TOTAL_NUMBER_OF_CONSUMERS; +import static org.apache.qpid.disttest.message.ParticipantAttribute.TOTAL_NUMBER_OF_PRODUCERS; +import static org.apache.qpid.disttest.message.ParticipantAttribute.TOTAL_PAYLOAD_PROCESSED; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.controller.ResultsForAllTests; +import org.apache.qpid.disttest.controller.TestResult; +import org.apache.qpid.disttest.message.ParticipantAttribute; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.qmf.QMFProperty.AccessCode; + +public class CSVFormaterTest extends TestCase +{ + private static final String TEST1 = "TEST1"; + private static final String PARTICIPANT = "PARTICIPANT"; + private static final String CONFIGURED_CLIENT1 = "CONFIGURED_CLIENT1"; + + private CSVFormater _formatter = new CSVFormater(); + + public void testResultsFileWithWithOneRow() throws Exception + { + ParticipantResult participantResult = mock(ParticipantResult.class); + Map<ParticipantAttribute, Object> participantAttributes = getParticipantAttributes(); + + when(participantResult.getAttributes()).thenReturn(participantAttributes); + when(participantResult.getParticipantName()).thenReturn(PARTICIPANT); + + TestResult testResult = new TestResult(TEST1); + testResult.addParticipantResult(participantResult); + + ResultsForAllTests resultsForAllTests = new ResultsForAllTests(); + resultsForAllTests.add(testResult); + + String output = _formatter.format(resultsForAllTests); + + String expectedOutput = readCsvOutputFileAsString("expectedOutput.csv"); + + assertEquals(expectedOutput, output); + } + + private Map<ParticipantAttribute, Object> getParticipantAttributes() + { + Map<ParticipantAttribute, Object> participantAttributes = new HashMap<ParticipantAttribute, Object>(); + + participantAttributes.put(TEST_NAME, TEST1); + participantAttributes.put(ITERATION_NUMBER, 0); + participantAttributes.put(CONFIGURED_CLIENT_NAME, CONFIGURED_CLIENT1); + participantAttributes.put(PARTICIPANT_NAME, PARTICIPANT); + participantAttributes.put(NUMBER_OF_MESSAGES_PROCESSED, 0); + participantAttributes.put(PAYLOAD_SIZE, 1); + participantAttributes.put(PRIORITY, 2); + participantAttributes.put(TIME_TO_LIVE, 3); + participantAttributes.put(ACKNOWLEDGE_MODE, 4); + participantAttributes.put(DELIVERY_MODE, 5); + participantAttributes.put(BATCH_SIZE, 6); + participantAttributes.put(MAXIMUM_DURATION, 7); + participantAttributes.put(PRODUCER_START_DELAY, 8); + participantAttributes.put(PRODUCER_INTERVAL, 9); + participantAttributes.put(IS_TOPIC, true); + participantAttributes.put(IS_DURABLE_SUBSCRIPTION, false); + participantAttributes.put(IS_BROWSIING_SUBSCRIPTION, true); + participantAttributes.put(IS_SELECTOR, false); + participantAttributes.put(IS_NO_LOCAL, true); + participantAttributes.put(IS_SYNCHRONOUS_CONSUMER, false); + participantAttributes.put(TOTAL_NUMBER_OF_CONSUMERS, 1); + participantAttributes.put(TOTAL_NUMBER_OF_PRODUCERS, 2); + participantAttributes.put(TOTAL_PAYLOAD_PROCESSED, 1024); + participantAttributes.put(THROUGHPUT, 2048); + participantAttributes.put(TIME_TAKEN, 1000); + participantAttributes.put(ERROR_MESSAGE, "error"); + + return participantAttributes; + } + + private String readCsvOutputFileAsString(String filename) throws Exception + { + InputStream is = getClass().getResourceAsStream(filename); + assertNotNull(is); + + StringBuilder output = new StringBuilder(); + + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String line = null; + while((line = br.readLine()) != null) + { + output.append(line); + output.append("\n"); + } + + return output.toString(); + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/results/formatting/CSVOrderParticipantResultComparatorTest.java b/java/perftests/src/test/java/org/apache/qpid/disttest/results/formatting/CSVOrderParticipantResultComparatorTest.java new file mode 100644 index 0000000000..6cec4b5245 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/results/formatting/CSVOrderParticipantResultComparatorTest.java @@ -0,0 +1,89 @@ +/* + * 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.disttest.results.formatting; + + +import junit.framework.TestCase; + +import org.apache.qpid.disttest.message.ConsumerParticipantResult; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.disttest.message.ProducerParticipantResult; +import org.apache.qpid.disttest.results.aggregation.TestResultAggregator; +import org.apache.qpid.disttest.results.formatting.CSVOrderParticipantResultComparator; + +public class CSVOrderParticipantResultComparatorTest extends TestCase +{ + CSVOrderParticipantResultComparator _comparator = new CSVOrderParticipantResultComparator(); + + public void testOrderedConsumerParticipants() throws Exception + { + assertCompare( + new ConsumerParticipantResult("apple"), + new ConsumerParticipantResult("banana")); + + } + public void testProducerPrecedesConsumerParticipants() throws Exception + { + assertCompare( + new ProducerParticipantResult(), + new ConsumerParticipantResult()); + } + + public void testProducerPrecedesAllProducersResult() + { + assertCompare( + new ProducerParticipantResult("participantName"), + new ParticipantResult(TestResultAggregator.ALL_PRODUCER_PARTICIPANTS_NAME)); + } + + public void testConsumerPrecedesAllConsumersResult() + { + assertCompare( + new ConsumerParticipantResult("participantName"), + new ParticipantResult(TestResultAggregator.ALL_CONSUMER_PARTICIPANTS_NAME)); + } + + public void testAllParticipantsPrecedesAllConsumersResult() + { + assertCompare( + new ParticipantResult(TestResultAggregator.ALL_PARTICIPANTS_NAME), + new ParticipantResult(TestResultAggregator.ALL_CONSUMER_PARTICIPANTS_NAME)); + } + + public void testAllParticipantsPrecedesAllProducersResult() + { + assertCompare( + new ParticipantResult(TestResultAggregator.ALL_PARTICIPANTS_NAME), + new ParticipantResult(TestResultAggregator.ALL_PRODUCER_PARTICIPANTS_NAME)); + } + + private void assertCompare(ParticipantResult smaller, ParticipantResult bigger) + { + assertEquals("Expected " + smaller + " to 'equal' itself", + 0, + _comparator.compare(smaller, smaller)); + + String failureMsg = "Expected " + smaller + " to be smaller than " + bigger; + + assertTrue(failureMsg, _comparator.compare(smaller, bigger) < 0); + assertTrue(failureMsg, _comparator.compare(bigger, smaller) > 0); + } + +} +// <ParticipantResult> diff --git a/java/perftests/src/test/java/org/apache/qpid/disttest/results/formatting/expectedOutput.csv b/java/perftests/src/test/java/org/apache/qpid/disttest/results/formatting/expectedOutput.csv new file mode 100644 index 0000000000..cfffb1e549 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/disttest/results/formatting/expectedOutput.csv @@ -0,0 +1,2 @@ +testName,iterationNumber,clientName,participantName,numberOfMessages,payloadSizeB,priority,timeToLiveMs,acknowledgeMode,deliveryMode,batchSize,maximumDurationMs,producerStartDelayMs,producerIntervalMs,isTopic,isDurableSubscription,isBrowsingSubscription,isSelector,isNoLocal,isSynchronousConsumer,totalNumberOfConsumers,totalNumberOfProducers,totalPayloadProcessedB,throughputKbPerS,timeTakenMs,errorMessage +TEST1,0,CONFIGURED_CLIENT1,PARTICIPANT,0,1,2,3,4,5,6,7,8,9,true,false,true,false,true,false,1,2,1024,2048,1000,error
\ No newline at end of file diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/DistributedTestSystemTestBase.java b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/DistributedTestSystemTestBase.java new file mode 100644 index 0000000000..96daf64526 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/DistributedTestSystemTestBase.java @@ -0,0 +1,72 @@ +/* + * 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.systest.disttest; + +import java.util.Properties; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.JMSException; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +import org.apache.qpid.test.utils.QpidBrokerTestCase; + +public class DistributedTestSystemTestBase extends QpidBrokerTestCase +{ + protected Context _context; + + protected Connection _connection; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + final Properties properties = new Properties(); + properties.load(DistributedTestSystemTestBase.class.getResourceAsStream("perftests.systests.properties")); + _context = new InitialContext(properties); + + _connection = getConnection(); + _connection.start(); + } + + @Override + protected void tearDown() throws Exception + { + // no need to close connections - this is done by superclass + + super.tearDown(); + } + + public Context getContext() + { + return _context; + } + + @Override + public Connection getConnection() throws JMSException, NamingException + { + final ConnectionFactory connectionFactory = (ConnectionFactory) _context.lookup("connectionfactory"); + final Connection connection = connectionFactory.createConnection(); + _connections.add(connection); + return connection; + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/QpidQueueCreatorTest.java b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/QpidQueueCreatorTest.java new file mode 100644 index 0000000000..784e43469e --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/QpidQueueCreatorTest.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.systest.disttest; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.jms.Connection; +import javax.jms.Session; + +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.disttest.DistributedTestException; +import org.apache.qpid.disttest.controller.config.QueueConfig; +import org.apache.qpid.disttest.jms.QpidQueueCreator; + +public class QpidQueueCreatorTest extends DistributedTestSystemTestBase +{ + private static final Map<String, Object> EMPTY_ATTRIBUTES = Collections.emptyMap(); + + private QpidQueueCreator _creator; + private Session _session; + private List<QueueConfig> _configs; + private String _queueName; + + @Override + public void setUp() throws Exception + { + super.setUp(); + Connection connection = getConnection(); + _session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + _creator = new QpidQueueCreator(); + _configs = new ArrayList<QueueConfig>(); + _queueName = "direct://amq.direct//" + getTestQueueName(); + } + + public void testCreateQueueWithoutAttributes() throws Exception + { + _configs.add(new QueueConfig(_queueName, true, EMPTY_ATTRIBUTES)); + + assertQueueBound(_queueName, false); + + _creator.createQueues(_session, _configs); + + assertQueueBound(_queueName, true); + } + + public void testCreateWithAttributes() throws Exception + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put("x-qpid-priorities", Integer.valueOf(5)); + _configs.add(new QueueConfig(_queueName, true, attributes)); + + assertQueueBound(_queueName, false); + + _creator.createQueues(_session, _configs); + + assertQueueBound(_queueName, true); + } + + public void testDeleteQueues() throws Exception + { + _configs.add(new QueueConfig(_queueName, true, EMPTY_ATTRIBUTES)); + + assertQueueBound(_queueName, false); + + _creator.createQueues(_session, _configs); + assertQueueBound(_queueName, true); + + _creator.deleteQueues(_session, _configs); + assertQueueBound(_queueName, false); + } + + public void testDeleteQueueThatDoesNotExist() throws Exception + { + String queueThatDoesNotExist = _queueName; + List<QueueConfig> configs = new ArrayList<QueueConfig>(); + Map<String, Object> attributes = Collections.emptyMap(); + configs.add(new QueueConfig(queueThatDoesNotExist, true, attributes)); + + try + { + _creator.deleteQueues(_session, configs); + fail("Exception not thrown"); + } + catch (DistributedTestException e) + { + // PASS + } + } + + private void assertQueueBound(String queueName, boolean isBound) throws Exception + { + AMQDestination destination = (AMQDestination)_session.createQueue(queueName); + assertEquals("Queue is not in expected bound state", isBound, ((AMQSession<?, ?>)_session).isQueueBound(destination)); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/SystemTestConstants.java b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/SystemTestConstants.java new file mode 100644 index 0000000000..808b428bc9 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/SystemTestConstants.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.systest.disttest; + +public abstract class SystemTestConstants +{ + public static final long REGISTRATION_TIMEOUT = 5000; + public static final long COMMAND_RESPONSE_TIMEOUT = 10000; + public static final long TEST_RESULT_TIMEOUT = 5000; + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/BasicDistributedClientTest.java b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/BasicDistributedClientTest.java new file mode 100644 index 0000000000..d599bdc5c4 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/BasicDistributedClientTest.java @@ -0,0 +1,186 @@ +/* + * 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.systest.disttest.clientonly; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; + +import org.apache.qpid.disttest.client.Client; +import org.apache.qpid.disttest.client.ClientState; +import org.apache.qpid.disttest.jms.ClientJmsDelegate; +import org.apache.qpid.disttest.jms.JmsMessageAdaptor; +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.CommandType; +import org.apache.qpid.disttest.message.CreateConnectionCommand; +import org.apache.qpid.disttest.message.CreateSessionCommand; +import org.apache.qpid.disttest.message.NoOpCommand; +import org.apache.qpid.disttest.message.RegisterClientCommand; +import org.apache.qpid.disttest.message.Response; +import org.apache.qpid.disttest.message.StopClientCommand; +import org.apache.qpid.systest.disttest.DistributedTestSystemTestBase; + +public class BasicDistributedClientTest extends DistributedTestSystemTestBase +{ + private Session _session = null; + private MessageProducer _clientQueueProducer; + private Client _client; + private ControllerQueue _controllerQueue; + private ClientJmsDelegate _clientJmsDelegate = null; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + _controllerQueue = new ControllerQueue(_connection, _context); + _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + _clientJmsDelegate = new ClientJmsDelegate(_context); + _client = new Client(_clientJmsDelegate); + _client.start(); + } + + @Override + protected void tearDown() throws Exception + { + try + { + _controllerQueue.close(); + if (_session != null) + { + _session.close(); + } + } + finally + { + super.tearDown(); + } + } + + public void testClientSendsRegistrationMessage() throws Exception + { + final RegisterClientCommand regClientCommand = _controllerQueue.getNext(); + + assertNotNull("Client must have a non-null name", regClientCommand.getClientName()); + assertEquals("Unexpected client name", _clientJmsDelegate.getClientName(), regClientCommand.getClientName()); + assertNotNull("Client queue name should not be null", regClientCommand.getClientQueueName()); + } + + public void testClientSendsCommandResponses() throws Exception + { + final RegisterClientCommand registrationCommand = _controllerQueue.getNext(); + createClientQueueProducer(registrationCommand); + + sendCommandToClient(new NoOpCommand()); + + final Response responseCommand = _controllerQueue.getNext(); + assertEquals("Incorrect client message type", CommandType.RESPONSE, responseCommand.getType()); + } + + public void testClientCanBeStoppedViaCommand() throws Exception + { + assertEquals("Expected client to be in STARTED state", ClientState.READY, _client.getState()); + + final RegisterClientCommand registrationCommand = _controllerQueue.getNext(); + createClientQueueProducer(registrationCommand); + + final Command stopClientCommand = new StopClientCommand(); + sendCommandToClient(stopClientCommand); + + _client.waitUntilStopped(1000); + + Response response = _controllerQueue.getNext(); + assertNotNull(response); + assertFalse("response shouldn't contain error", response.hasError()); + + assertEquals("Expected client to be in STOPPED state", ClientState.STOPPED, _client.getState()); + } + + public void testClientCanCreateTestConnection() throws Exception + { + assertEquals("Unexpected number of test connections", 0, _clientJmsDelegate.getNoOfTestConnections()); + + final RegisterClientCommand registration = _controllerQueue.getNext(); + createClientQueueProducer(registration); + + final CreateConnectionCommand createConnectionCommand = new CreateConnectionCommand(); + createConnectionCommand.setConnectionName("newTestConnection"); + createConnectionCommand.setConnectionFactoryName("connectionfactory"); + + sendCommandToClient(createConnectionCommand); + Response response = _controllerQueue.getNext(); + + assertFalse("Response message should not have indicated an error", response.hasError()); + assertEquals("Unexpected number of test connections", 1, _clientJmsDelegate.getNoOfTestConnections()); + } + + public void testClientCanCreateTestSession() throws Exception + { + assertEquals("Unexpected number of test sessions", 0, _clientJmsDelegate.getNoOfTestSessions()); + + final RegisterClientCommand registration = _controllerQueue.getNext(); + createClientQueueProducer(registration); + + final CreateConnectionCommand createConnectionCommand = new CreateConnectionCommand(); + createConnectionCommand.setConnectionName("newTestConnection"); + createConnectionCommand.setConnectionFactoryName("connectionfactory"); + + sendCommandToClient(createConnectionCommand); + Response response = _controllerQueue.getNext(); + assertFalse("Response message should not have indicated an error", response.hasError()); + + final CreateSessionCommand createSessionCommand = new CreateSessionCommand(); + createSessionCommand.setConnectionName("newTestConnection"); + createSessionCommand.setSessionName("newTestSession"); + createSessionCommand.setAcknowledgeMode(Session.AUTO_ACKNOWLEDGE); + + sendCommandToClient(createSessionCommand); + response = _controllerQueue.getNext(); + + assertFalse("Response message should not have indicated an error", response.hasError()); + assertEquals("Unexpected number of test sessions", 1, _clientJmsDelegate.getNoOfTestSessions()); + } + + private void sendCommandToClient(final Command command) throws JMSException + { + final Message message = JmsMessageAdaptor.commandToMessage(_session, command); + _clientQueueProducer.send(message); + } + + private void createClientQueueProducer( + final RegisterClientCommand registration) throws JMSException + { + final Destination clientCommandQueue = createDestinationFromRegistration(registration); + _clientQueueProducer = _session.createProducer(clientCommandQueue); + } + + private Queue createDestinationFromRegistration( + final RegisterClientCommand registrationCommand) + throws JMSException + { + String clientQueueName = registrationCommand.getClientQueueName(); + assertNotNull("Null client queue in register message", clientQueueName); + return _session.createQueue(clientQueueName); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/ConsumerParticipantTest.java b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/ConsumerParticipantTest.java new file mode 100644 index 0000000000..a3c0430865 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/ConsumerParticipantTest.java @@ -0,0 +1,156 @@ +/* + * 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.systest.disttest.clientonly; + +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; + +import org.apache.qpid.disttest.client.Client; +import org.apache.qpid.disttest.client.ConsumerParticipant; +import org.apache.qpid.disttest.client.ParticipantExecutor; +import org.apache.qpid.disttest.message.CreateConsumerCommand; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.systest.disttest.DistributedTestSystemTestBase; +import org.apache.qpid.systest.disttest.clientonly.ProducerParticipantTest.TestClientJmsDelegate; + +public class ConsumerParticipantTest extends DistributedTestSystemTestBase +{ + private MessageProducer _producer; + private Session _session; + private TestClientJmsDelegate _delegate; + private Client _client; + private ControllerQueue _controllerQueue; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + _controllerQueue = new ControllerQueue(_connection, _context); + _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + _producer = _session.createProducer(getTestQueue()); + + _delegate = new TestClientJmsDelegate(getContext()); + _client = new Client(_delegate); + } + + + @Override + protected void tearDown() throws Exception + { + _controllerQueue.close(); + super.tearDown(); + } + + public void testConsumeNumberOfMessagesSynchronously() throws Exception + { + runTest(Session.AUTO_ACKNOWLEDGE, 10, 0, true); + } + + public void testConsumeNumberOfMessagesAsynchronously() throws Exception + { + runTest(Session.AUTO_ACKNOWLEDGE, 10, 0, false); + } + + public void testSelectors() throws Exception + { + final CreateConsumerCommand command = new CreateConsumerCommand(); + command.setNumberOfMessages(10); + command.setSessionName("testSession"); + command.setDestinationName(getTestQueueName()); + command.setSelector("id=1"); + Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + _delegate.addConnection("name-does-not-matter", _connection); + _delegate.addSession(command.getSessionName(), session); + + ConsumerParticipant consumerParticipant = new ConsumerParticipant(_delegate, command); + _delegate.createConsumer(command); + + for (int i = 0; i < 20; i++) + { + Message message = _session.createMessage(); + if (i % 2 == 0) + { + message.setIntProperty("id", 0); + } + else + { + message.setIntProperty("id", 1); + } + _producer.send(message); + } + + new ParticipantExecutor(consumerParticipant).start(_client); + + ParticipantResult results = _controllerQueue.getNext(); + assertNotNull("No results message recieved", results); + assertEquals("Unexpected number of messages received", 10, results.getNumberOfMessagesProcessed()); + + Session testQueueConsumerSession = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + final MessageConsumer testQueueConsumer = testQueueConsumerSession.createConsumer(getTestQueue()); + for (int i = 0; i < 10; i++) + { + Message message = testQueueConsumer.receive(2000); + assertNotNull("Message is not received: " + message, message); + assertEquals("Unexpected id value", 0, message.getIntProperty("id")); + } + Message message = testQueueConsumer.receive(2000); + assertNull("Unexpected message remaining on test queue: " + message, message); + + _connection.stop(); + } + + protected void runTest(int acknowledgeMode, int numberOfMessages, int batchSize, boolean synchronous) throws Exception + { + final CreateConsumerCommand command = new CreateConsumerCommand(); + command.setNumberOfMessages(numberOfMessages); + command.setBatchSize(batchSize); + command.setSessionName("testSession"); + command.setDestinationName(getTestQueueName()); + command.setSynchronous(synchronous); + + Session session = _connection.createSession(Session.SESSION_TRANSACTED == acknowledgeMode, acknowledgeMode); + + _delegate.addConnection("name-does-not-matter", _connection); + _delegate.addSession(command.getSessionName(), session); + + ConsumerParticipant consumerParticipant = new ConsumerParticipant(_delegate, command); + _delegate.createConsumer(command); + + for (int i = 0; i < numberOfMessages; i++) + { + _producer.send(_session.createMessage()); + } + + new ParticipantExecutor(consumerParticipant).start(_client); + + ParticipantResult results = _controllerQueue.getNext(); + assertNotNull("No results message recieved", results); + + Session testQueueConsumerSession = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + final MessageConsumer testQueueConsumer = testQueueConsumerSession.createConsumer(getTestQueue()); + Message message = testQueueConsumer.receive(2000); + assertNull("Unexpected message remaining on test queue: " + message, message); + + _connection.stop(); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/ControllerQueue.java b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/ControllerQueue.java new file mode 100644 index 0000000000..7f0c23eb38 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/ControllerQueue.java @@ -0,0 +1,90 @@ +package org.apache.qpid.systest.disttest.clientonly; + +import java.util.Map; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Session; +import javax.naming.Context; + +import junit.framework.Assert; + +import org.apache.qpid.disttest.DistributedTestConstants; +import org.apache.qpid.disttest.jms.JmsMessageAdaptor; +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.CommandType; + +/** + * Helper for unit tests to simplify access to the Controller Queue. + * + * Implicitly creates the queue, so you must create a {@link ControllerQueue} object before + * trying to use the underlying queue. + */ +public class ControllerQueue +{ + private MessageConsumer _controllerQueueMessageConsumer; + private Session _controllerQueueSession; + + /** + * Implicitly creates the queue, so you must create a {@link ControllerQueue} object before + * trying to use the underlying queue. + * + * @param context used for looking up the controller queue {@link Destination} + */ + public ControllerQueue(Connection connection, Context context) throws Exception + { + _controllerQueueSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Destination controllerQueue = (Destination) context.lookup(DistributedTestConstants.CONTROLLER_QUEUE_JNDI_NAME); + _controllerQueueMessageConsumer = _controllerQueueSession.createConsumer(controllerQueue); + } + + public <T extends Command> T getNext(long timeout) throws JMSException + { + final Message message = _controllerQueueMessageConsumer.receive(timeout); + if(message == null) + { + return null; + } + + return (T) JmsMessageAdaptor.messageToCommand(message); + } + + public void addNextResponse(Map<CommandType, Command> responses) throws JMSException + { + Command nextResponse = getNext(); + responses.put(nextResponse.getType(), nextResponse); + } + + @SuppressWarnings("unchecked") + public <T extends Command> T getNext() throws JMSException + { + return (T)getNext(true); + } + + public <T extends Command> T getNext(boolean assertMessageExists) throws JMSException + { + final Message message = _controllerQueueMessageConsumer.receive(1000); + if(assertMessageExists) + { + Assert.assertNotNull("No message received from control queue", message); + } + + if(message == null) + { + return null; + } + + T command = (T) JmsMessageAdaptor.messageToCommand(message); + + return command; + } + + public void close() throws Exception + { + _controllerQueueMessageConsumer.close(); + _controllerQueueSession.close(); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/DistributedClientTest.java b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/DistributedClientTest.java new file mode 100644 index 0000000000..4a872a7ee2 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/DistributedClientTest.java @@ -0,0 +1,323 @@ +/* + * 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.systest.disttest.clientonly; + +import static org.apache.qpid.disttest.client.ClientState.READY; +import static org.apache.qpid.disttest.client.ClientState.RUNNING_TEST; + +import java.util.HashMap; +import java.util.Map; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; + +import org.apache.qpid.disttest.client.Client; +import org.apache.qpid.disttest.client.ClientState; +import org.apache.qpid.disttest.jms.ClientJmsDelegate; +import org.apache.qpid.disttest.jms.JmsMessageAdaptor; +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.CommandType; +import org.apache.qpid.disttest.message.CreateConnectionCommand; +import org.apache.qpid.disttest.message.CreateConsumerCommand; +import org.apache.qpid.disttest.message.CreateProducerCommand; +import org.apache.qpid.disttest.message.CreateSessionCommand; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.disttest.message.RegisterClientCommand; +import org.apache.qpid.disttest.message.Response; +import org.apache.qpid.disttest.message.StartTestCommand; +import org.apache.qpid.disttest.message.TearDownTestCommand; +import org.apache.qpid.systest.disttest.DistributedTestSystemTestBase; + +public class DistributedClientTest extends DistributedTestSystemTestBase +{ + private static final String TEST_CONSUMER = "newTestConsumer"; + private static final String TEST_DESTINATION = "newDestination"; + private static final String TEST_PRODUCER_NAME = "newTestProducer"; + private static final String TEST_SESSION_NAME = "newTestSession"; + private static final String TEST_CONNECTION_NAME = "newTestConnection"; + + private Session _session = null; + private MessageProducer _clientQueueProducer; + private Client _client; + private ControllerQueue _controllerQueue; + protected ClientJmsDelegate _clientJmsDelegate; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + _controllerQueue = new ControllerQueue(_connection, _context); + + _clientJmsDelegate = new ClientJmsDelegate(_context); + _client = new Client(_clientJmsDelegate); + _client.start(); + + final RegisterClientCommand registrationCommand = _controllerQueue.getNext(); + createClientQueueProducer(registrationCommand); + + createTestConnection(TEST_CONNECTION_NAME); + createTestSession(TEST_CONNECTION_NAME, TEST_SESSION_NAME); + + assertEquals("Expected no test producers at start of test", 0, _clientJmsDelegate.getNoOfTestProducers()); + assertEquals("Expected no test consumers at start of test", 0, _clientJmsDelegate.getNoOfTestConsumers()); + } + + @Override + protected void tearDown() throws Exception + { + try + { + _controllerQueue.close(); + if (_session != null) + { + _session.close(); + } + } + finally + { + super.tearDown(); + } + } + + public void testClientCanCreateTestProducer() throws Exception + { + assertEquals("Should initially have zero producers", 0, _clientJmsDelegate.getNoOfTestProducers()); + + createTestProducer(TEST_SESSION_NAME, TEST_PRODUCER_NAME, TEST_DESTINATION); + + assertEquals("Should now have one test producer", 1, _clientJmsDelegate.getNoOfTestProducers()); + } + + public void testClientCanCreateTestConsumer() throws Exception + { + assertEquals("Should initially have no test consumers", 0, _clientJmsDelegate.getNoOfTestConsumers()); + + createTestConsumer(TEST_SESSION_NAME, TEST_CONSUMER, TEST_DESTINATION); + + assertEquals("Should now have one test consumer", 1, _clientJmsDelegate.getNoOfTestConsumers()); + } + + public void testClientFailsToCreateSessionUsingInvalidConnection() throws Exception + { + int initialNoOfTestSessions = _clientJmsDelegate.getNoOfTestSessions(); + + createTestSession("nonExistentConnection", TEST_SESSION_NAME, false /* shouldSucceed */); + + assertEquals("Number of test sessions should not have changed", initialNoOfTestSessions, _clientJmsDelegate.getNoOfTestSessions()); + } + + public void testClientFailsToCreateProducerUsingInvalidSession() throws Exception + { + int initialNoOfTestProducers = _clientJmsDelegate.getNoOfTestProducers(); + + createTestProducer("invalidSessionName", TEST_PRODUCER_NAME, TEST_DESTINATION, false /* shouldSucceed */); + + assertEquals("Number of test producers should not have changed", initialNoOfTestProducers, _clientJmsDelegate.getNoOfTestProducers()); + } + + public void testClientFailsToCreateConsumerUsingInvalidSession() throws Exception + { + int initialNoOfTestConsumers = _clientJmsDelegate.getNoOfTestConsumers(); + + createTestConsumer("invalidSessionName", TEST_CONSUMER, TEST_DESTINATION, false /* shouldSucceed */); + + assertEquals("Number of test consumers should not have changed", initialNoOfTestConsumers, _clientJmsDelegate.getNoOfTestConsumers()); + } + + public void testClientCanStartPerformingTests() throws Exception + { + createTestProducer(TEST_SESSION_NAME, TEST_PRODUCER_NAME, TEST_DESTINATION); + + sendCommandToClient(new StartTestCommand()); + + validateStartTestResponseAndParticipantResults(CommandType.PRODUCER_PARTICIPANT_RESULT); + + assertState(_client, RUNNING_TEST); + } + + public void testParticipantsSendResults() throws JMSException + { + createTestProducer(TEST_SESSION_NAME, TEST_PRODUCER_NAME, TEST_DESTINATION); + + sendCommandToClient(new StartTestCommand()); + + validateStartTestResponseAndParticipantResults(CommandType.PRODUCER_PARTICIPANT_RESULT); + } + + /** + * Need to validate both of these responses together because their order is non-deterministic + * @param expectedParticipantResultCommandType TODO + */ + private void validateStartTestResponseAndParticipantResults(CommandType expectedParticipantResultCommandType) throws JMSException + { + Map<CommandType, Command> responses = new HashMap<CommandType, Command>(); + _controllerQueue.addNextResponse(responses); + _controllerQueue.addNextResponse(responses); + + ParticipantResult results = (ParticipantResult) responses.get(expectedParticipantResultCommandType); + validateResponse(null, results, true); + + Response startTestResponse = (Response) responses.get(CommandType.RESPONSE); + validateResponse(CommandType.START_TEST, startTestResponse, true); + } + + public void testClientCannotStartPerformingTestsInNonReadyState() throws Exception + { + assertState(_client, READY); + sendCommandAndValidateResponse(new StartTestCommand(), true); + assertState(_client, RUNNING_TEST); + + // Send another start test command + sendCommandAndValidateResponse(new StartTestCommand(), false /*should reject duplicate start command*/); + assertState(_client, RUNNING_TEST); + } + + public void testNonRunningClientIsUnaffectedByStopTestCommand() throws Exception + { + assertState(_client, READY); + + sendCommandAndValidateResponse(new TearDownTestCommand(), false); + + assertState(_client, READY); + } + + private void sendCommandToClient(final Command command) throws JMSException + { + final Message message = JmsMessageAdaptor.commandToMessage(_session, command); + _clientQueueProducer.send(message); + } + + private void sendCommandAndValidateResponse(final Command command, boolean shouldSucceed) throws JMSException + { + sendCommandToClient(command); + Response response = _controllerQueue.getNext(); + validateResponse(command.getType(), response, shouldSucceed); + } + + private void sendCommandAndValidateResponse(final Command command) throws JMSException + { + sendCommandAndValidateResponse(command, true); + } + + private void createTestConnection(String connectionName) throws Exception + { + int initialNumberOfConnections = _clientJmsDelegate.getNoOfTestConnections(); + + final CreateConnectionCommand createConnectionCommand = new CreateConnectionCommand(); + createConnectionCommand.setConnectionName(connectionName); + createConnectionCommand.setConnectionFactoryName("connectionfactory"); + + sendCommandAndValidateResponse(createConnectionCommand); + + int expectedNumberOfConnections = initialNumberOfConnections + 1; + + assertEquals("unexpected number of test connections", expectedNumberOfConnections, _clientJmsDelegate.getNoOfTestConnections()); + } + + private void createTestSession(String connectionName, String sessionName, boolean shouldSucceed) throws Exception + { + int initialNumberOfSessions = _clientJmsDelegate.getNoOfTestSessions(); + + final CreateSessionCommand createSessionCommand = new CreateSessionCommand(); + createSessionCommand.setConnectionName(connectionName); + createSessionCommand.setSessionName(sessionName); + createSessionCommand.setAcknowledgeMode(Session.AUTO_ACKNOWLEDGE); + + sendCommandAndValidateResponse(createSessionCommand, shouldSucceed); + + int expectedNumberOfSessions = initialNumberOfSessions + (shouldSucceed ? 1 : 0); + + assertEquals("unexpected number of test sessions", expectedNumberOfSessions, _clientJmsDelegate.getNoOfTestSessions()); + } + + private void createTestSession(String connectionName, String sessionName) throws Exception + { + createTestSession(connectionName, sessionName, true); + } + + private void createTestProducer(String sessionName, String producerName, String destinationName, boolean shouldSucceed) throws JMSException + { + final CreateProducerCommand createProducerCommand = new CreateProducerCommand(); + createProducerCommand.setParticipantName(producerName); + createProducerCommand.setSessionName(sessionName); + createProducerCommand.setDestinationName(destinationName); + createProducerCommand.setNumberOfMessages(100); + + sendCommandAndValidateResponse(createProducerCommand, shouldSucceed); + } + + private void createTestProducer(String sessionName, String producerName, String destinationName) throws JMSException + { + createTestProducer(sessionName, producerName, destinationName, true); + } + + private void createTestConsumer(String sessionName, String consumerName, String destinationName, boolean shouldSucceed) throws JMSException + { + final CreateConsumerCommand createConsumerCommand = new CreateConsumerCommand(); + createConsumerCommand.setSessionName(sessionName); + createConsumerCommand.setDestinationName(destinationName); + createConsumerCommand.setParticipantName(consumerName); + createConsumerCommand.setNumberOfMessages(1); + + sendCommandAndValidateResponse(createConsumerCommand, shouldSucceed); + } + + private void createTestConsumer(String sessionName, String consumerName, String destinationName) throws JMSException + { + createTestConsumer(sessionName, consumerName, destinationName, true); + } + + private void validateResponse(CommandType originatingCommandType, Response response, boolean shouldSucceed) throws JMSException + { + assertEquals("Response is a reply to the wrong command: " + response, + originatingCommandType, + response.getInReplyToCommandType()); + + boolean shouldHaveError = !shouldSucceed; + assertEquals("Response message " + response + " should have indicated hasError=" + shouldHaveError, + shouldHaveError, + response.hasError()); + } + + private void createClientQueueProducer(final RegisterClientCommand registration) throws JMSException + { + final Destination clientCommandQueue = createDestinationFromRegistration(registration); + _clientQueueProducer = _session.createProducer(clientCommandQueue); + } + + private Queue createDestinationFromRegistration(final RegisterClientCommand registrationCommand) throws JMSException + { + String clientQueueName = registrationCommand.getClientQueueName(); + assertNotNull("Null client queue in register message", clientQueueName); + return _session.createQueue(clientQueueName); + } + + private static void assertState(Client client, ClientState expectedState) + { + ClientState clientState = client.getState(); + assertEquals("Client should be in state: " + expectedState + " but is in state " + clientState, expectedState, clientState); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/MessageProviderTest.java b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/MessageProviderTest.java new file mode 100644 index 0000000000..dcbff6518b --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/MessageProviderTest.java @@ -0,0 +1,119 @@ +/* + * 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.systest.disttest.clientonly; + +import java.util.HashMap; +import java.util.Map; + +import javax.jms.DeliveryMode; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import org.apache.qpid.disttest.client.MessageProvider; +import org.apache.qpid.disttest.client.property.PropertyValue; +import org.apache.qpid.disttest.client.property.SimplePropertyValue; +import org.apache.qpid.disttest.message.CreateMessageProviderCommand; +import org.apache.qpid.disttest.message.CreateProducerCommand; +import org.apache.qpid.systest.disttest.DistributedTestSystemTestBase; +import org.apache.qpid.systest.disttest.clientonly.ProducerParticipantTest.TestClientJmsDelegate; + +public class MessageProviderTest extends DistributedTestSystemTestBase +{ + private MessageConsumer _consumer; + private Session _session; + private TestClientJmsDelegate _delegate; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + _consumer = _session.createConsumer(getTestQueue()); + _delegate = new TestClientJmsDelegate(getContext()); + } + + public void testMessageSize() throws Exception + { + runSizeTest(0); + runSizeTest(5); + runSizeTest(512); + } + + public void runSizeTest(int size) throws Exception + { + CreateProducerCommand command = new CreateProducerCommand(); + command.setMessageSize(size); + MessageProvider messageProvider = new MessageProvider(null); + Message message = messageProvider.nextMessage(_session, command); + assertNotNull("Message is not generated", message); + assertTrue("Wrong message type", message instanceof TextMessage); + TextMessage textMessage = (TextMessage)message; + String text = textMessage.getText(); + assertNotNull("Message payload is not generated", text); + assertEquals("Message payload size is incorrect", size, text.length()); + } + + public void testCreateMessageProviderAndSendMessage() throws Exception + { + final CreateMessageProviderCommand messageProviderCommand = new CreateMessageProviderCommand(); + messageProviderCommand.setProviderName("test1"); + Map<String, PropertyValue> messageProperties = new HashMap<String, PropertyValue>(); + messageProperties.put("test", new SimplePropertyValue("testValue")); + messageProperties.put("priority", new SimplePropertyValue(new Integer(9))); + messageProviderCommand.setMessageProperties(messageProperties); + _delegate.createMessageProvider(messageProviderCommand); + + final CreateProducerCommand producerCommand = new CreateProducerCommand(); + producerCommand.setNumberOfMessages(1); + producerCommand.setDeliveryMode(DeliveryMode.PERSISTENT); + producerCommand.setPriority(6); + producerCommand.setParticipantName("test"); + producerCommand.setMessageSize(10); + producerCommand.setSessionName("testSession"); + producerCommand.setDestinationName(getTestQueueName()); + producerCommand.setMessageProviderName(messageProviderCommand.getProviderName()); + + Session session = _connection.createSession(true, Session.SESSION_TRANSACTED); + _delegate.addConnection("name-does-not-matter", _connection); + _delegate.addSession(producerCommand.getSessionName(), session); + _delegate.createProducer(producerCommand); + + Message message = _delegate.sendNextMessage(producerCommand); + session.commit(); + assertMessage(message); + + _connection.start(); + Message receivedMessage = _consumer.receive(1000l); + assertMessage(receivedMessage); + } + + protected void assertMessage(Message message) throws JMSException + { + assertNotNull("Message should not be null", message); + assertEquals("Unexpected test property", "testValue", message.getStringProperty("test")); + assertEquals("Unexpected priority property", 9, message.getJMSPriority()); + assertTrue("Unexpected message type", message instanceof TextMessage); + String text = ((TextMessage)message).getText(); + assertNotNull("Message text should not be null", text); + assertNotNull("Unexpected message size ", text.length()); + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/ProducerParticipantTest.java b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/ProducerParticipantTest.java new file mode 100644 index 0000000000..54bb9efa98 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/clientonly/ProducerParticipantTest.java @@ -0,0 +1,132 @@ +/* + * 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.systest.disttest.clientonly; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.Context; + +import org.apache.qpid.disttest.client.Client; +import org.apache.qpid.disttest.client.ParticipantExecutor; +import org.apache.qpid.disttest.client.ProducerParticipant; +import org.apache.qpid.disttest.jms.ClientJmsDelegate; +import org.apache.qpid.disttest.message.CreateProducerCommand; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.systest.disttest.DistributedTestSystemTestBase; + +public class ProducerParticipantTest extends DistributedTestSystemTestBase +{ + private MessageConsumer _consumer; + private TestClientJmsDelegate _delegate; + private Client _client; + private ControllerQueue _controllerQueue; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + _controllerQueue = new ControllerQueue(_connection, _context); + Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + _consumer = session.createConsumer(getTestQueue()); + + _delegate = new TestClientJmsDelegate(getContext()); + _client = new Client(_delegate); + } + + + + @Override + protected void tearDown() throws Exception + { + _controllerQueue.close(); + super.tearDown(); + } + + + + public void testProduceNumberOfMessages() throws Exception + { + runTest(Session.AUTO_ACKNOWLEDGE, 100, 10, 0, 0); + } + + protected void runTest(int acknowledgeMode, int messageSize, int numberOfMessages, int batchSize, long publishInterval) throws Exception + { + final CreateProducerCommand command = new CreateProducerCommand(); + command.setNumberOfMessages(numberOfMessages); + command.setDeliveryMode(DeliveryMode.PERSISTENT); + command.setParticipantName("test"); + command.setMessageSize(messageSize); + command.setBatchSize(batchSize); + command.setInterval(publishInterval); + command.setSessionName("testSession"); + command.setDestinationName(getTestQueueName()); + + Session session = _connection.createSession(Session.SESSION_TRANSACTED == acknowledgeMode, acknowledgeMode); + + _delegate.addConnection("name-does-not-matter", _connection); + _delegate.addSession(command.getSessionName(), session); + _delegate.createProducer(command); + + final ProducerParticipant producer = new ProducerParticipant(_delegate, command); + + new ParticipantExecutor(producer).start(_client); + + _connection.start(); + for (int i = 0; i < numberOfMessages; i++) + { + final Message m = _consumer.receive(1000l); + assertNotNull("Expected message [" + i + "] is not received", m); + assertTrue("Unexpected message", m instanceof TextMessage); + } + Message m = _consumer.receive(500l); + assertNull("Unexpected message", m); + + ParticipantResult results = _controllerQueue.getNext(); + + assertNotNull("no results", results); + assertFalse(results.getStartInMillis() == 0); + assertFalse(results.getEndInMillis() == 0); + } + + static class TestClientJmsDelegate extends ClientJmsDelegate + { + + public TestClientJmsDelegate(Context context) + { + super(context); + } + + @Override + public void addSession(final String sessionName, final Session newSession) + { + super.addSession(sessionName, newSession); + } + + @Override + public void addConnection(final String connectionName, final Connection newConnection) + { + super.addConnection(connectionName, newConnection); + } + } +} diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/ControllerAndClientTest.java b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/ControllerAndClientTest.java new file mode 100644 index 0000000000..9fd90d3215 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/ControllerAndClientTest.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.systest.disttest.controllerandclient; + +import static org.apache.qpid.systest.disttest.SystemTestConstants.COMMAND_RESPONSE_TIMEOUT; +import static org.apache.qpid.systest.disttest.SystemTestConstants.REGISTRATION_TIMEOUT; +import static org.apache.qpid.systest.disttest.SystemTestConstants.TEST_RESULT_TIMEOUT; + +import java.util.List; + +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.naming.NamingException; + +import org.apache.qpid.disttest.ConfigFileHelper; +import org.apache.qpid.disttest.client.Client; +import org.apache.qpid.disttest.client.ClientState; +import org.apache.qpid.disttest.controller.Controller; +import org.apache.qpid.disttest.controller.ResultsForAllTests; +import org.apache.qpid.disttest.controller.TestResult; +import org.apache.qpid.disttest.controller.config.Config; +import org.apache.qpid.disttest.jms.ClientJmsDelegate; +import org.apache.qpid.disttest.jms.ControllerJmsDelegate; +import org.apache.qpid.disttest.message.ConsumerParticipantResult; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.disttest.message.ProducerParticipantResult; +import org.apache.qpid.disttest.results.aggregation.ITestResult; +import org.apache.qpid.systest.disttest.DistributedTestSystemTestBase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ControllerAndClientTest extends DistributedTestSystemTestBase +{ + private static final Logger LOGGER = LoggerFactory.getLogger(ControllerAndClientTest.class); + private static final long CLIENT_BACKGROUND_THREAD_WAIT_TIME = 5000; + + private Controller _controller; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + _controller = new Controller(new ControllerJmsDelegate(_context), REGISTRATION_TIMEOUT, COMMAND_RESPONSE_TIMEOUT); + _controller.setTestResultTimeout(TEST_RESULT_TIMEOUT); + } + + public void testProducerAndConsumerInSeparateClients() throws Exception + { + List<TestResult> resultList = runTestsForTwoClients("producerAndConsumerInSeparateClients.json", 1); + + TestResult testResult1 = resultList.get(0); + assertEquals("Unexpected test name", "Test 1", testResult1.getName()); + List<ParticipantResult> test1ParticipantResults = testResult1.getParticipantResults(); + assertEquals("Unexpected number of participant results for test 1", 2, test1ParticipantResults.size()); + assertParticipantNames(test1ParticipantResults, "participantConsumer1", "participantProducer1"); + } + + public void testProducerClient() throws Exception + { + Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = session.createQueue("producerClient"); + MessageConsumer consumer = session.createConsumer(queue); + + // queue is not declared in configuration + // controller is not able to clean it + // cleaning manually + while(consumer.receive(1000l) != null); + + final Config config = ConfigFileHelper.getConfigFromResource(getClass(), "produceClient.json"); + _controller.setConfig(config); + final Client client1 = new Client(new ClientJmsDelegate(_context)); + final Thread client1Thread = createBackgroundClientThread(client1); + _controller.awaitClientRegistrations(); + + ResultsForAllTests results = _controller.runAllTests(); + _controller.stopAllRegisteredClients(); + + assertClientThreadsShutdown(client1Thread); + assertClientsStopped(ClientState.STOPPED, client1); + assertFalse("Test should have no errors", results.hasErrors()); + List<ITestResult> allTestResults = results.getTestResults(); + assertEquals("Unexpected number of test results", 1, allTestResults.size()); + ITestResult testResult1 = allTestResults.get(0); + assertEquals("Unexpected test name", "Test 1", testResult1.getName()); + List<ParticipantResult> test1ParticipantResults = testResult1.getParticipantResults(); + assertEquals("Unexpected number of participant results for test 1", 1, test1ParticipantResults.size()); + assertParticipantNames(test1ParticipantResults, "participantProducer1"); + + // check message properties + for (int i=0; i< 10; i++) + { + Message message = consumer.receive(1000l); + assertNotNull("Message " + i + " is not received", message); + assertEquals("Unexpected priority", i, message.getJMSPriority()); + assertEquals("Unexpected id", i, message.getIntProperty("id")); + assertEquals("Unexpected test", "test-value", message.getStringProperty("test")); + } + } + + public void testProducerAndThreeConsumersInSeparateClients() throws Exception + { + List<TestResult> resultList = runTestsForTwoClients("producerAndThreeConsumersInSeparateClients.json", 1); + + TestResult testResult1 = resultList.get(0); + List<ParticipantResult> test1ParticipantResults = testResult1.getParticipantResults(); + assertEquals("Unexpected number of participant results for test", 4, test1ParticipantResults.size()); + + assertParticipantNames(test1ParticipantResults, "participantConsumer1", "participantConsumer2", "participantConsumer3", "participantProducer1"); + + ConsumerParticipantResult consumer1 = (ConsumerParticipantResult) test1ParticipantResults.get(0); + assertEquals(3, consumer1.getNumberOfMessagesProcessed()); + assertEquals(true, consumer1.isSynchronousConsumer()); + + ProducerParticipantResult producer1 = (ProducerParticipantResult) test1ParticipantResults.get(3); + assertEquals(9, producer1.getNumberOfMessagesProcessed()); + assertEquals(2, producer1.getBatchSize()); + assertEquals(50, producer1.getInterval()); + } + + public void testIteratingFeature() throws Exception + { + List<TestResult> resultList = runTestsForTwoClients("iteratingFeature.json", 2); + + assertTestResultMessageSize(resultList.get(0), 0, 100, 10); + assertTestResultMessageSize(resultList.get(1), 1, 200, 5); + + } + + private void assertTestResultMessageSize(TestResult testResult, int iterationNumber, int expectedMessageSize, int expectedNumberOfMessages) + { + List<ParticipantResult> test1ParticipantResults = testResult.getParticipantResults(); + assertEquals("Unexpected number of participant results for test", 2, test1ParticipantResults.size()); + + ParticipantResult producer1 = (ParticipantResult) test1ParticipantResults.get(1); + + assertEquals(expectedMessageSize, producer1.getPayloadSize()); + assertEquals(iterationNumber, producer1.getIterationNumber()); + } + + public void testTwoTests() throws Exception + { + List<TestResult> resultList = runTestsForTwoClients("testWithTwoTests.json", 2); + + assertEquals("Test 1", resultList.get(0).getName()); + assertEquals("Test 2", resultList.get(1).getName()); + } + + private List<TestResult> runTestsForTwoClients(String jsonConfigFile, int expectedNumberOfTests) throws NamingException, InterruptedException + { + final Config config = ConfigFileHelper.getConfigFromResource(getClass(), jsonConfigFile); + _controller.setConfig(config); + + final Client client1 = new Client(new ClientJmsDelegate(_context)); + final Client client2 = new Client(new ClientJmsDelegate(_context)); + + final Thread client1Thread = createBackgroundClientThread(client1); + final Thread client2Thread = createBackgroundClientThread(client2); + + _controller.awaitClientRegistrations(); + + ResultsForAllTests results = _controller.runAllTests(); + _controller.stopAllRegisteredClients(); + + assertClientThreadsShutdown(client1Thread, client2Thread); + assertClientsStopped(ClientState.STOPPED, client1, client2); + + assertFalse("Test should have no errors", results.hasErrors()); + + List<TestResult> allTestResults = (List)results.getTestResults(); + assertEquals("Unexpected number of test results", expectedNumberOfTests, allTestResults.size()); + + return allTestResults; + } + + + private void assertParticipantNames(List<ParticipantResult> participants, String... expectedOrderedParticipantNames) + { + assertEquals("Size of list of expected participant names is different from actual", expectedOrderedParticipantNames.length, participants.size()); + + for (int i = 0; i < expectedOrderedParticipantNames.length; i++) + { + String expectedParticipantName = expectedOrderedParticipantNames[i]; + ParticipantResult participant = participants.get(i); + assertEquals(expectedParticipantName, participant.getParticipantName()); + } + } + + private void assertClientsStopped(ClientState expectedState, final Client... clients) + { + for (Client client : clients) + { + assertEquals(client.getClientName() + " in unexpected state", expectedState, client.getState()); + } + } + + private void assertClientThreadsShutdown(final Thread... clientThreads) + throws InterruptedException + { + for (Thread clientThread : clientThreads) + { + clientThread.join(2000); + assertFalse(clientThread.getName() + " should have shutdown", clientThread.isAlive()); + } + } + + private Thread createBackgroundClientThread(final Client client) throws NamingException + { + final String clientThreadName = client.getClientName() + "-thread"; + final Thread clientThread = new Thread(new Runnable() + { + @Override + public void run() + { + try + { + client.start(); + client.waitUntilStopped(CLIENT_BACKGROUND_THREAD_WAIT_TIME); + } + finally + { + LOGGER.debug("Client thread {} finished", clientThreadName); + } + } + }, clientThreadName); + clientThread.start(); + return clientThread; + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/iteratingFeature.json b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/iteratingFeature.json new file mode 100644 index 0000000000..89123302b7 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/iteratingFeature.json @@ -0,0 +1,63 @@ +{ + "_tests":[ + { + "_name": "Test iteration feature", + "_iterations":[ + { + "_messageSize": 100, + "_numberOfMessages": 10 + }, + { + "_messageSize": 200, + "_numberOfMessages": 5 + } + ], + "_queues":[ + { + "_name": "direct://amq.direct//testQueue" + } + ], + "_clients":[ + { + "_name": "producingClient", + "_connections":[ + { + "_name": "connection1", + "_factory": "connectionfactory", + "_sessions": [ + { + "_sessionName": "session1", + "_producers": [ + { + "_name": "participantProducer1", + "_destinationName": "direct://amq.direct//testQueue" + } + ] + } + ] + } + ] + }, + { + "_name": "consumingClient", + "_connections":[ + { + "_name": "connection1", + "_factory": "connectionfactory", + "_sessions": [ + { + "_sessionName": "session1", + "_consumers": [ + { + "_name": "participantConsumer1", + "_destinationName": "direct://amq.direct//testQueue" + } + ] + } + ] + } + ] + } + ] + }] +}
\ No newline at end of file diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/produceClient.json b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/produceClient.json new file mode 100644 index 0000000000..605e5cb585 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/produceClient.json @@ -0,0 +1,41 @@ +{ + "_tests":[ + { + "_name": "Test 1"; + "_clients":[ + { + "_name": "producingClient", + "_connections":[ + { + "_name": "connection1", + "_factory": "connectionfactory", + "_sessions": [ + { + "_sessionName": "session1", + "_producers": [ + { + "_name": "participantProducer1", + "_destinationName": "direct://amq.direct//producerClient", + "_numberOfMessages": 10; + "_messageProviderName": "testProvider1" + } + ] + } + ] + } + ]; + "_messageProviders":[ + { + "_name": "testProvider1"; + "_messageProperties": { + "priority": {"@def": "list"; "_items": [0,1,2,3,4,5,6,7,8,9]}; + "id": {"@def": "range"; "_upper": 10; "_type": "int"}; + "test": "test-value" + } + } + ] + } + ] + } + ] +}
\ No newline at end of file diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/producerAndConsumerInSeparateClients.json b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/producerAndConsumerInSeparateClients.json new file mode 100644 index 0000000000..8d210dce84 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/producerAndConsumerInSeparateClients.json @@ -0,0 +1,55 @@ +{ + "_tests":[ + { + "_name": "Test 1"; + "_queues":[ + { + "_name": "direct://amq.direct//testQueue" + } + ]; + "_clients":[ + { + "_name": "producingClient", + "_connections":[ + { + "_name": "connection1", + "_factory": "connectionfactory", + "_sessions": [ + { + "_sessionName": "session1", + "_producers": [ + { + "_name": "participantProducer1", + "_destinationName": "direct://amq.direct//testQueue", + "_numberOfMessages": 1 + } + ] + } + ] + } + ] + }, + { + "_name": "consumingClient", + "_connections":[ + { + "_name": "connection1", + "_factory": "connectionfactory", + "_sessions": [ + { + "_sessionName": "session1", + "_consumers": [ + { + "_name": "participantConsumer1", + "_destinationName": "direct://amq.direct//testQueue", + "_numberOfMessages": 1 + } + ] + } + ] + } + ] + } + ] + }] +}
\ No newline at end of file diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/producerAndThreeConsumersInSeparateClients.json b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/producerAndThreeConsumersInSeparateClients.json new file mode 100644 index 0000000000..f94c4f0ae0 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/producerAndThreeConsumersInSeparateClients.json @@ -0,0 +1,77 @@ +{ + "_tests":[ + { + "_name": "ProducerAndThreeConsumersInSeparateClients"; + "_queues":[ + { + "_name": "direct://amq.direct//testQueue" + } + ]; + "_clients":[ + { + "_name": "producingClient", + "_connections":[ + { + "_name": "connection1", + "_factory": "connectionfactory", + "_sessions": [ + { + "_sessionName": "session1", + "_producers": [ + { + "_name": "participantProducer1", + "_destinationName": "direct://amq.direct//testQueue", + "_numberOfMessages": 9, + "_batchSize": 2, + "_interval": 50 + } + ] + } + ] + } + ] + }, + { + "_name": "consumingClient", + "_connections":[ + { + "_name": "connection1", + "_factory": "connectionfactory", + "_sessions": [ + { + "_sessionName": "session1", + "_consumers": [ + { + "_name": "participantConsumer1", + "_destinationName": "direct://amq.direct//testQueue", + "_numberOfMessages": 3 + } + ] + }, + { + "_sessionName": "session2", + "_consumers": [ + { + "_name": "participantConsumer2", + "_destinationName": "direct://amq.direct//testQueue", + "_numberOfMessages": 3 + } + ] + }, + { + "_sessionName": "session3", + "_consumers": [ + { + "_name": "participantConsumer3", + "_destinationName": "direct://amq.direct//testQueue", + "_numberOfMessages": 3 + } + ] + } + ] + } + ] + } + ] + }] +}
\ No newline at end of file diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/testWithTwoTests.json b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/testWithTwoTests.json new file mode 100644 index 0000000000..4abd7f4feb --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controllerandclient/testWithTwoTests.json @@ -0,0 +1,107 @@ +{ + "_tests":[ + { + "_name": "Test 1"; + "_queues":[ + { + "_name": "direct://amq.direct//testQueue" + } + ]; + "_clients":[ + { + "_name": "producingClient", + "_connections":[ + { + "_name": "connection1", + "_factory": "connectionfactory", + "_sessions": [ + { + "_sessionName": "session1", + "_producers": [ + { + "_name": "participantProducer1", + "_destinationName": "direct://amq.direct//testQueue", + "_numberOfMessages": 1 + } + ] + } + ] + } + ] + }, + { + "_name": "consumingClient", + "_connections":[ + { + "_name": "connection1", + "_factory": "connectionfactory", + "_sessions": [ + { + "_sessionName": "session1", + "_consumers": [ + { + "_name": "participantconsumer1", + "_destinationName": "direct://amq.direct//testQueue", + "_numberOfMessages": 1 + } + ] + } + ] + } + ] + } + ] + }, + { + "_name": "Test 2"; + "_queues":[ + { + "_name": "direct://amq.direct//testQueue2" + } + ]; + "_clients":[ + { + "_name": "producingClient", + "_connections":[ + { + "_name": "connection1", + "_factory": "connectionfactory", + "_sessions": [ + { + "_sessionName": "session1", + "_producers": [ + { + "_name": "participantProducer2", + "_destinationName": "direct://amq.direct//testQueue2", + "_numberOfMessages": 1 + } + ] + } + ] + } + ] + }, + { + "_name": "consumingClient", + "_connections":[ + { + "_name": "connection1", + "_factory": "connectionfactory", + "_sessions": [ + { + "_sessionName": "session1", + "_consumers": [ + { + "_name": "participantConsumer2", + "_destinationName": "direct://amq.direct//testQueue2", + "_numberOfMessages": 1 + } + ] + } + ] + } + ] + } + ] + }] +}
\ No newline at end of file diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controlleronly/DistributedControllerTest.java b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controlleronly/DistributedControllerTest.java new file mode 100644 index 0000000000..ad7f0e0682 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controlleronly/DistributedControllerTest.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.systest.disttest.controlleronly; + +import static org.apache.qpid.systest.disttest.SystemTestConstants.COMMAND_RESPONSE_TIMEOUT; +import static org.apache.qpid.systest.disttest.SystemTestConstants.REGISTRATION_TIMEOUT; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +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.TemporaryQueue; + +import org.apache.qpid.disttest.ConfigFileHelper; +import org.apache.qpid.disttest.controller.Controller; +import org.apache.qpid.disttest.controller.config.Config; +import org.apache.qpid.disttest.jms.ControllerJmsDelegate; +import org.apache.qpid.disttest.jms.JmsMessageAdaptor; +import org.apache.qpid.disttest.message.Command; +import org.apache.qpid.disttest.message.CommandType; +import org.apache.qpid.disttest.message.RegisterClientCommand; +import org.apache.qpid.disttest.message.Response; +import org.apache.qpid.systest.disttest.DistributedTestSystemTestBase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DistributedControllerTest extends DistributedTestSystemTestBase +{ + private static final Logger LOGGER = LoggerFactory.getLogger(DistributedControllerTest.class); + + private static final String CLIENT1 = "client1"; + private Controller _controller = null; + private Session _session = null; + private Connection _connection = null; + private Destination _controllerQueue = null; + private TemporaryQueue _clientQueue = null; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + _controllerQueue = (Destination) _context.lookup("controllerqueue"); + + final ConnectionFactory connectionFactory = (ConnectionFactory) _context.lookup("connectionfactory"); + _connection = connectionFactory.createConnection(); + _connection.start(); + _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + _clientQueue = _session.createTemporaryQueue(); + + _controller = new Controller(new ControllerJmsDelegate(_context), REGISTRATION_TIMEOUT, COMMAND_RESPONSE_TIMEOUT); + } + + @Override + protected void tearDown() throws Exception + { + try + { + if (_connection != null) + { + _connection.close(); + } + } + finally + { + super.tearDown(); + } + } + + public void testControllerSendsOneCommandToSingleClient() throws Exception + { + Config config = ConfigFileHelper.getConfigFromResource(getClass(), "distributedControllerTest.json"); + _controller.setConfig(config); + + sendRegistration(CLIENT1); + _controller.awaitClientRegistrations(); + + final ArrayBlockingQueue<Command> commandList = new ArrayBlockingQueue<Command>(4); + final MessageConsumer clientConsumer = _session.createConsumer(_clientQueue); + final AtomicReference<Exception> listenerException = new AtomicReference<Exception>(); + final MessageProducer producer = _session.createProducer(_controllerQueue); + clientConsumer.setMessageListener(new MessageListener() + { + @Override + public void onMessage(Message message) + { + try + { + Command command = JmsMessageAdaptor.messageToCommand(message); + LOGGER.debug("Test client received " + command); + commandList.add(command); + producer.send(JmsMessageAdaptor.commandToMessage(_session, new Response(CLIENT1, command.getType()))); + } + catch(Exception e) + { + listenerException.set(e); + } + } + }); + + _controller.runAllTests(); + assertCommandType(CommandType.CREATE_CONNECTION, commandList); + assertCommandType(CommandType.START_TEST, commandList); + assertCommandType(CommandType.TEAR_DOWN_TEST, commandList); + + _controller.stopAllRegisteredClients(); + assertCommandType(CommandType.STOP_CLIENT, commandList); + assertNull("Unexpected exception occured", listenerException.get()); + Command command = commandList.poll(1l, TimeUnit.SECONDS); + assertNull("Unexpected command is received", command); + } + + private void assertCommandType(CommandType expectedType, BlockingQueue<Command> commandList) throws InterruptedException + { + Command command = commandList.poll(1l, TimeUnit.SECONDS); + assertNotNull("Command of type " + expectedType + " is not received", command); + assertEquals("Unexpected command type", expectedType, command.getType()); + } + + private void sendRegistration(final String clientId) throws JMSException + { + final MessageProducer registrationProducer = _session.createProducer(_controllerQueue); + + final Command command = new RegisterClientCommand(clientId, _clientQueue.getQueueName()); + final Message registrationMessage = JmsMessageAdaptor.commandToMessage(_session, command); + registrationProducer.send(registrationMessage); + LOGGER.debug("sent registrationMessage: " + registrationMessage); + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controlleronly/distributedControllerTest.json b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controlleronly/distributedControllerTest.json new file mode 100644 index 0000000000..b49603ef23 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/controlleronly/distributedControllerTest.json @@ -0,0 +1,17 @@ +{ + "_tests":[ + { + "_name": "Test 1"; + "_clients":[ + { + "_name": "client1", + "_connections":[ + { + "_name": "connection1", + "_factory": "connectionfactory" + } + ] + } + ] + }] +}
\ No newline at end of file diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/endtoend/EndToEndTest.java b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/endtoend/EndToEndTest.java new file mode 100644 index 0000000000..63c9b42858 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/endtoend/EndToEndTest.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.systest.disttest.endtoend; + +import static org.apache.qpid.disttest.AbstractRunner.JNDI_CONFIG_PROP; +import static org.apache.qpid.disttest.ControllerRunner.OUTPUT_DIR_PROP; +import static org.apache.qpid.disttest.ControllerRunner.TEST_CONFIG_PROP; + +import java.io.File; +import java.io.IOException; + +import org.apache.qpid.disttest.ControllerRunner; +import org.apache.qpid.disttest.message.ParticipantAttribute; +import org.apache.qpid.disttest.results.aggregation.TestResultAggregator; +import org.apache.qpid.test.utils.QpidBrokerTestCase; +import org.apache.qpid.util.FileUtils; + +public class EndToEndTest extends QpidBrokerTestCase +{ + private ControllerRunner _runner; + private static final String TEST_CONFIG = "perftests/src/test/java/org/apache/qpid/systest/disttest/endtoend/endtoend.json"; + private static final String JNDI_CONFIG_FILE = "perftests/src/test/java/org/apache/qpid/systest/disttest/perftests.systests.properties"; + + public void testRunner() throws Exception + { + File csvOutputDir = createTemporaryCsvDirectory(); + assertTrue("CSV output dir must not exist",csvOutputDir.isDirectory()); + + final String[] args = new String[] {TEST_CONFIG_PROP + "=" + TEST_CONFIG, + JNDI_CONFIG_PROP + "=" + JNDI_CONFIG_FILE, + OUTPUT_DIR_PROP + "=" + csvOutputDir.getAbsolutePath()}; + _runner = new ControllerRunner(); + _runner.parseArgumentsIntoConfig(args); + _runner.runController(); + + File expectedCsvOutputFile = new File(csvOutputDir, "endtoend.csv"); + assertTrue("CSV output file must exist", expectedCsvOutputFile.exists()); + final String csvContents = FileUtils.readFileAsString(expectedCsvOutputFile); + final String[] csvLines = csvContents.split("\n"); + + int numberOfHeaders = 1; + int numberOfParticipants = 2; + int numberOfSummaries = 3; + + int numberOfExpectedRows = numberOfHeaders + numberOfParticipants + numberOfSummaries; + assertEquals("Unexpected number of lines in CSV", numberOfExpectedRows, csvLines.length); + + assertDataRowsHaveCorrectTestAndClientName("End To End 1", "producingClient", "participantProducer1", csvLines[1], 1); + assertDataRowsHaveCorrectTestAndClientName("End To End 1", "consumingClient", "participantConsumer1", csvLines[2], 1); + + assertDataRowsHaveCorrectTestAndClientName("End To End 1", "", TestResultAggregator.ALL_PARTICIPANTS_NAME, csvLines[3], 1); + assertDataRowsHaveCorrectTestAndClientName("End To End 1", "", TestResultAggregator.ALL_CONSUMER_PARTICIPANTS_NAME, csvLines[4], 1); + assertDataRowsHaveCorrectTestAndClientName("End To End 1", "", TestResultAggregator.ALL_PRODUCER_PARTICIPANTS_NAME, csvLines[5], 1); + + } + + private void assertDataRowsHaveCorrectTestAndClientName(String testName, String clientName, String participantName, String csvLine, int expectedNumberOfMessagesProcessed) + { + final int DONT_STRIP_EMPTY_LAST_FIELD_FLAG = -1; + String[] cells = csvLine.split(",", DONT_STRIP_EMPTY_LAST_FIELD_FLAG); + // All attributes become cells in the CSV, so this will be true + assertEquals("Unexpected number of cells in CSV line " + csvLine, ParticipantAttribute.values().length, cells.length); + assertEquals("Unexpected test name in CSV line " + csvLine, testName, cells[0]); + assertEquals("Unexpected client name in CSV line " + csvLine, clientName, cells[2]); + assertEquals("Unexpected participant name in CSV line " + csvLine, participantName, cells[3]); + assertEquals("Unexpected number of messages processed in CSV line " + csvLine, String.valueOf(expectedNumberOfMessagesProcessed), cells[4]); + + } + + private File createTemporaryCsvDirectory() throws IOException + { + String tmpDir = System.getProperty("java.io.tmpdir"); + File csvDir = new File(tmpDir, "csv"); + csvDir.mkdir(); + csvDir.deleteOnExit(); + return csvDir; + } + +} diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/endtoend/endtoend.json b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/endtoend/endtoend.json new file mode 100644 index 0000000000..1b7cc51265 --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/endtoend/endtoend.json @@ -0,0 +1,65 @@ +{ + "_tests":[ + { + "_name": "End To End 1"; + "_queues":[ + { + "_name": "direct://amq.direct//testQueue" + } + ]; + "_clients":[ + { + "_name": "producingClient", + "_connections":[ + { + "_name": "connection1", + "_factory": "connectionfactory", + "_sessions": [ + { + "_sessionName": "session1", + "_producers": [ + { + "_name": "participantProducer1", + "_destinationName": "direct://amq.direct//testQueue", + "_numberOfMessages": 1 + } + ] + } + ]; + "_messageProviders":[ + { + "_name": "testProvider1"; + "_messageProperties": { + "priority": {"@def": "list"; "_items": [1,2,3,4,4]}; + "id": {"@def": "random"; "_upper": 10}; + "test": "test-value" + } + } + ] + } + ] + }, + { + "_name": "consumingClient", + "_connections":[ + { + "_name": "connection1", + "_factory": "connectionfactory", + "_sessions": [ + { + "_sessionName": "session1", + "_consumers": [ + { + "_name": "participantConsumer1", + "_destinationName": "direct://amq.direct//testQueue", + "_numberOfMessages": 1 + } + ] + } + ] + } + ] + } + ] + }] +}
\ No newline at end of file diff --git a/java/perftests/src/test/java/org/apache/qpid/systest/disttest/perftests.systests.properties b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/perftests.systests.properties new file mode 100644 index 0000000000..b5d053227c --- /dev/null +++ b/java/perftests/src/test/java/org/apache/qpid/systest/disttest/perftests.systests.properties @@ -0,0 +1,26 @@ +# 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. + +# this file is used for running system tests of the performance test framework, +# (i.e. not for running the performance tests themselves!) + +java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory + +# use QpidBrokerTestCase's default port +connectionfactory.connectionfactory = amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:15672' + +destination.controllerqueue = direct://amq.direct//controllerqueue |
