You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by or...@apache.org on 2014/10/23 18:03:46 UTC
svn commit: r1633860 - in /qpid/trunk/qpid/java/tools: ./
src/main/java/org/apache/qpid/tools/
src/main/java/org/apache/qpid/tools/util/
Author: orudyy
Date: Thu Oct 23 16:03:46 2014
New Revision: 1633860
URL: http://svn.apache.org/r1633860
Log:
QPID-6158: Stress test utilities for testing of java broker REST and JMX interfaces
Added:
qpid/trunk/qpid/java/tools/src/main/java/org/apache/qpid/tools/JMXStressTestClient.java
qpid/trunk/qpid/java/tools/src/main/java/org/apache/qpid/tools/RestStressTestClient.java
qpid/trunk/qpid/java/tools/src/main/java/org/apache/qpid/tools/util/
qpid/trunk/qpid/java/tools/src/main/java/org/apache/qpid/tools/util/ArgumentsParser.java
Modified:
qpid/trunk/qpid/java/tools/pom.xml
Modified: qpid/trunk/qpid/java/tools/pom.xml
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/tools/pom.xml?rev=1633860&r1=1633859&r2=1633860&view=diff
==============================================================================
--- qpid/trunk/qpid/java/tools/pom.xml (original)
+++ qpid/trunk/qpid/java/tools/pom.xml Thu Oct 23 16:03:46 2014
@@ -52,6 +52,24 @@
<artifactId>geronimo-jms_1.1_spec</artifactId>
<version>${geronimo-jms-1-1-version}</version>
</dependency>
+
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>${commons-codec-version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ <version>${jackson-version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ <version>${jackson-version}</version>
+ </dependency>
</dependencies>
<build>
Added: qpid/trunk/qpid/java/tools/src/main/java/org/apache/qpid/tools/JMXStressTestClient.java
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/tools/src/main/java/org/apache/qpid/tools/JMXStressTestClient.java?rev=1633860&view=auto
==============================================================================
--- qpid/trunk/qpid/java/tools/src/main/java/org/apache/qpid/tools/JMXStressTestClient.java (added)
+++ qpid/trunk/qpid/java/tools/src/main/java/org/apache/qpid/tools/JMXStressTestClient.java Thu Oct 23 16:03:46 2014
@@ -0,0 +1,329 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.tools;
+
+
+import javax.management.MBeanServerConnection;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.qpid.tools.util.ArgumentsParser;
+
+public class JMXStressTestClient
+{
+
+ public static void main(String[] args) throws Exception
+ {
+ ArgumentsParser parser = new ArgumentsParser();
+ Arguments arguments;
+ try
+ {
+ arguments = parser.parse(args, Arguments.class);
+ arguments.validate();
+ }
+ catch(IllegalArgumentException e)
+ {
+ System.out.println("Invalid argument:" + e.getMessage());
+ parser.usage(Arguments.class, Arguments.REQUIRED);
+ System.out.println("\nRun example:");
+ System.out.println(" java -cp qpid-tools.jar org.apache.qpid.tools.JMXStressTestClient \\");
+ System.out.println(" repetitions=10 host=localhost port=8999 username=admin password=admin \\");
+ System.out.println(" virtualHost=default createQueue=true bindQueue=true deleteQueue=true \\");
+ System.out.println(" uniqueQueues=true queueName=boo exchangeName=amq.fanout");
+ return;
+ }
+
+ JMXStressTestClient client = new JMXStressTestClient();
+ client.run(arguments);
+ }
+
+ public void run(Arguments arguments) throws IOException,MalformedObjectNameException
+ {
+ log(arguments.toString());
+ for (int i = 0; i < arguments.getRepetitions(); i++)
+ {
+ try(JMXConnector connector = createConnector(arguments.getHost(), arguments.getPort(), arguments.getUsername(), arguments.getPassword()))
+ {
+ runIteration(arguments, connector, i);
+ }
+ }
+ }
+
+ private void runIteration(Arguments arguments, JMXConnector connector, int iteration) throws IOException, MalformedObjectNameException
+ {
+ log("Iteration " + iteration);
+ MBeanServerConnection connection = connector.getMBeanServerConnection();
+ String virtualHost = arguments.getVirtualHost();
+ if (virtualHost != null)
+ {
+ ObjectName virtualHostMBeanName = new ObjectName("org.apache.qpid:type=VirtualHost.VirtualHostManager,VirtualHost="
+ + ObjectName.quote(virtualHost));
+
+ Set<ObjectName> virtualHostMBeans = connection.queryNames(virtualHostMBeanName, null);
+ if(virtualHostMBeans.size() == 0)
+ {
+ throw new IllegalArgumentException("VirtualHost MBean was not found for virtual host " + virtualHost);
+ }
+
+ createAndBindQueueIfRequired(arguments, iteration, connection, virtualHostMBeanName);
+ }
+ }
+
+ private void log(String logMessage)
+ {
+ System.out.println(logMessage);
+ }
+
+ private void createAndBindQueueIfRequired(Arguments arguments, int iteration, MBeanServerConnection connection,
+ ObjectName virtualHostMBeanName) throws MalformedObjectNameException, IOException
+ {
+ if (arguments.isCreateQueue())
+ {
+ String queueName = arguments.getQueueName();
+
+ if (queueName == null)
+ {
+ queueName = "temp-queue-" + System.nanoTime();
+ }
+ else if (arguments.isUniqueQueues())
+ {
+ queueName = queueName + "-" + iteration;
+ }
+
+ createQueue(connection, virtualHostMBeanName, queueName);
+
+ if (arguments.isBindQueue())
+ {
+ bindQueue(connection, arguments.getVirtualHost(), queueName, arguments.getExchangeName());
+ }
+
+ if (arguments.isDeleteQueue())
+ {
+ deleteQueue(connection, virtualHostMBeanName, queueName);
+ }
+ }
+ }
+
+ private void deleteQueue(MBeanServerConnection connection, ObjectName virtualHostMBeanName, String queueName)
+ {
+ log(" Delete queue " + queueName);
+ try
+ {
+ connection.invoke(virtualHostMBeanName, "deleteQueue", new Object[]{queueName}, new String[]{String.class.getName()});
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Cannot delete queue " + queueName, e);
+ }
+ }
+
+ private void createQueue(MBeanServerConnection connection, ObjectName virtualHostMBeanName, String queueName)
+ {
+ log(" Create queue " + queueName);
+ try
+ {
+ connection.invoke(virtualHostMBeanName, "createNewQueue", new Object[]{queueName, null, true},
+ new String[]{String.class.getName(), String.class.getName(), boolean.class.getName()});
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Cannot create queue " + queueName, e);
+ }
+ }
+
+ private void bindQueue(MBeanServerConnection connection, String virtualHost, String queueName, String exchangeName)
+ throws MalformedObjectNameException, IOException
+ {
+ if (exchangeName == null)
+ {
+ exchangeName = "amq.direct";
+ }
+
+ log(" Bind queue " + queueName + " to " + exchangeName + " using binding key " + queueName);
+
+ ObjectName exchangeObjectName = new ObjectName("org.apache.qpid:type=VirtualHost.Exchange,VirtualHost="
+ + ObjectName.quote(virtualHost) + ","
+ + "name=" + ObjectName.quote(exchangeName) + ",ExchangeType=*");
+
+ Set<ObjectName> exchanges = connection.queryNames(exchangeObjectName, null);
+
+ if(exchanges.size() == 0)
+ {
+ throw new IllegalArgumentException("Cannot find exchange MBean for exchange " + exchangeName);
+ }
+
+ try
+ {
+ connection.invoke(exchanges.iterator().next(), "createNewBinding", new Object[]{queueName, queueName},
+ new String[]{String.class.getName(), String.class.getName()});
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Cannot delete queue " + queueName, e);
+ }
+ }
+
+ JMXConnector createConnector(String host, int port, String username, String password) throws IOException
+ {
+ Map<String, Object> env = new HashMap<>();
+ JMXServiceURL jmxUrl = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + host + ":" + port + "/jmxrmi");
+ env.put(JMXConnector.CREDENTIALS, new String[] {username,password});
+
+ return JMXConnectorFactory.connect(jmxUrl, env);
+ }
+
+ public static class Arguments
+ {
+ private static final Set<String> REQUIRED = new HashSet<>(Arrays.asList("host", "port", "username", "password"));
+
+ private String host = null;
+ private int port = -1;
+ private String username = null;
+ private String password = null;
+
+ private String virtualHost = null;
+ private String queueName = null;
+ private String exchangeName = null;
+
+ private int repetitions = 1;
+
+ private boolean createQueue = false;
+ private boolean deleteQueue = false;
+ private boolean uniqueQueues = false;
+ private boolean bindQueue = false;
+
+ public Arguments()
+ {
+ }
+
+ public void validate()
+ {
+ if (host == null || host.equals(""))
+ {
+ throw new IllegalArgumentException("Mandatory argument 'host' is not specified");
+ }
+
+ if (port == -1)
+ {
+ throw new IllegalArgumentException("Mandatory argument 'port' is not specified");
+ }
+
+ if (username == null || username.equals(""))
+ {
+ throw new IllegalArgumentException("Mandatory argument 'username' is not specified");
+ }
+
+ if (password == null || password.equals(""))
+ {
+ throw new IllegalArgumentException("Mandatory argument 'password' is not specified");
+ }
+ }
+
+ public int getRepetitions()
+ {
+ return repetitions;
+ }
+
+ public String getHost()
+ {
+ return host;
+ }
+
+ public int getPort()
+ {
+ return port;
+ }
+
+ public String getUsername()
+ {
+ return username;
+ }
+
+ public String getPassword()
+ {
+ return password;
+ }
+
+ public String getVirtualHost()
+ {
+ return virtualHost;
+ }
+
+ public boolean isCreateQueue()
+ {
+ return createQueue;
+ }
+
+ public boolean isDeleteQueue()
+ {
+ return deleteQueue;
+ }
+
+ public boolean isUniqueQueues()
+ {
+ return uniqueQueues;
+ }
+
+ public String getQueueName()
+ {
+ return queueName;
+ }
+
+ public boolean isBindQueue()
+ {
+ return bindQueue;
+ }
+
+ public String getExchangeName()
+ {
+ return exchangeName;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Arguments{" +
+ "host='" + host + '\'' +
+ ", port=" + port +
+ ", username='" + username + '\'' +
+ ", password='" + password + '\'' +
+ ", virtualHost='" + virtualHost + '\'' +
+ ", queueName='" + queueName + '\'' +
+ ", exchangeName='" + exchangeName + '\'' +
+ ", repetitions=" + repetitions +
+ ", createQueue=" + createQueue +
+ ", deleteQueue=" + deleteQueue +
+ ", uniqueQueues=" + uniqueQueues +
+ ", bindQueue=" + bindQueue +
+ '}';
+ }
+ }
+
+}
Added: qpid/trunk/qpid/java/tools/src/main/java/org/apache/qpid/tools/RestStressTestClient.java
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/tools/src/main/java/org/apache/qpid/tools/RestStressTestClient.java?rev=1633860&view=auto
==============================================================================
--- qpid/trunk/qpid/java/tools/src/main/java/org/apache/qpid/tools/RestStressTestClient.java (added)
+++ qpid/trunk/qpid/java/tools/src/main/java/org/apache/qpid/tools/RestStressTestClient.java Thu Oct 23 16:03:46 2014
@@ -0,0 +1,667 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.tools;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.qpid.tools.util.ArgumentsParser;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.type.TypeReference;
+
+public class RestStressTestClient
+{
+
+ public static void main(String[] args) throws Exception
+ {
+ ArgumentsParser parser = new ArgumentsParser();
+ Arguments arguments;
+ try
+ {
+ arguments = parser.parse(args, Arguments.class);
+ arguments.validate();
+ }
+ catch(IllegalArgumentException e)
+ {
+ System.out.println("Invalid argument:" + e.getMessage());
+ parser.usage(Arguments.class, Arguments.REQUIRED);
+ System.out.println("\nRun examples:" );
+ System.out.println(" Using Basic authentication:" );
+ System.out.println(" java -cp qpid-tools.jar:commons-codec.jar:jackson-core.jar:jackson-mapper.jar \\" );
+ System.out.println(" -Djavax.net.ssl.trustStore=java_client_truststore.jks \\");
+ System.out.println(" -Djavax.net.ssl.trustStorePassword=password \\");
+ System.out.println(" org.apache.qpid.tools.RestStressTestClient \\");
+ System.out.println(" repetitions=10 brokerUrl=https://localhost:8081 username=admin password=admin \\");
+ System.out.println(" virtualHost=default virtualHostNode=default createQueue=true bindQueue=true \\");
+ System.out.println(" deleteQueue=true uniqueQueues=true queueName=boo exchangeName=amq.fanout" );
+ System.out.println(" Using CRAM-MD5 SASL authentication:" );
+ System.out.println(" java -cp qpid-tools.jar:commons-codec.jar:jackson-core.jar:jackson-mapper.jar \\" );
+ System.out.println(" org.apache.qpid.tools.RestStressTestClient saslMechanism=CRAM-MD5 \\");
+ System.out.println(" repetitions=10 brokerUrl=http://localhost:8080 username=admin password=admin \\");
+ System.out.println(" virtualHost=default virtualHostNode=default createQueue=true bindQueue=true \\");
+ System.out.println(" deleteQueue=true uniqueQueues=true queueName=boo exchangeName=amq.fanout" );
+ return;
+ }
+
+ RestStressTestClient client = new RestStressTestClient();
+ client.run(arguments);
+ }
+
+ public void run(Arguments arguments) throws IOException
+ {
+ log(arguments.toString());
+ for (int i = 0; i < arguments.getRepetitions(); i++)
+ {
+ runIteration(arguments, i);
+ }
+ }
+
+ private void runIteration(Arguments arguments, int iteration) throws IOException
+ {
+ log("Iteration " + iteration);
+
+ RestClient client = new RestClient(arguments.getBrokerUrl(), arguments.getUsername(), arguments.getPassword(), arguments.getSaslMechanism());
+ client.authenticateIfSaslAuthenticationRequested();
+ try
+ {
+ List<Map<String, Object>> brokerData = client.get("/api/latest/broker?depth=0");
+ log(" Connected to broker " + brokerData.get(0).get("name"));
+ createAndBindQueueIfRequired(arguments, client, iteration);
+ }
+ finally
+ {
+ if (arguments.isLogout())
+ {
+ client.logout();
+ }
+ }
+ }
+
+ private void log(String logMessage)
+ {
+ System.out.println(logMessage);
+ }
+
+ private void createAndBindQueueIfRequired(Arguments arguments, RestClient client, int iteration) throws IOException
+ {
+ if (arguments.isCreateQueue())
+ {
+ String virtualHostNode = arguments.getVirtualHostNode();
+ String virtualHost = arguments.getVirtualHost();
+ String queueName = arguments.getQueueName();
+
+ if (queueName == null)
+ {
+ queueName = "temp-queue-" + System.nanoTime();
+ }
+ else if (arguments.isUniqueQueues())
+ {
+ queueName = queueName + "-" + iteration;
+ }
+
+ createQueue(client, virtualHostNode, virtualHost, queueName);
+
+ if (arguments.isBindQueue())
+ {
+ bindQueue(client, virtualHostNode, virtualHost, queueName, arguments.getExchangeName());
+ }
+
+ if (arguments.isDeleteQueue())
+ {
+ deleteQueue(client, virtualHostNode, virtualHost, queueName);
+ }
+ }
+ }
+
+ private void createQueue(RestClient client, String virtualHostNode, String virtualHost, String queueName) throws IOException
+ {
+ log(" Create queue " + queueName);
+
+ String queueUrl = getQueueServiceUrl(virtualHostNode, virtualHost, queueName);
+ Map<String, Object> queueData = new HashMap<>();
+ queueData.put("name", queueName);
+ queueData.put("durable", true);
+
+ int result = client.put(queueUrl, queueData);
+
+ if (result != RestClient.RESPONSE_PUT_CREATE_OK)
+ {
+ throw new RuntimeException("Failure to create queue " + queueName);
+ }
+ }
+
+ private String getQueueServiceUrl(String virtualHostNode, String virtualHost, String queueName)
+ {
+ return "/api/latest/queue/" + virtualHostNode + "/" + virtualHost + "/" + queueName;
+ }
+
+ private void deleteQueue(RestClient client, String virtualHostNode, String virtualHost, String queueName) throws IOException
+ {
+ log(" Delete queue " + queueName);
+ int result = client.delete(getQueueServiceUrl(virtualHostNode, virtualHost, queueName));
+ if (result != RestClient.RESPONSE_PUT_UPDATE_OK)
+ {
+ throw new RuntimeException("Failure to delete queue " + queueName);
+ }
+ }
+
+ private void bindQueue(RestClient client, String virtualHostNode, String virtualHost, String queueName, String exchangeName)
+ throws IOException
+ {
+ if (exchangeName == null)
+ {
+ exchangeName = "amq.direct";
+ }
+
+ log(" Bind queue " + queueName + " to " + exchangeName + " using binding key " + queueName);
+
+ String bindingUrl = "/api/latest/binding/" + virtualHostNode + "/" + virtualHost + "/" + exchangeName + "/" + queueName + "/" + queueName;
+
+ Map<String, Object> bindingData = new HashMap<>();
+ bindingData.put("name", queueName);
+ bindingData.put("queue", queueName);
+ bindingData.put("exchange", exchangeName);
+
+ int result = client.put(bindingUrl, bindingData);
+
+ if (result != RestClient.RESPONSE_PUT_CREATE_OK)
+ {
+ throw new RuntimeException("Failure to bind queue " + queueName + " to " + exchangeName);
+ }
+ }
+
+ public static class RestClient
+ {
+ private static final TypeReference<List<LinkedHashMap<String, Object>>> TYPE_LIST_OF_LINKED_HASH_MAPS = new TypeReference<List<LinkedHashMap<String, Object>>>()
+ {
+ };
+
+ private static final TypeReference<LinkedHashMap<String, Object>> TYPE_HASH_MAP = new TypeReference<LinkedHashMap<String, Object>>()
+ {
+ };
+
+ public static final int RESPONSE_PUT_CREATE_OK = 201;
+ public static final int RESPONSE_PUT_UPDATE_OK = 200;
+ public static final int RESPONSE_OK = 200;
+ public static final int RESPONSE_AUTHENTICATION_REQUIRED = 401;
+
+ private final ObjectMapper _mapper;
+ private final String _brokerUrl;
+ private final String _username;
+ private final String _password;
+ private final String _saslMechanism;
+ private final String _authorizationHeader;
+
+ private List<String> _cookies;
+
+ public RestClient(String brokerUrl, String username, String password, String saslMechanism)
+ {
+ _mapper = new ObjectMapper();
+ _brokerUrl = brokerUrl;
+ _username = username;
+ _password = password;
+ _saslMechanism = saslMechanism;
+
+ if (saslMechanism == null)
+ {
+ _authorizationHeader = "Basic " + new String(new Base64().encode((_username + ":" + _password).getBytes()));
+ }
+ else
+ {
+ _authorizationHeader = null;
+ }
+ }
+
+ public List<Map<String, Object>> get(String restServiceUrl) throws IOException
+ {
+ HttpURLConnection connection = createConnection("GET", restServiceUrl, _cookies);
+ try
+ {
+ connection.connect();
+ byte[] data = readConnectionInputStream(connection);
+ checkResponseCode(connection);
+ return _mapper.readValue(new ByteArrayInputStream(data), TYPE_LIST_OF_LINKED_HASH_MAPS);
+ }
+ finally
+ {
+ connection.disconnect();
+ }
+ }
+
+ public int put(String restServiceUrl, Map<String, Object> attributes) throws IOException
+ {
+ HttpURLConnection connection = createConnection("PUT", restServiceUrl, _cookies);
+ try
+ {
+ connection.connect();
+ if (attributes != null)
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.writeValue(connection.getOutputStream(), attributes);
+ }
+ checkResponseCode(connection);
+ return connection.getResponseCode();
+ }
+ finally
+ {
+ connection.disconnect();
+ }
+ }
+
+ public int delete(String restServiceUrl) throws IOException
+ {
+ HttpURLConnection connection = createConnection("DELETE", restServiceUrl, _cookies);
+ try
+ {
+ checkResponseCode(connection);
+ return connection.getResponseCode();
+ }
+ finally
+ {
+ connection.disconnect();
+ }
+ }
+
+ public int post(String restServiceUrl, Map<String, String> postData) throws IOException
+ {
+ HttpURLConnection connection = createConnectionAndPostData(restServiceUrl, postData, _cookies);
+ try
+ {
+ checkResponseCode(connection);
+ return connection.getResponseCode();
+ }
+ finally
+ {
+ connection.disconnect();
+ }
+ }
+
+ private HttpURLConnection createConnectionAndPostData(String restServiceUrl, Map<String, String> postData, List<String> cookies) throws IOException
+ {
+ String postParameters = getPostDataString(postData);
+ HttpURLConnection connection = createConnection("POST", restServiceUrl, cookies);
+ try
+ {
+ OutputStream os = connection.getOutputStream();
+ os.write(postParameters.getBytes());
+ os.flush();
+ }
+ catch (IOException e)
+ {
+ connection.disconnect();
+ throw e;
+ }
+ return connection;
+ }
+
+ private void checkResponseCode(HttpURLConnection connection) throws IOException
+ {
+ if (connection.getResponseCode() == RESPONSE_AUTHENTICATION_REQUIRED)
+ {
+ _cookies = null;
+ throw new IllegalArgumentException("Authentication is required");
+ }
+ }
+
+ private String getPostDataString(Map<String, String> postData)
+ {
+ StringBuilder sb = new StringBuilder();
+ if (postData != null)
+ {
+ Iterator<String> iterator = postData.keySet().iterator();
+ while (iterator.hasNext())
+ {
+ String key = iterator.next();
+ sb.append(key + "=" + postData.get(key));
+ if (iterator.hasNext())
+ {
+ sb.append("&");
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ private HttpURLConnection createConnection(String method, String restServiceUrl, List<String> cookies) throws IOException
+ {
+ HttpURLConnection httpConnection = (HttpURLConnection) new URL(_brokerUrl + restServiceUrl).openConnection();
+ if (cookies != null)
+ {
+ for (String cookie : cookies)
+ {
+ httpConnection.addRequestProperty("Cookie", cookie.split(";", 2)[0]);
+ }
+ }
+ if (_saslMechanism == null)
+ {
+ httpConnection.setRequestProperty("Authorization", _authorizationHeader);
+ }
+
+ httpConnection.setDoOutput(true);
+ httpConnection.setRequestMethod(method);
+ return httpConnection;
+ }
+
+ public void authenticateIfSaslAuthenticationRequested() throws IOException
+ {
+ if (_saslMechanism == null)
+ {
+ // basic authentication will be used with each request
+ }
+ else if ("CRAM-MD5".equals(_saslMechanism))
+ {
+ _cookies = performCramMD5Authentication();
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unsupported SASL mechanism :" + _saslMechanism);
+ }
+ }
+
+
+ public void logout() throws IOException
+ {
+ if (_cookies != null)
+ {
+ HttpURLConnection connection = createConnection("GET", "/service/logout", _cookies);
+ try
+ {
+ connection.connect();
+ _cookies = null;
+ }
+ finally
+ {
+ connection.disconnect();
+ }
+ }
+
+ //TODO: we need to track sessions for basic auth in order to logout those
+ }
+
+ private List<String> performCramMD5Authentication() throws IOException
+ {
+ // request the challenge for CRAM-MD5
+ HttpURLConnection connection = createConnectionAndPostData("/service/sasl", Collections.singletonMap("mechanism", "CRAM-MD5"), null);
+ try
+ {
+ List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
+
+ // get response
+ byte[] data = readConnectionInputStream(connection);
+ Map<String, Object> response = _mapper.readValue(new ByteArrayInputStream(data), TYPE_HASH_MAP);
+ String challenge = (String) response.get("challenge");
+
+ // generate the authentication response for the received challenge
+ String responseData = generateResponseForChallengeAndCredentials(challenge, _username, _password);
+
+ Map<String, String> saslResponse = new HashMap<>();
+ saslResponse.put("id", (String)response.get("id"));
+ saslResponse.put("response", responseData);
+
+ HttpURLConnection authenticateConnection = createConnectionAndPostData("/service/sasl", saslResponse, cookies);
+ try
+ {
+ int code = authenticateConnection.getResponseCode();
+ if (code != RESPONSE_OK)
+ {
+ throw new RuntimeException("Authentication failed");
+ }
+ else
+ {
+ return cookies;
+ }
+ }
+ finally
+ {
+ authenticateConnection.disconnect();
+ }
+ }
+ finally
+ {
+ connection.disconnect();
+ }
+ }
+
+ private String generateResponseForChallengeAndCredentials(String challenge, String username, String password)
+ {
+ try
+ {
+ byte[] challengeBytes = Base64.decodeBase64(challenge);
+
+ String macAlgorithm = "HmacMD5";
+ Mac mac = Mac.getInstance(macAlgorithm);
+ mac.init(new SecretKeySpec(password.getBytes("UTF-8"), macAlgorithm));
+ final byte[] messageAuthenticationCode = mac.doFinal(challengeBytes);
+ String responseAsString = username + " " + toHex(messageAuthenticationCode);
+ byte[] responseBytes = responseAsString.getBytes();
+ return Base64.encodeBase64String(responseBytes);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("Unexpected exception", e);
+ }
+ }
+
+ private String toHex(byte[] data)
+ {
+ StringBuffer hash = new StringBuffer();
+ for (int i = 0; i < data.length; i++)
+ {
+ String hex = Integer.toHexString(0xFF & data[i]);
+ if (hex.length() == 1)
+ {
+ hash.append('0');
+ }
+ hash.append(hex);
+ }
+ return hash.toString();
+ }
+
+ private byte[] readConnectionInputStream(HttpURLConnection connection) throws IOException
+ {
+ if (connection.getResponseCode() == RESPONSE_AUTHENTICATION_REQUIRED)
+ {
+ _cookies = null;
+ }
+ InputStream is = connection.getInputStream();
+ try(ByteArrayOutputStream baos = new ByteArrayOutputStream())
+ {
+ byte[] buffer = new byte[1024];
+ int len;
+ while ((len = is.read(buffer)) != -1)
+ {
+ baos.write(buffer, 0, len);
+ }
+ return baos.toByteArray();
+ }
+ }
+
+ }
+
+ public static class Arguments
+ {
+ private static final Set<String> REQUIRED = new HashSet<>(Arrays.asList("brokerUrl", "username", "password"));
+
+ private String brokerUrl = null;
+ private String username = null;
+ private String password = null;
+ private String saslMechanism = null;
+
+ private String virtualHostNode = null;
+ private String virtualHost = null;
+ private String queueName = null;
+ private String exchangeName = null;
+
+ private int repetitions = 1;
+
+ private boolean createQueue = false;
+ private boolean deleteQueue = false;
+ private boolean uniqueQueues = false;
+ private boolean bindQueue = false;
+
+ private boolean logout = true;
+
+ public Arguments()
+ {
+ }
+
+ public void validate()
+ {
+ if (brokerUrl == null || brokerUrl.equals(""))
+ {
+ throw new IllegalArgumentException("Mandatory argument 'brokerUrl' is not specified");
+ }
+
+ if (username == null || username.equals(""))
+ {
+ throw new IllegalArgumentException("Mandatory argument 'username' is not specified");
+ }
+
+ if (password == null || password.equals(""))
+ {
+ throw new IllegalArgumentException("Mandatory argument 'password' is not specified");
+ }
+
+ if (createQueue)
+ {
+ if (virtualHostNode == null || virtualHostNode.equals(""))
+ {
+ throw new IllegalArgumentException("Virtual host node name needs to be specified for queue creation");
+ }
+
+ if (virtualHost == null || virtualHost.equals(""))
+ {
+ throw new IllegalArgumentException("Virtual host name needs to be specified for queue creation");
+ }
+ }
+ }
+
+ public String getUsername()
+ {
+ return username;
+ }
+
+ public String getPassword()
+ {
+ return password;
+ }
+
+ public String getVirtualHost()
+ {
+ return virtualHost;
+ }
+
+ public boolean isCreateQueue()
+ {
+ return createQueue;
+ }
+
+ public boolean isDeleteQueue()
+ {
+ return deleteQueue;
+ }
+
+ public boolean isUniqueQueues()
+ {
+ return uniqueQueues;
+ }
+
+ public String getQueueName()
+ {
+ return queueName;
+ }
+
+ public boolean isBindQueue()
+ {
+ return bindQueue;
+ }
+
+ public String getExchangeName()
+ {
+ return exchangeName;
+ }
+
+ public String getVirtualHostNode()
+ {
+ return virtualHostNode;
+ }
+
+
+ public int getRepetitions()
+ {
+ return repetitions;
+ }
+
+ public String getBrokerUrl()
+ {
+ return brokerUrl;
+ }
+
+ public String getSaslMechanism()
+ {
+ return saslMechanism;
+ }
+
+ public boolean isLogout()
+ {
+ return logout;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Arguments{" +
+ "brokerUrl='" + brokerUrl + '\'' +
+ ", username='" + username + '\'' +
+ ", password='" + password + '\'' +
+ ", saslMechanism='" + saslMechanism + '\'' +
+ ", virtualHostNode='" + virtualHostNode + '\'' +
+ ", virtualHost='" + virtualHost + '\'' +
+ ", queueName='" + queueName + '\'' +
+ ", exchangeName='" + exchangeName + '\'' +
+ ", repetitions=" + repetitions +
+ ", createQueue=" + createQueue +
+ ", deleteQueue=" + deleteQueue +
+ ", uniqueQueues=" + uniqueQueues +
+ ", bindQueue=" + bindQueue +
+ ", logout=" + logout +
+ '}';
+ }
+ }
+
+}
Added: qpid/trunk/qpid/java/tools/src/main/java/org/apache/qpid/tools/util/ArgumentsParser.java
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/tools/src/main/java/org/apache/qpid/tools/util/ArgumentsParser.java?rev=1633860&view=auto
==============================================================================
--- qpid/trunk/qpid/java/tools/src/main/java/org/apache/qpid/tools/util/ArgumentsParser.java (added)
+++ qpid/trunk/qpid/java/tools/src/main/java/org/apache/qpid/tools/util/ArgumentsParser.java Thu Oct 23 16:03:46 2014
@@ -0,0 +1,152 @@
+package org.apache.qpid.tools.util;
+
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Set;
+
+public class ArgumentsParser
+{
+ public ArgumentsParser()
+ {
+ }
+
+ public <T> T parse(String[] args, Class<T> pojoClass)
+ {
+ T object;
+ try
+ {
+ object = pojoClass.newInstance();
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("Cannot instantiate object of class " + pojoClass, e);
+ }
+
+ for (String arg: args)
+ {
+ int pos = arg.indexOf('=');
+ if (pos == -1)
+ {
+ throw new IllegalArgumentException("Invalid argument '" + arg + "' Argument should be specified in format <name>=<value>");
+ }
+ String name = arg.substring(0, pos);
+ String value = arg.substring(pos + 1);
+
+ Field field = findField(pojoClass, name);
+ if (field != null)
+ {
+ setField(object, field, value);
+ }
+ }
+ return object;
+ }
+
+ private Field findField(Class<?> objectClass, String name)
+ {
+ Field[] fields = objectClass.getDeclaredFields();
+
+ Field field = null;
+ for (int i = 0 ; i< fields.length ; i++)
+ {
+ if (fields[i].getName().equals(name) && !Modifier.isFinal(fields[i].getModifiers()))
+ {
+ field = fields[i];
+ break;
+ }
+ }
+ return field;
+ }
+
+ private void setField(Object object, Field field, String value)
+ {
+ Object convertedValue = convertStringToType(value, field.getType(), field.getName());
+
+ field.setAccessible(true);
+
+ try
+ {
+ field.set(object, convertedValue);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new RuntimeException("Cannot access field " + field.getName());
+ }
+ }
+
+ private Object convertStringToType(String value, Class<?> fieldType, String fieldName)
+ {
+ Object o;
+ if (fieldType == String.class)
+ {
+ o = value;
+ }
+ else if (fieldType == boolean.class)
+ {
+ try
+ {
+ o = Boolean.parseBoolean(value);
+ }
+ catch(Exception e)
+ {
+ throw new RuntimeException("Cannot convert to boolean argument " + fieldName);
+ }
+ }
+ else if (fieldType == int.class)
+ {
+ try
+ {
+ o = Integer.parseInt(value);
+ }
+ catch(Exception e)
+ {
+ throw new RuntimeException("Cannot convert to int argument " + fieldName);
+ }
+ }
+ else
+ {
+ throw new RuntimeException("Unsupported tye " + fieldType + " in " + fieldName);
+ }
+ return o;
+ }
+
+ public void usage(Class<?> objectClass, Set<String> requiredFields)
+ {
+ System.out.println("Supported arguments:");
+ Field[] fields = objectClass.getDeclaredFields();
+
+ Object object = null;
+ try
+ {
+ object = objectClass.newInstance();
+ }
+ catch(Exception e)
+ {
+ // ignore any
+ }
+
+ for (int i = 0 ; i< fields.length ; i++)
+ {
+ Field field = fields[i];
+ if (!Modifier.isFinal(field.getModifiers()))
+ {
+ Object defaultValue = null;
+ try
+ {
+ field.setAccessible(true);
+ defaultValue = field.get(object);
+ }
+ catch(Exception e)
+ {
+ // ignore any
+ }
+
+ System.out.println(" " + field.getName() + " ( type: "
+ + field.getType().getSimpleName().toLowerCase()
+ + (object != null ? ", default: " + defaultValue : "")
+ + (requiredFields != null && requiredFields.contains(field.getName()) ? ", mandatory" : "")
+ + ")");
+ }
+ }
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org