You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by an...@apache.org on 2016/07/20 09:35:37 UTC

[5/9] activemq-artemis git commit: ARTEMIS-637 Port 5.x AMQP test client

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/df41a60e/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/IdGenerator.java
----------------------------------------------------------------------
diff --git a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/IdGenerator.java b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/IdGenerator.java
new file mode 100644
index 0000000..c662b59
--- /dev/null
+++ b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/IdGenerator.java
@@ -0,0 +1,274 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.transport.amqp.client.util;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.UnknownHostException;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Generator for Globally unique Strings.
+ */
+public class IdGenerator {
+
+   private static final Logger LOG = LoggerFactory.getLogger(IdGenerator.class);
+   private static final String UNIQUE_STUB;
+   private static int instanceCount;
+   private static String hostName;
+   private String seed;
+   private final AtomicLong sequence = new AtomicLong(1);
+   private int length;
+   public static final String PROPERTY_IDGENERATOR_PORT = "activemq.idgenerator.port";
+
+   static {
+      String stub = "";
+      boolean canAccessSystemProps = true;
+      try {
+         SecurityManager sm = System.getSecurityManager();
+         if (sm != null) {
+            sm.checkPropertiesAccess();
+         }
+      }
+      catch (SecurityException se) {
+         canAccessSystemProps = false;
+      }
+
+      if (canAccessSystemProps) {
+         int idGeneratorPort = 0;
+         ServerSocket ss = null;
+         try {
+            idGeneratorPort = Integer.parseInt(System.getProperty(PROPERTY_IDGENERATOR_PORT, "0"));
+            LOG.trace("Using port {}", idGeneratorPort);
+            hostName = getLocalHostName();
+            ss = new ServerSocket(idGeneratorPort);
+            stub = "-" + ss.getLocalPort() + "-" + System.currentTimeMillis() + "-";
+            Thread.sleep(100);
+         }
+         catch (Exception e) {
+            if (LOG.isTraceEnabled()) {
+               LOG.trace("could not generate unique stub by using DNS and binding to local port", e);
+            }
+            else {
+               LOG.warn("could not generate unique stub by using DNS and binding to local port: {} {}", e.getClass().getCanonicalName(), e.getMessage());
+            }
+
+            // Restore interrupted state so higher level code can deal with it.
+            if (e instanceof InterruptedException) {
+               Thread.currentThread().interrupt();
+            }
+         }
+         finally {
+            if (ss != null) {
+               try {
+                  ss.close();
+               }
+               catch (IOException ioe) {
+                  if (LOG.isTraceEnabled()) {
+                     LOG.trace("Closing the server socket failed", ioe);
+                  }
+                  else {
+                     LOG.warn("Closing the server socket failed" + " due " + ioe.getMessage());
+                  }
+               }
+            }
+         }
+      }
+
+      if (hostName == null) {
+         hostName = "localhost";
+      }
+      hostName = sanitizeHostName(hostName);
+
+      if (stub.length() == 0) {
+         stub = "-1-" + System.currentTimeMillis() + "-";
+      }
+      UNIQUE_STUB = stub;
+   }
+
+   /**
+    * Construct an IdGenerator
+    *
+    * @param prefix The prefix value that is applied to all generated IDs.
+    */
+   public IdGenerator(String prefix) {
+      synchronized (UNIQUE_STUB) {
+         this.seed = prefix + UNIQUE_STUB + (instanceCount++) + ":";
+         this.length = this.seed.length() + ("" + Long.MAX_VALUE).length();
+      }
+   }
+
+   public IdGenerator() {
+      this("ID:" + hostName);
+   }
+
+   /**
+    * As we have to find the host name as a side-affect of generating a unique stub, we allow
+    * it's easy retrieval here
+    *
+    * @return the local host name
+    */
+   public static String getHostName() {
+      return hostName;
+   }
+
+   /**
+    * Generate a unique id
+    *
+    * @return a unique id
+    */
+   public synchronized String generateId() {
+      StringBuilder sb = new StringBuilder(length);
+      sb.append(seed);
+      sb.append(sequence.getAndIncrement());
+      return sb.toString();
+   }
+
+   public static String sanitizeHostName(String hostName) {
+      boolean changed = false;
+
+      StringBuilder sb = new StringBuilder();
+      for (char ch : hostName.toCharArray()) {
+         // only include ASCII chars
+         if (ch < 127) {
+            sb.append(ch);
+         }
+         else {
+            changed = true;
+         }
+      }
+
+      if (changed) {
+         String newHost = sb.toString();
+         LOG.info("Sanitized hostname from: {} to: {}", hostName, newHost);
+         return newHost;
+      }
+      else {
+         return hostName;
+      }
+   }
+
+   /**
+    * Generate a unique ID - that is friendly for a URL or file system
+    *
+    * @return a unique id
+    */
+   public String generateSanitizedId() {
+      String result = generateId();
+      result = result.replace(':', '-');
+      result = result.replace('_', '-');
+      result = result.replace('.', '-');
+      return result;
+   }
+
+   /**
+    * From a generated id - return the seed (i.e. minus the count)
+    *
+    * @param id the generated identifier
+    * @return the seed
+    */
+   public static String getSeedFromId(String id) {
+      String result = id;
+      if (id != null) {
+         int index = id.lastIndexOf(':');
+         if (index > 0 && (index + 1) < id.length()) {
+            result = id.substring(0, index);
+         }
+      }
+      return result;
+   }
+
+   /**
+    * From a generated id - return the generator count
+    *
+    * @param id The ID that will be parsed for a sequence number.
+    * @return the sequence value parsed from the given ID.
+    */
+   public static long getSequenceFromId(String id) {
+      long result = -1;
+      if (id != null) {
+         int index = id.lastIndexOf(':');
+
+         if (index > 0 && (index + 1) < id.length()) {
+            String numStr = id.substring(index + 1, id.length());
+            result = Long.parseLong(numStr);
+         }
+      }
+      return result;
+   }
+
+   /**
+    * Does a proper compare on the Id's
+    *
+    * @param id1 the lhs of the comparison.
+    * @param id2 the rhs of the comparison.
+    * @return 0 if equal else a positive if {@literal id1 > id2} ...
+    */
+   public static int compare(String id1, String id2) {
+      int result = -1;
+      String seed1 = IdGenerator.getSeedFromId(id1);
+      String seed2 = IdGenerator.getSeedFromId(id2);
+      if (seed1 != null && seed2 != null) {
+         result = seed1.compareTo(seed2);
+         if (result == 0) {
+            long count1 = IdGenerator.getSequenceFromId(id1);
+            long count2 = IdGenerator.getSequenceFromId(id2);
+            result = (int) (count1 - count2);
+         }
+      }
+      return result;
+   }
+
+   /**
+    * When using the {@link java.net.InetAddress#getHostName()} method in an
+    * environment where neither a proper DNS lookup nor an <tt>/etc/hosts</tt>
+    * entry exists for a given host, the following exception will be thrown:
+    * <code>
+    * java.net.UnknownHostException: &lt;hostname&gt;: &lt;hostname&gt;
+    * at java.net.InetAddress.getLocalHost(InetAddress.java:1425)
+    * ...
+    * </code>
+    * Instead of just throwing an UnknownHostException and giving up, this
+    * method grabs a suitable hostname from the exception and prevents the
+    * exception from being thrown. If a suitable hostname cannot be acquired
+    * from the exception, only then is the <tt>UnknownHostException</tt> thrown.
+    *
+    * @return The hostname
+    * @throws UnknownHostException if the given host cannot be looked up.
+    * @see java.net.InetAddress#getLocalHost()
+    * @see java.net.InetAddress#getHostName()
+    */
+   protected static String getLocalHostName() throws UnknownHostException {
+      try {
+         return (InetAddress.getLocalHost()).getHostName();
+      }
+      catch (UnknownHostException uhe) {
+         String host = uhe.getMessage(); // host = "hostname: hostname"
+         if (host != null) {
+            int colon = host.indexOf(':');
+            if (colon > 0) {
+               return host.substring(0, colon);
+            }
+         }
+         throw uhe;
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/df41a60e/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/NoOpAsyncResult.java
----------------------------------------------------------------------
diff --git a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/NoOpAsyncResult.java b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/NoOpAsyncResult.java
new file mode 100644
index 0000000..5dd4d12
--- /dev/null
+++ b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/NoOpAsyncResult.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.activemq.transport.amqp.client.util;
+
+/**
+ * Simple NoOp implementation used when the result of the operation does not matter.
+ */
+public class NoOpAsyncResult implements AsyncResult {
+
+   public static final NoOpAsyncResult INSTANCE = new NoOpAsyncResult();
+
+   @Override
+   public void onFailure(Throwable result) {
+
+   }
+
+   @Override
+   public void onSuccess() {
+
+   }
+
+   @Override
+   public boolean isComplete() {
+      return true;
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/df41a60e/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/PropertyUtil.java
----------------------------------------------------------------------
diff --git a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/PropertyUtil.java b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/PropertyUtil.java
new file mode 100644
index 0000000..1285a0f
--- /dev/null
+++ b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/PropertyUtil.java
@@ -0,0 +1,533 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.transport.amqp.client.util;
+
+import javax.net.ssl.SSLContext;
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+
+/**
+ * Utilities for properties
+ */
+public class PropertyUtil {
+
+   /**
+    * Creates a URI from the original URI and the given parameters.
+    *
+    * @param originalURI The URI whose current parameters are removed and replaced with the given remainder value.
+    * @param params      The URI params that should be used to replace the current ones in the target.
+    * @return a new URI that matches the original one but has its query options replaced with
+    * the given ones.
+    * @throws URISyntaxException if the given URI is invalid.
+    */
+   public static URI replaceQuery(URI originalURI, Map<String, String> params) throws URISyntaxException {
+      String s = createQueryString(params);
+      if (s.length() == 0) {
+         s = null;
+      }
+      return replaceQuery(originalURI, s);
+   }
+
+   /**
+    * Creates a URI with the given query, removing an previous query value from the given URI.
+    *
+    * @param uri   The source URI whose existing query is replaced with the newly supplied one.
+    * @param query The new URI query string that should be appended to the given URI.
+    * @return a new URI that is a combination of the original URI and the given query string.
+    * @throws URISyntaxException if the given URI is invalid.
+    */
+   public static URI replaceQuery(URI uri, String query) throws URISyntaxException {
+      String schemeSpecificPart = uri.getRawSchemeSpecificPart();
+      // strip existing query if any
+      int questionMark = schemeSpecificPart.lastIndexOf("?");
+      // make sure question mark is not within parentheses
+      if (questionMark < schemeSpecificPart.lastIndexOf(")")) {
+         questionMark = -1;
+      }
+      if (questionMark > 0) {
+         schemeSpecificPart = schemeSpecificPart.substring(0, questionMark);
+      }
+      if (query != null && query.length() > 0) {
+         schemeSpecificPart += "?" + query;
+      }
+      return new URI(uri.getScheme(), schemeSpecificPart, uri.getFragment());
+   }
+
+   /**
+    * Creates a URI with the given query, removing an previous query value from the given URI.
+    *
+    * @param uri The source URI whose existing query is replaced with the newly supplied one.
+    * @return a new URI that is a combination of the original URI and the given query string.
+    * @throws URISyntaxException if the given URI is invalid.
+    */
+   public static URI eraseQuery(URI uri) throws URISyntaxException {
+      return replaceQuery(uri, (String) null);
+   }
+
+   /**
+    * Given a key / value mapping, create and return a URI formatted query string that is valid
+    * and can be appended to a URI.
+    *
+    * @param options The Mapping that will create the new Query string.
+    * @return a URI formatted query string.
+    * @throws URISyntaxException if the given URI is invalid.
+    */
+   public static String createQueryString(Map<String, ?> options) throws URISyntaxException {
+      try {
+         if (options.size() > 0) {
+            StringBuffer rc = new StringBuffer();
+            boolean first = true;
+            for (Entry<String, ?> entry : options.entrySet()) {
+               if (first) {
+                  first = false;
+               }
+               else {
+                  rc.append("&");
+               }
+               rc.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
+               rc.append("=");
+               rc.append(URLEncoder.encode((String) entry.getValue(), "UTF-8"));
+            }
+            return rc.toString();
+         }
+         else {
+            return "";
+         }
+      }
+      catch (UnsupportedEncodingException e) {
+         throw (URISyntaxException) new URISyntaxException(e.toString(), "Invalid encoding").initCause(e);
+      }
+   }
+
+   /**
+    * Get properties from a URI and return them in a new {@code Map<String, String>} instance.
+    *
+    * If the URI is null or the query string of the URI is null an empty Map is returned.
+    *
+    * @param uri the URI whose parameters are to be parsed.
+    * @return <Code>Map</Code> of properties
+    * @throws Exception if an error occurs while parsing the query options.
+    */
+   public static Map<String, String> parseParameters(URI uri) throws Exception {
+      if (uri == null || uri.getQuery() == null) {
+         return Collections.emptyMap();
+      }
+
+      return parseQuery(stripPrefix(uri.getQuery(), "?"));
+   }
+
+   /**
+    * Parse properties from a named resource -eg. a URI or a simple name e.g.
+    * {@literal foo?name="fred"&size=2}
+    *
+    * @param uri the URI whose parameters are to be parsed.
+    * @return <Code>Map</Code> of properties
+    * @throws Exception if an error occurs while parsing the query options.
+    */
+   public static Map<String, String> parseParameters(String uri) throws Exception {
+      if (uri == null) {
+         return Collections.emptyMap();
+      }
+
+      return parseQuery(stripUpto(uri, '?'));
+   }
+
+   /**
+    * Get properties from a URI query string.
+    *
+    * @param queryString the string value returned from a call to the URI class getQuery method.
+    * @return <Code>Map</Code> of properties from the parsed string.
+    * @throws Exception if an error occurs while parsing the query options.
+    */
+   public static Map<String, String> parseQuery(String queryString) throws Exception {
+      if (queryString != null && !queryString.isEmpty()) {
+         Map<String, String> rc = new HashMap<>();
+         String[] parameters = queryString.split("&");
+         for (int i = 0; i < parameters.length; i++) {
+            int p = parameters[i].indexOf("=");
+            if (p >= 0) {
+               String name = URLDecoder.decode(parameters[i].substring(0, p), "UTF-8");
+               String value = URLDecoder.decode(parameters[i].substring(p + 1), "UTF-8");
+               rc.put(name, value);
+            }
+            else {
+               rc.put(parameters[i], null);
+            }
+         }
+         return rc;
+      }
+
+      return Collections.emptyMap();
+   }
+
+   /**
+    * Given a map of properties, filter out only those prefixed with the given value, the
+    * values filtered are returned in a new Map instance.
+    *
+    * @param properties   The map of properties to filter.
+    * @param optionPrefix The prefix value to use when filtering.
+    * @return a filter map with only values that match the given prefix.
+    */
+   public static Map<String, String> filterProperties(Map<String, String> properties, String optionPrefix) {
+      if (properties == null) {
+         throw new IllegalArgumentException("The given properties object was null.");
+      }
+
+      HashMap<String, String> rc = new HashMap<>(properties.size());
+
+      for (Iterator<Entry<String, String>> iter = properties.entrySet().iterator(); iter.hasNext(); ) {
+         Entry<String, String> entry = iter.next();
+         if (entry.getKey().startsWith(optionPrefix)) {
+            String name = entry.getKey().substring(optionPrefix.length());
+            rc.put(name, entry.getValue());
+            iter.remove();
+         }
+      }
+
+      return rc;
+   }
+
+   /**
+    * Enumerate the properties of the target object and add them as additional entries
+    * to the query string of the given string URI.
+    *
+    * @param uri  The string URI value to append the object properties to.
+    * @param bean The Object whose properties will be added to the target URI.
+    * @return a new String value that is the original URI with the added bean properties.
+    * @throws Exception if an error occurs while enumerating the bean properties.
+    */
+   public static String addPropertiesToURIFromBean(String uri, Object bean) throws Exception {
+      Map<String, String> properties = PropertyUtil.getProperties(bean);
+      return PropertyUtil.addPropertiesToURI(uri, properties);
+   }
+
+   /**
+    * Enumerate the properties of the target object and add them as additional entries
+    * to the query string of the given URI.
+    *
+    * @param uri        The URI value to append the object properties to.
+    * @param properties The Object whose properties will be added to the target URI.
+    * @return a new String value that is the original URI with the added bean properties.
+    * @throws Exception if an error occurs while enumerating the bean properties.
+    */
+   public static String addPropertiesToURI(URI uri, Map<String, String> properties) throws Exception {
+      return addPropertiesToURI(uri.toString(), properties);
+   }
+
+   /**
+    * Append the given properties to the query portion of the given URI.
+    *
+    * @param uri        The string URI value to append the object properties to.
+    * @param properties The properties that will be added to the target URI.
+    * @return a new String value that is the original URI with the added properties.
+    * @throws Exception if an error occurs while building the new URI string.
+    */
+   public static String addPropertiesToURI(String uri, Map<String, String> properties) throws Exception {
+      String result = uri;
+      if (uri != null && properties != null) {
+         StringBuilder base = new StringBuilder(stripBefore(uri, '?'));
+         Map<String, String> map = parseParameters(uri);
+         if (!map.isEmpty()) {
+            map.putAll(properties);
+         }
+         else {
+            map = properties;
+         }
+         if (!map.isEmpty()) {
+            base.append('?');
+            boolean first = true;
+            for (Map.Entry<String, String> entry : map.entrySet()) {
+               if (!first) {
+                  base.append('&');
+               }
+               first = false;
+               base.append(entry.getKey()).append("=").append(entry.getValue());
+            }
+            result = base.toString();
+         }
+      }
+      return result;
+   }
+
+   /**
+    * Set properties on an object using the provided map. The return value
+    * indicates if all properties from the given map were set on the target object.
+    *
+    * @param target     the object whose properties are to be set from the map options.
+    * @param properties the properties that should be applied to the given object.
+    * @return true if all values in the properties map were applied to the target object.
+    */
+   public static Map<String, String> setProperties(Object target, Map<String, String> properties) {
+      if (target == null) {
+         throw new IllegalArgumentException("target object cannot be null");
+      }
+      if (properties == null) {
+         throw new IllegalArgumentException("Given Properties object cannot be null");
+      }
+
+      Map<String, String> unmatched = new HashMap<>();
+
+      for (Map.Entry<String, String> entry : properties.entrySet()) {
+         if (!setProperty(target, entry.getKey(), entry.getValue())) {
+            unmatched.put(entry.getKey(), entry.getValue());
+         }
+      }
+
+      return Collections.unmodifiableMap(unmatched);
+   }
+
+   //TODO: common impl for above and below methods.
+
+   /**
+    * Set properties on an object using the provided Properties object. The return value
+    * indicates if all properties from the given map were set on the target object.
+    *
+    * @param target     the object whose properties are to be set from the map options.
+    * @param properties the properties that should be applied to the given object.
+    * @return an unmodifiable map with any values that could not be applied to the target.
+    */
+   public static Map<String, Object> setProperties(Object target, Properties properties) {
+      if (target == null) {
+         throw new IllegalArgumentException("target object cannot be null");
+      }
+      if (properties == null) {
+         throw new IllegalArgumentException("Given Properties object cannot be null");
+      }
+
+      Map<String, Object> unmatched = new HashMap<>();
+
+      for (Map.Entry<Object, Object> entry : properties.entrySet()) {
+         if (!setProperty(target, (String) entry.getKey(), entry.getValue())) {
+            unmatched.put((String) entry.getKey(), entry.getValue());
+         }
+      }
+
+      return Collections.<String, Object>unmodifiableMap(unmatched);
+   }
+
+   /**
+    * Get properties from an object using reflection.  If the passed object is null an
+    * empty <code>Map</code> is returned.
+    *
+    * @param object the Object whose properties are to be extracted.
+    * @return <Code>Map</Code> of properties extracted from the given object.
+    * @throws Exception if an error occurs while examining the object's properties.
+    */
+   public static Map<String, String> getProperties(Object object) throws Exception {
+      if (object == null) {
+         return Collections.emptyMap();
+      }
+
+      Map<String, String> properties = new LinkedHashMap<>();
+      BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
+      Object[] NULL_ARG = {};
+      PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
+      if (propertyDescriptors != null) {
+         for (int i = 0; i < propertyDescriptors.length; i++) {
+            PropertyDescriptor pd = propertyDescriptors[i];
+            if (pd.getReadMethod() != null && !pd.getName().equals("class") && !pd.getName().equals("properties") && !pd.getName().equals("reference")) {
+               Object value = pd.getReadMethod().invoke(object, NULL_ARG);
+               if (value != null) {
+                  if (value instanceof Boolean || value instanceof Number || value instanceof String || value instanceof URI || value instanceof URL) {
+                     properties.put(pd.getName(), ("" + value));
+                  }
+                  else if (value instanceof SSLContext) {
+                     // ignore this one..
+                  }
+                  else {
+                     Map<String, String> inner = getProperties(value);
+                     for (Map.Entry<String, String> entry : inner.entrySet()) {
+                        properties.put(pd.getName() + "." + entry.getKey(), entry.getValue());
+                     }
+                  }
+               }
+            }
+         }
+      }
+
+      return properties;
+   }
+
+   /**
+    * Find a specific property getter in a given object based on a property name.
+    *
+    * @param object the object to search.
+    * @param name   the property name to search for.
+    * @return the result of invoking the specific property get method.
+    * @throws Exception if an error occurs while searching the object's bean info.
+    */
+   public static Object getProperty(Object object, String name) throws Exception {
+      BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
+      PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
+      if (propertyDescriptors != null) {
+         for (int i = 0; i < propertyDescriptors.length; i++) {
+            PropertyDescriptor pd = propertyDescriptors[i];
+            if (pd.getReadMethod() != null && pd.getName().equals(name)) {
+               return pd.getReadMethod().invoke(object);
+            }
+         }
+      }
+      return null;
+   }
+
+   /**
+    * Set a property named property on a given Object.
+    * <p>
+    * The object is searched for an set method that would match the given named
+    * property and if one is found.  If necessary an attempt will be made to convert
+    * the new value to an acceptable type.
+    *
+    * @param target The object whose property is to be set.
+    * @param name   The name of the property to set.
+    * @param value  The new value to set for the named property.
+    * @return true if the property was able to be set on the target object.
+    */
+   public static boolean setProperty(Object target, String name, Object value) {
+      try {
+         int dotPos = name.indexOf(".");
+         while (dotPos >= 0) {
+            String getterName = name.substring(0, dotPos);
+            target = getProperty(target, getterName);
+            name = name.substring(dotPos + 1);
+            dotPos = name.indexOf(".");
+         }
+
+         Class<?> clazz = target.getClass();
+         Method setter = findSetterMethod(clazz, name);
+         if (setter == null) {
+            return false;
+         }
+         // If the type is null or it matches the needed type, just use the
+         // value directly
+         if (value == null || value.getClass() == setter.getParameterTypes()[0]) {
+            setter.invoke(target, new Object[]{value});
+         }
+         else {
+            setter.invoke(target, new Object[]{convert(value, setter.getParameterTypes()[0])});
+         }
+         return true;
+      }
+      catch (Throwable ignore) {
+         return false;
+      }
+   }
+
+   /**
+    * Return a String minus the given prefix.  If the string does not start
+    * with the given prefix the original string value is returned.
+    *
+    * @param value  The String whose prefix is to be removed.
+    * @param prefix The prefix string to remove from the target string.
+    * @return stripped version of the original input string.
+    */
+   public static String stripPrefix(String value, String prefix) {
+      if (value != null && prefix != null && value.startsWith(prefix)) {
+         return value.substring(prefix.length());
+      }
+      return value;
+   }
+
+   /**
+    * Return a portion of a String value by looking beyond the given
+    * character.
+    *
+    * @param value The string value to split
+    * @param c     The character that marks the split point.
+    * @return the sub-string value starting beyond the given character.
+    */
+   public static String stripUpto(String value, char c) {
+      String result = null;
+      if (value != null) {
+         int index = value.indexOf(c);
+         if (index > 0) {
+            result = value.substring(index + 1);
+         }
+      }
+      return result;
+   }
+
+   /**
+    * Return a String up to and including character
+    *
+    * @param value The string value to split
+    * @param c     The character that marks the start of split point.
+    * @return the sub-string value starting from the given character.
+    */
+   public static String stripBefore(String value, char c) {
+      String result = value;
+      if (value != null) {
+         int index = value.indexOf(c);
+         if (index > 0) {
+            result = value.substring(0, index);
+         }
+      }
+      return result;
+   }
+
+   private static Method findSetterMethod(Class<?> clazz, String name) {
+      // Build the method name.
+      name = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
+      Method[] methods = clazz.getMethods();
+      for (int i = 0; i < methods.length; i++) {
+         Method method = methods[i];
+         Class<?>[] params = method.getParameterTypes();
+         if (method.getName().equals(name) && params.length == 1) {
+            return method;
+         }
+      }
+      return null;
+   }
+
+   private static Object convert(Object value, Class<?> type) throws Exception {
+      if (value == null) {
+         if (boolean.class.isAssignableFrom(type)) {
+            return Boolean.FALSE;
+         }
+         return null;
+      }
+
+      if (type.isAssignableFrom(value.getClass())) {
+         return type.cast(value);
+      }
+
+      // special for String[] as we do not want to use a PropertyEditor for that
+      if (type.isAssignableFrom(String[].class)) {
+         return StringArrayConverter.convertToStringArray(value);
+      }
+
+      if (type == URI.class) {
+         return new URI(value.toString());
+      }
+
+      return TypeConversionSupport.convert(value, type);
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/df41a60e/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/StringArrayConverter.java
----------------------------------------------------------------------
diff --git a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/StringArrayConverter.java b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/StringArrayConverter.java
new file mode 100644
index 0000000..3fc9eb4
--- /dev/null
+++ b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/StringArrayConverter.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.activemq.transport.amqp.client.util;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/**
+ * Class for converting to/from String[] to be used instead of a
+ * {@link java.beans.PropertyEditor} which otherwise causes memory leaks as the
+ * JDK {@link java.beans.PropertyEditorManager} is a static class and has strong
+ * references to classes, causing problems in hot-deployment environments.
+ */
+public class StringArrayConverter {
+
+   public static String[] convertToStringArray(Object value) {
+      if (value == null) {
+         return null;
+      }
+
+      String text = value.toString();
+      if (text == null || text.isEmpty()) {
+         return null;
+      }
+
+      StringTokenizer stok = new StringTokenizer(text, ",");
+      final List<String> list = new ArrayList<>();
+
+      while (stok.hasMoreTokens()) {
+         list.add(stok.nextToken());
+      }
+
+      String[] array = list.toArray(new String[list.size()]);
+      return array;
+   }
+
+   public static String convertToString(String[] value) {
+      if (value == null || value.length == 0) {
+         return null;
+      }
+
+      StringBuffer result = new StringBuffer(String.valueOf(value[0]));
+      for (int i = 1; i < value.length; i++) {
+         result.append(",").append(value[i]);
+      }
+
+      return result.toString();
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/df41a60e/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/TypeConversionSupport.java
----------------------------------------------------------------------
diff --git a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/TypeConversionSupport.java b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/TypeConversionSupport.java
new file mode 100644
index 0000000..7d07551
--- /dev/null
+++ b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/TypeConversionSupport.java
@@ -0,0 +1,218 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.transport.amqp.client.util;
+
+import java.util.Date;
+import java.util.HashMap;
+
+public final class TypeConversionSupport {
+
+   static class ConversionKey {
+
+      final Class<?> from;
+      final Class<?> to;
+      final int hashCode;
+
+      ConversionKey(Class<?> from, Class<?> to) {
+         this.from = from;
+         this.to = to;
+         this.hashCode = from.hashCode() ^ (to.hashCode() << 1);
+      }
+
+      @Override
+      public boolean equals(Object o) {
+         if (this == o) {
+            return true;
+         }
+
+         if (o == null || o.getClass() != this.getClass()) {
+            return false;
+         }
+
+         ConversionKey x = (ConversionKey) o;
+         return x.from == from && x.to == to;
+      }
+
+      @Override
+      public int hashCode() {
+         return hashCode;
+      }
+   }
+
+   interface Converter {
+
+      Object convert(Object value);
+   }
+
+   private static final HashMap<ConversionKey, Converter> CONVERSION_MAP = new HashMap<>();
+
+   static {
+      Converter toStringConverter = new Converter() {
+         @Override
+         public Object convert(Object value) {
+            return value.toString();
+         }
+      };
+      CONVERSION_MAP.put(new ConversionKey(Boolean.class, String.class), toStringConverter);
+      CONVERSION_MAP.put(new ConversionKey(Byte.class, String.class), toStringConverter);
+      CONVERSION_MAP.put(new ConversionKey(Short.class, String.class), toStringConverter);
+      CONVERSION_MAP.put(new ConversionKey(Integer.class, String.class), toStringConverter);
+      CONVERSION_MAP.put(new ConversionKey(Long.class, String.class), toStringConverter);
+      CONVERSION_MAP.put(new ConversionKey(Float.class, String.class), toStringConverter);
+      CONVERSION_MAP.put(new ConversionKey(Double.class, String.class), toStringConverter);
+
+      CONVERSION_MAP.put(new ConversionKey(String.class, Boolean.class), new Converter() {
+         @Override
+         public Object convert(Object value) {
+            return Boolean.valueOf((String) value);
+         }
+      });
+      CONVERSION_MAP.put(new ConversionKey(String.class, Byte.class), new Converter() {
+         @Override
+         public Object convert(Object value) {
+            return Byte.valueOf((String) value);
+         }
+      });
+      CONVERSION_MAP.put(new ConversionKey(String.class, Short.class), new Converter() {
+         @Override
+         public Object convert(Object value) {
+            return Short.valueOf((String) value);
+         }
+      });
+      CONVERSION_MAP.put(new ConversionKey(String.class, Integer.class), new Converter() {
+         @Override
+         public Object convert(Object value) {
+            return Integer.valueOf((String) value);
+         }
+      });
+      CONVERSION_MAP.put(new ConversionKey(String.class, Long.class), new Converter() {
+         @Override
+         public Object convert(Object value) {
+            return Long.valueOf((String) value);
+         }
+      });
+      CONVERSION_MAP.put(new ConversionKey(String.class, Float.class), new Converter() {
+         @Override
+         public Object convert(Object value) {
+            return Float.valueOf((String) value);
+         }
+      });
+      CONVERSION_MAP.put(new ConversionKey(String.class, Double.class), new Converter() {
+         @Override
+         public Object convert(Object value) {
+            return Double.valueOf((String) value);
+         }
+      });
+
+      Converter longConverter = new Converter() {
+         @Override
+         public Object convert(Object value) {
+            return Long.valueOf(((Number) value).longValue());
+         }
+      };
+      CONVERSION_MAP.put(new ConversionKey(Byte.class, Long.class), longConverter);
+      CONVERSION_MAP.put(new ConversionKey(Short.class, Long.class), longConverter);
+      CONVERSION_MAP.put(new ConversionKey(Integer.class, Long.class), longConverter);
+      CONVERSION_MAP.put(new ConversionKey(Date.class, Long.class), new Converter() {
+         @Override
+         public Object convert(Object value) {
+            return Long.valueOf(((Date) value).getTime());
+         }
+      });
+
+      Converter intConverter = new Converter() {
+         @Override
+         public Object convert(Object value) {
+            return Integer.valueOf(((Number) value).intValue());
+         }
+      };
+      CONVERSION_MAP.put(new ConversionKey(Byte.class, Integer.class), intConverter);
+      CONVERSION_MAP.put(new ConversionKey(Short.class, Integer.class), intConverter);
+
+      CONVERSION_MAP.put(new ConversionKey(Byte.class, Short.class), new Converter() {
+         @Override
+         public Object convert(Object value) {
+            return Short.valueOf(((Number) value).shortValue());
+         }
+      });
+
+      CONVERSION_MAP.put(new ConversionKey(Float.class, Double.class), new Converter() {
+         @Override
+         public Object convert(Object value) {
+            return new Double(((Number) value).doubleValue());
+         }
+      });
+   }
+
+   public static Object convert(Object value, Class<?> toClass) {
+
+      assert value != null && toClass != null;
+
+      if (value.getClass() == toClass) {
+         return value;
+      }
+
+      Class<?> fromClass = value.getClass();
+
+      if (fromClass.isPrimitive()) {
+         fromClass = convertPrimitiveTypeToWrapperType(fromClass);
+      }
+
+      if (toClass.isPrimitive()) {
+         toClass = convertPrimitiveTypeToWrapperType(toClass);
+      }
+
+      Converter c = CONVERSION_MAP.get(new ConversionKey(fromClass, toClass));
+      if (c == null) {
+         return null;
+      }
+
+      return c.convert(value);
+   }
+
+   private static Class<?> convertPrimitiveTypeToWrapperType(Class<?> type) {
+      Class<?> rc = type;
+      if (type.isPrimitive()) {
+         if (type == int.class) {
+            rc = Integer.class;
+         }
+         else if (type == long.class) {
+            rc = Long.class;
+         }
+         else if (type == double.class) {
+            rc = Double.class;
+         }
+         else if (type == float.class) {
+            rc = Float.class;
+         }
+         else if (type == short.class) {
+            rc = Short.class;
+         }
+         else if (type == byte.class) {
+            rc = Byte.class;
+         }
+         else if (type == boolean.class) {
+            rc = Boolean.class;
+         }
+      }
+
+      return rc;
+   }
+
+   private TypeConversionSupport() {
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/df41a60e/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableConnection.java
----------------------------------------------------------------------
diff --git a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableConnection.java b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableConnection.java
new file mode 100644
index 0000000..32003a4
--- /dev/null
+++ b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableConnection.java
@@ -0,0 +1,202 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.transport.amqp.client.util;
+
+import java.util.EnumSet;
+import java.util.Map;
+
+import org.apache.qpid.proton.amqp.Symbol;
+import org.apache.qpid.proton.amqp.transport.ErrorCondition;
+import org.apache.qpid.proton.engine.Collector;
+import org.apache.qpid.proton.engine.Connection;
+import org.apache.qpid.proton.engine.Delivery;
+import org.apache.qpid.proton.engine.EndpointState;
+import org.apache.qpid.proton.engine.Link;
+import org.apache.qpid.proton.engine.Record;
+import org.apache.qpid.proton.engine.Session;
+import org.apache.qpid.proton.engine.Transport;
+import org.apache.qpid.proton.reactor.Reactor;
+
+/**
+ * Unmodifiable Connection wrapper used to prevent test code from accidentally
+ * modifying Connection state.
+ */
+public class UnmodifiableConnection implements Connection {
+
+   private final Connection connection;
+
+   public UnmodifiableConnection(Connection connection) {
+      this.connection = connection;
+   }
+
+   @Override
+   public EndpointState getLocalState() {
+      return connection.getLocalState();
+   }
+
+   @Override
+   public EndpointState getRemoteState() {
+      return connection.getRemoteState();
+   }
+
+   @Override
+   public ErrorCondition getCondition() {
+      return connection.getCondition();
+   }
+
+   @Override
+   public void setCondition(ErrorCondition condition) {
+      throw new UnsupportedOperationException("Cannot alter the Connection");
+   }
+
+   @Override
+   public ErrorCondition getRemoteCondition() {
+      return connection.getRemoteCondition();
+   }
+
+   @Override
+   public void free() {
+      throw new UnsupportedOperationException("Cannot alter the Connection");
+   }
+
+   @Override
+   public void open() {
+      throw new UnsupportedOperationException("Cannot alter the Connection");
+   }
+
+   @Override
+   public void close() {
+      throw new UnsupportedOperationException("Cannot alter the Connection");
+   }
+
+   @Override
+   public Session session() {
+      throw new UnsupportedOperationException("Cannot alter the Connection");
+   }
+
+   @Override
+   public Session sessionHead(EnumSet<EndpointState> local, EnumSet<EndpointState> remote) {
+      Session head = connection.sessionHead(local, remote);
+      if (head != null) {
+         head = new UnmodifiableSession(head);
+      }
+
+      return head;
+   }
+
+   @Override
+   public Link linkHead(EnumSet<EndpointState> local, EnumSet<EndpointState> remote) {
+      // TODO - If implemented this method should return an unmodifiable link isntance.
+      return null;
+   }
+
+   @Override
+   public Delivery getWorkHead() {
+      // TODO - If implemented this method should return an unmodifiable delivery isntance.
+      return null;
+   }
+
+   @Override
+   public void setContainer(String container) {
+      throw new UnsupportedOperationException("Cannot alter the Connection");
+   }
+
+   @Override
+   public void setHostname(String hostname) {
+      throw new UnsupportedOperationException("Cannot alter the Connection");
+   }
+
+   @Override
+   public String getHostname() {
+      return connection.getHostname();
+   }
+
+   @Override
+   public String getRemoteContainer() {
+      return connection.getRemoteContainer();
+   }
+
+   @Override
+   public String getRemoteHostname() {
+      return connection.getRemoteHostname();
+   }
+
+   @Override
+   public void setOfferedCapabilities(Symbol[] capabilities) {
+      throw new UnsupportedOperationException("Cannot alter the Connection");
+   }
+
+   @Override
+   public void setDesiredCapabilities(Symbol[] capabilities) {
+      throw new UnsupportedOperationException("Cannot alter the Connection");
+   }
+
+   @Override
+   public Symbol[] getRemoteOfferedCapabilities() {
+      return connection.getRemoteOfferedCapabilities();
+   }
+
+   @Override
+   public Symbol[] getRemoteDesiredCapabilities() {
+      return connection.getRemoteDesiredCapabilities();
+   }
+
+   @Override
+   public Map<Symbol, Object> getRemoteProperties() {
+      return connection.getRemoteProperties();
+   }
+
+   @Override
+   public void setProperties(Map<Symbol, Object> properties) {
+      throw new UnsupportedOperationException("Cannot alter the Connection");
+   }
+
+   @Override
+   public Object getContext() {
+      return connection.getContext();
+   }
+
+   @Override
+   public void setContext(Object context) {
+      throw new UnsupportedOperationException("Cannot alter the Connection");
+   }
+
+   @Override
+   public void collect(Collector collector) {
+      throw new UnsupportedOperationException("Cannot alter the Connection");
+   }
+
+   @Override
+   public String getContainer() {
+      return connection.getContainer();
+   }
+
+   @Override
+   public Transport getTransport() {
+      return new UnmodifiableTransport(connection.getTransport());
+   }
+
+   @Override
+   public Record attachments() {
+      return connection.attachments();
+   }
+
+   @Override
+   public Reactor getReactor() {
+      return connection.getReactor();
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/df41a60e/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableDelivery.java
----------------------------------------------------------------------
diff --git a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableDelivery.java b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableDelivery.java
new file mode 100644
index 0000000..9f48b41
--- /dev/null
+++ b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableDelivery.java
@@ -0,0 +1,170 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.transport.amqp.client.util;
+
+import org.apache.qpid.proton.amqp.transport.DeliveryState;
+import org.apache.qpid.proton.engine.Delivery;
+import org.apache.qpid.proton.engine.Link;
+import org.apache.qpid.proton.engine.Receiver;
+import org.apache.qpid.proton.engine.Record;
+import org.apache.qpid.proton.engine.Sender;
+
+/**
+ * Unmodifiable Delivery wrapper used to prevent test code from accidentally
+ * modifying Delivery state.
+ */
+public class UnmodifiableDelivery implements Delivery {
+
+   private final Delivery delivery;
+
+   public UnmodifiableDelivery(Delivery delivery) {
+      this.delivery = delivery;
+   }
+
+   @Override
+   public byte[] getTag() {
+      return delivery.getTag();
+   }
+
+   @Override
+   public Link getLink() {
+      if (delivery.getLink() instanceof Sender) {
+         return new UnmodifiableSender((Sender) delivery.getLink());
+      }
+      else if (delivery.getLink() instanceof Receiver) {
+         return new UnmodifiableReceiver((Receiver) delivery.getLink());
+      }
+      else {
+         throw new IllegalStateException("Delivery has unknown link type");
+      }
+   }
+
+   @Override
+   public DeliveryState getLocalState() {
+      return delivery.getLocalState();
+   }
+
+   @Override
+   public DeliveryState getRemoteState() {
+      return delivery.getRemoteState();
+   }
+
+   @Override
+   public int getMessageFormat() {
+      return delivery.getMessageFormat();
+   }
+
+   @Override
+   public void disposition(DeliveryState state) {
+      throw new UnsupportedOperationException("Cannot alter the Delivery state");
+   }
+
+   @Override
+   public void settle() {
+      throw new UnsupportedOperationException("Cannot alter the Delivery state");
+   }
+
+   @Override
+   public boolean isSettled() {
+      return delivery.isSettled();
+   }
+
+   @Override
+   public boolean remotelySettled() {
+      return delivery.remotelySettled();
+   }
+
+   @Override
+   public void free() {
+      throw new UnsupportedOperationException("Cannot alter the Delivery state");
+   }
+
+   @Override
+   public Delivery getWorkNext() {
+      return new UnmodifiableDelivery(delivery.getWorkNext());
+   }
+
+   @Override
+   public Delivery next() {
+      return new UnmodifiableDelivery(delivery.next());
+   }
+
+   @Override
+   public boolean isWritable() {
+      return delivery.isWritable();
+   }
+
+   @Override
+   public boolean isReadable() {
+      return delivery.isReadable();
+   }
+
+   @Override
+   public void setContext(Object o) {
+      throw new UnsupportedOperationException("Cannot alter the Delivery state");
+   }
+
+   @Override
+   public Object getContext() {
+      return delivery.getContext();
+   }
+
+   @Override
+   public boolean isUpdated() {
+      return delivery.isUpdated();
+   }
+
+   @Override
+   public void clear() {
+      throw new UnsupportedOperationException("Cannot alter the Delivery state");
+   }
+
+   @Override
+   public boolean isPartial() {
+      return delivery.isPartial();
+   }
+
+   @Override
+   public int pending() {
+      return delivery.pending();
+   }
+
+   @Override
+   public boolean isBuffered() {
+      return delivery.isBuffered();
+   }
+
+   @Override
+   public Record attachments() {
+      return delivery.attachments();
+   }
+
+   @Override
+   public DeliveryState getDefaultDeliveryState() {
+      return delivery.getDefaultDeliveryState();
+   }
+
+   @Override
+   public void setDefaultDeliveryState(DeliveryState state) {
+      throw new UnsupportedOperationException("Cannot alter the Delivery");
+   }
+
+   @Override
+   public void setMessageFormat(int messageFormat) {
+      throw new UnsupportedOperationException("Cannot alter the Delivery");
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/df41a60e/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableLink.java
----------------------------------------------------------------------
diff --git a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableLink.java b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableLink.java
new file mode 100644
index 0000000..a58bfe7
--- /dev/null
+++ b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableLink.java
@@ -0,0 +1,276 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.transport.amqp.client.util;
+
+import java.util.EnumSet;
+import java.util.Map;
+
+import org.apache.qpid.proton.amqp.Symbol;
+import org.apache.qpid.proton.amqp.transport.ErrorCondition;
+import org.apache.qpid.proton.amqp.transport.ReceiverSettleMode;
+import org.apache.qpid.proton.amqp.transport.SenderSettleMode;
+import org.apache.qpid.proton.amqp.transport.Source;
+import org.apache.qpid.proton.amqp.transport.Target;
+import org.apache.qpid.proton.engine.Delivery;
+import org.apache.qpid.proton.engine.EndpointState;
+import org.apache.qpid.proton.engine.Link;
+import org.apache.qpid.proton.engine.Receiver;
+import org.apache.qpid.proton.engine.Record;
+import org.apache.qpid.proton.engine.Sender;
+import org.apache.qpid.proton.engine.Session;
+
+/**
+ * Unmodifiable Session wrapper used to prevent test code from accidentally
+ * modifying Session state.
+ */
+public class UnmodifiableLink implements Link {
+
+   private final Link link;
+
+   public UnmodifiableLink(Link link) {
+      this.link = link;
+   }
+
+   @Override
+   public EndpointState getLocalState() {
+      return link.getLocalState();
+   }
+
+   @Override
+   public EndpointState getRemoteState() {
+      return link.getRemoteState();
+   }
+
+   @Override
+   public ErrorCondition getCondition() {
+      return link.getCondition();
+   }
+
+   @Override
+   public void setCondition(ErrorCondition condition) {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public ErrorCondition getRemoteCondition() {
+      return link.getRemoteCondition();
+   }
+
+   @Override
+   public void free() {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public void open() {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public void close() {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public void setContext(Object o) {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public Object getContext() {
+      return link.getContext();
+   }
+
+   @Override
+   public String getName() {
+      return link.getName();
+   }
+
+   @Override
+   public Delivery delivery(byte[] tag) {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public Delivery delivery(byte[] tag, int offset, int length) {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public Delivery head() {
+      return new UnmodifiableDelivery(link.head());
+   }
+
+   @Override
+   public Delivery current() {
+      return new UnmodifiableDelivery(link.current());
+   }
+
+   @Override
+   public boolean advance() {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public Source getSource() {
+      // TODO Figure out a simple way to wrap the odd Source types in Proton-J
+      return link.getSource();
+   }
+
+   @Override
+   public Target getTarget() {
+      // TODO Figure out a simple way to wrap the odd Source types in Proton-J
+      return link.getTarget();
+   }
+
+   @Override
+   public void setSource(Source address) {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public void setTarget(Target address) {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public Source getRemoteSource() {
+      // TODO Figure out a simple way to wrap the odd Source types in Proton-J
+      return link.getRemoteSource();
+   }
+
+   @Override
+   public Target getRemoteTarget() {
+      // TODO Figure out a simple way to wrap the odd Target types in Proton-J
+      return link.getRemoteTarget();
+   }
+
+   @Override
+   public Link next(EnumSet<EndpointState> local, EnumSet<EndpointState> remote) {
+      Link next = link.next(local, remote);
+
+      if (next != null) {
+         if (next instanceof Sender) {
+            next = new UnmodifiableSender((Sender) next);
+         }
+         else {
+            next = new UnmodifiableReceiver((Receiver) next);
+         }
+      }
+
+      return next;
+   }
+
+   @Override
+   public int getCredit() {
+      return link.getCredit();
+   }
+
+   @Override
+   public int getQueued() {
+      return link.getQueued();
+   }
+
+   @Override
+   public int getUnsettled() {
+      return link.getUnsettled();
+   }
+
+   @Override
+   public Session getSession() {
+      return new UnmodifiableSession(link.getSession());
+   }
+
+   @Override
+   public SenderSettleMode getSenderSettleMode() {
+      return link.getSenderSettleMode();
+   }
+
+   @Override
+   public void setSenderSettleMode(SenderSettleMode senderSettleMode) {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public SenderSettleMode getRemoteSenderSettleMode() {
+      return link.getRemoteSenderSettleMode();
+   }
+
+   @Override
+   public ReceiverSettleMode getReceiverSettleMode() {
+      return link.getReceiverSettleMode();
+   }
+
+   @Override
+   public void setReceiverSettleMode(ReceiverSettleMode receiverSettleMode) {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public ReceiverSettleMode getRemoteReceiverSettleMode() {
+      return link.getRemoteReceiverSettleMode();
+   }
+
+   @Override
+   public void setRemoteSenderSettleMode(SenderSettleMode remoteSenderSettleMode) {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public int drained() {
+      return link.drained();  // TODO - Is this a mutating call?
+   }
+
+   @Override
+   public int getRemoteCredit() {
+      return link.getRemoteCredit();
+   }
+
+   @Override
+   public boolean getDrain() {
+      return link.getDrain();
+   }
+
+   @Override
+   public void detach() {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public boolean detached() {
+      return link.detached();
+   }
+
+   public Record attachments() {
+      return link.attachments();
+   }
+
+   @Override
+   public Map<Symbol, Object> getProperties() {
+      return link.getProperties();
+   }
+
+   @Override
+   public void setProperties(Map<Symbol, Object> properties) {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public Map<Symbol, Object> getRemoteProperties() {
+      return link.getRemoteProperties();
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/df41a60e/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableReceiver.java
----------------------------------------------------------------------
diff --git a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableReceiver.java b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableReceiver.java
new file mode 100644
index 0000000..92760db
--- /dev/null
+++ b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableReceiver.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.transport.amqp.client.util;
+
+import org.apache.qpid.proton.engine.Receiver;
+
+/**
+ * Unmodifiable Receiver wrapper used to prevent test code from accidentally
+ * modifying Receiver state.
+ */
+public class UnmodifiableReceiver extends UnmodifiableLink implements Receiver {
+
+   private final Receiver receiver;
+
+   public UnmodifiableReceiver(Receiver receiver) {
+      super(receiver);
+
+      this.receiver = receiver;
+   }
+
+   @Override
+   public void flow(int credits) {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public int recv(byte[] bytes, int offset, int size) {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public void drain(int credit) {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public boolean draining() {
+      return receiver.draining();
+   }
+
+   @Override
+   public void setDrain(boolean drain) {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/df41a60e/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableSender.java
----------------------------------------------------------------------
diff --git a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableSender.java b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableSender.java
new file mode 100644
index 0000000..89742cb
--- /dev/null
+++ b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableSender.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.activemq.transport.amqp.client.util;
+
+import org.apache.qpid.proton.engine.Sender;
+
+/**
+ * Unmodifiable Sender wrapper used to prevent test code from accidentally
+ * modifying Sender state.
+ */
+public class UnmodifiableSender extends UnmodifiableLink implements Sender {
+
+   public UnmodifiableSender(Sender sender) {
+      super(sender);
+   }
+
+   @Override
+   public void offer(int credits) {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public int send(byte[] bytes, int offset, int length) {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+
+   @Override
+   public void abort() {
+      throw new UnsupportedOperationException("Cannot alter the Link state");
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/df41a60e/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableSession.java
----------------------------------------------------------------------
diff --git a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableSession.java b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableSession.java
new file mode 100644
index 0000000..a44028e
--- /dev/null
+++ b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableSession.java
@@ -0,0 +1,150 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.transport.amqp.client.util;
+
+import java.util.EnumSet;
+
+import org.apache.qpid.proton.amqp.transport.ErrorCondition;
+import org.apache.qpid.proton.engine.Connection;
+import org.apache.qpid.proton.engine.EndpointState;
+import org.apache.qpid.proton.engine.Receiver;
+import org.apache.qpid.proton.engine.Record;
+import org.apache.qpid.proton.engine.Sender;
+import org.apache.qpid.proton.engine.Session;
+
+/**
+ * Unmodifiable Session wrapper used to prevent test code from accidentally
+ * modifying Session state.
+ */
+public class UnmodifiableSession implements Session {
+
+   private final Session session;
+
+   public UnmodifiableSession(Session session) {
+      this.session = session;
+   }
+
+   @Override
+   public EndpointState getLocalState() {
+      return session.getLocalState();
+   }
+
+   @Override
+   public EndpointState getRemoteState() {
+      return session.getRemoteState();
+   }
+
+   @Override
+   public ErrorCondition getCondition() {
+      return session.getCondition();
+   }
+
+   @Override
+   public void setCondition(ErrorCondition condition) {
+      throw new UnsupportedOperationException("Cannot alter the Session");
+   }
+
+   @Override
+   public ErrorCondition getRemoteCondition() {
+      return session.getRemoteCondition();
+   }
+
+   @Override
+   public void free() {
+      throw new UnsupportedOperationException("Cannot alter the Session");
+   }
+
+   @Override
+   public void open() {
+      throw new UnsupportedOperationException("Cannot alter the Session");
+   }
+
+   @Override
+   public void close() {
+      throw new UnsupportedOperationException("Cannot alter the Session");
+   }
+
+   @Override
+   public void setContext(Object o) {
+      throw new UnsupportedOperationException("Cannot alter the Session");
+   }
+
+   @Override
+   public Object getContext() {
+      return session.getContext();
+   }
+
+   @Override
+   public Sender sender(String name) {
+      throw new UnsupportedOperationException("Cannot alter the Session");
+   }
+
+   @Override
+   public Receiver receiver(String name) {
+      throw new UnsupportedOperationException("Cannot alter the Session");
+   }
+
+   @Override
+   public Session next(EnumSet<EndpointState> local, EnumSet<EndpointState> remote) {
+      Session next = session.next(local, remote);
+      if (next != null) {
+         next = new UnmodifiableSession(next);
+      }
+
+      return next;
+   }
+
+   @Override
+   public Connection getConnection() {
+      return new UnmodifiableConnection(session.getConnection());
+   }
+
+   @Override
+   public int getIncomingCapacity() {
+      return session.getIncomingCapacity();
+   }
+
+   @Override
+   public void setIncomingCapacity(int bytes) {
+      throw new UnsupportedOperationException("Cannot alter the Session");
+   }
+
+   @Override
+   public int getIncomingBytes() {
+      return session.getIncomingBytes();
+   }
+
+   @Override
+   public int getOutgoingBytes() {
+      return session.getOutgoingBytes();
+   }
+
+   @Override
+   public Record attachments() {
+      return session.attachments();
+   }
+
+   @Override
+   public long getOutgoingWindow() {
+      return session.getOutgoingWindow();
+   }
+
+   @Override
+   public void setOutgoingWindow(long outgoingWindowSize) {
+      throw new UnsupportedOperationException("Cannot alter the Session");
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/df41a60e/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableTransport.java
----------------------------------------------------------------------
diff --git a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableTransport.java b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableTransport.java
new file mode 100644
index 0000000..5e305f4
--- /dev/null
+++ b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/UnmodifiableTransport.java
@@ -0,0 +1,274 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.transport.amqp.client.util;
+
+import java.nio.ByteBuffer;
+
+import org.apache.qpid.proton.amqp.transport.ErrorCondition;
+import org.apache.qpid.proton.engine.Connection;
+import org.apache.qpid.proton.engine.EndpointState;
+import org.apache.qpid.proton.engine.Record;
+import org.apache.qpid.proton.engine.Sasl;
+import org.apache.qpid.proton.engine.Ssl;
+import org.apache.qpid.proton.engine.SslDomain;
+import org.apache.qpid.proton.engine.SslPeerDetails;
+import org.apache.qpid.proton.engine.Transport;
+import org.apache.qpid.proton.engine.TransportException;
+import org.apache.qpid.proton.engine.TransportResult;
+
+/**
+ * Unmodifiable Transport wrapper used to prevent test code from accidentally
+ * modifying Transport state.
+ */
+public class UnmodifiableTransport implements Transport {
+
+   private final Transport transport;
+
+   public UnmodifiableTransport(Transport transport) {
+      this.transport = transport;
+   }
+
+   @Override
+   public void close() {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public void free() {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public Object getContext() {
+      return null;
+   }
+
+   @Override
+   public EndpointState getLocalState() {
+      return transport.getLocalState();
+   }
+
+   @Override
+   public ErrorCondition getRemoteCondition() {
+      return transport.getRemoteCondition();
+   }
+
+   @Override
+   public EndpointState getRemoteState() {
+      return transport.getRemoteState();
+   }
+
+   @Override
+   public void open() {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public void setCondition(ErrorCondition arg0) {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public void setContext(Object arg0) {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public void bind(Connection arg0) {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public int capacity() {
+      return transport.capacity();
+   }
+
+   @Override
+   public void close_head() {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public void close_tail() {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public int getChannelMax() {
+      return transport.getChannelMax();
+   }
+
+   @Override
+   public ErrorCondition getCondition() {
+      return transport.getCondition();
+   }
+
+   @Override
+   public int getIdleTimeout() {
+      return transport.getIdleTimeout();
+   }
+
+   @Override
+   public ByteBuffer getInputBuffer() {
+      return null;
+   }
+
+   @Override
+   public int getMaxFrameSize() {
+      return transport.getMaxFrameSize();
+   }
+
+   @Override
+   public ByteBuffer getOutputBuffer() {
+      return null;
+   }
+
+   @Override
+   public int getRemoteChannelMax() {
+      return transport.getRemoteChannelMax();
+   }
+
+   @Override
+   public int getRemoteIdleTimeout() {
+      return transport.getRemoteIdleTimeout();
+   }
+
+   @Override
+   public int getRemoteMaxFrameSize() {
+      return transport.getRemoteMaxFrameSize();
+   }
+
+   @Override
+   public ByteBuffer head() {
+      return null;
+   }
+
+   @Override
+   public int input(byte[] arg0, int arg1, int arg2) {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public boolean isClosed() {
+      return transport.isClosed();
+   }
+
+   @Override
+   public int output(byte[] arg0, int arg1, int arg2) {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public void outputConsumed() {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public int pending() {
+      return transport.pending();
+   }
+
+   @Override
+   public void pop(int arg0) {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public void process() throws TransportException {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public TransportResult processInput() {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public Sasl sasl() throws IllegalStateException {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public void setChannelMax(int arg0) {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public void setIdleTimeout(int arg0) {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public void setMaxFrameSize(int arg0) {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public Ssl ssl(SslDomain arg0) {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public Ssl ssl(SslDomain arg0, SslPeerDetails arg1) {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public ByteBuffer tail() {
+      return null;
+   }
+
+   @Override
+   public long tick(long arg0) {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public void trace(int arg0) {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public void unbind() {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public Record attachments() {
+      return transport.attachments();
+   }
+
+   @Override
+   public long getFramesInput() {
+      return transport.getFramesInput();
+   }
+
+   @Override
+   public long getFramesOutput() {
+      return transport.getFramesOutput();
+   }
+
+   @Override
+   public void setEmitFlowEventOnSend(boolean emitFlowEventOnSend) {
+      throw new UnsupportedOperationException("Cannot alter the Transport");
+   }
+
+   @Override
+   public boolean isEmitFlowEventOnSend() {
+      return transport.isEmitFlowEventOnSend();
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/df41a60e/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/WrappedAsyncResult.java
----------------------------------------------------------------------
diff --git a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/WrappedAsyncResult.java b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/WrappedAsyncResult.java
new file mode 100644
index 0000000..bfe9a80
--- /dev/null
+++ b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/util/WrappedAsyncResult.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.transport.amqp.client.util;
+
+/**
+ * Base class used to wrap one AsyncResult with another.
+ */
+public abstract class WrappedAsyncResult implements AsyncResult {
+
+   protected final AsyncResult wrapped;
+
+   /**
+    * Create a new WrappedAsyncResult for the target AsyncResult
+    */
+   public WrappedAsyncResult(AsyncResult wrapped) {
+      this.wrapped = wrapped;
+   }
+
+   @Override
+   public void onFailure(Throwable result) {
+      if (wrapped != null) {
+         wrapped.onFailure(result);
+      }
+   }
+
+   @Override
+   public void onSuccess() {
+      if (wrapped != null) {
+         wrapped.onSuccess();
+      }
+   }
+
+   @Override
+   public boolean isComplete() {
+      if (wrapped != null) {
+         return wrapped.isComplete();
+      }
+
+      return false;
+   }
+
+   public AsyncResult getWrappedRequest() {
+      return wrapped;
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/df41a60e/tests/integration-tests/pom.xml
----------------------------------------------------------------------
diff --git a/tests/integration-tests/pom.xml b/tests/integration-tests/pom.xml
index 752e288..5d7617c 100644
--- a/tests/integration-tests/pom.xml
+++ b/tests/integration-tests/pom.xml
@@ -340,6 +340,11 @@
 		   <artifactId>org.apache.karaf.shell.console</artifactId>
 		   <version>${karaf.version}</version>
 	   </dependency>
+      <dependency>
+         <groupId>org.apache.activemq.tests</groupId>
+         <artifactId>artemis-test-support</artifactId>
+         <version>${project.version}</version>
+      </dependency>
    </dependencies>
 
    <build>

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/df41a60e/tests/pom.xml
----------------------------------------------------------------------
diff --git a/tests/pom.xml b/tests/pom.xml
index 6a9c000..a2efeac 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -46,6 +46,13 @@
             <version>1.2</version>
             <!-- License: Apache: 2.0 -->
          </dependency>
+         <dependency>
+            <groupId>org.apache.qpid</groupId>
+            <artifactId>qpid-jms-client</artifactId>
+            <version>0.10.0</version>
+            <!-- License: Apache: 2.0 -->
+         </dependency>
+
          <!-- End JMS Dependencies -->
       </dependencies>
    </dependencyManagement>
@@ -122,5 +129,6 @@
       <module>soak-tests</module>
       <module>stress-tests</module>
       <module>performance-tests</module>
+      <module>artemis-test-support</module>
    </modules>
 </project>