You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by kw...@apache.org on 2016/06/27 15:21:41 UTC

svn commit: r1750359 - in /qpid/java/trunk: client/src/main/java/org/apache/qpid/client/ client/src/main/java/org/apache/qpid/client/message/ client/src/main/java/org/apache/qpid/client/util/ client/src/main/java/org/apache/qpid/jms/ client/src/test/ja...

Author: kwall
Date: Mon Jun 27 15:21:41 2016
New Revision: 1750359

URL: http://svn.apache.org/viewvc?rev=1750359&view=rev
Log:
QPID-7323: [Java Client] Improvements to the ObjectMessage implementation

Added:
    qpid/java/trunk/client/src/test/java/org/apache/qpid/client/util/SimplePojo.java
    qpid/java/trunk/systests/src/test/java/org/apache/qpid/client/message/JmsObjectMessageTest.java
Modified:
    qpid/java/trunk/client/src/main/java/org/apache/qpid/client/AMQConnection.java
    qpid/java/trunk/client/src/main/java/org/apache/qpid/client/AMQSession.java
    qpid/java/trunk/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java
    qpid/java/trunk/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java
    qpid/java/trunk/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java
    qpid/java/trunk/client/src/main/java/org/apache/qpid/client/util/ClassLoadingAwareObjectInputStream.java
    qpid/java/trunk/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java
    qpid/java/trunk/client/src/test/java/org/apache/qpid/client/message/TestMessageHelper.java
    qpid/java/trunk/client/src/test/java/org/apache/qpid/client/util/ClassLoadingAwareObjectInputStreamTest.java
    qpid/java/trunk/common/src/main/java/org/apache/qpid/configuration/CommonProperties.java

Modified: qpid/java/trunk/client/src/main/java/org/apache/qpid/client/AMQConnection.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/client/src/main/java/org/apache/qpid/client/AMQConnection.java?rev=1750359&r1=1750358&r2=1750359&view=diff
==============================================================================
--- qpid/java/trunk/client/src/main/java/org/apache/qpid/client/AMQConnection.java (original)
+++ qpid/java/trunk/client/src/main/java/org/apache/qpid/client/AMQConnection.java Mon Jun 27 15:21:41 2016
@@ -32,6 +32,7 @@ import java.security.KeyStore;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -58,6 +59,7 @@ import javax.naming.Referenceable;
 import javax.naming.StringRefAddr;
 
 import org.apache.qpid.client.state.AMQState;
+import org.apache.qpid.client.util.ClassLoadingAwareObjectInputStream;
 import org.apache.qpid.jndi.ObjectFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -86,7 +88,8 @@ import org.apache.qpid.protocol.AMQConst
 import org.apache.qpid.transport.ConnectionSettings;
 import org.apache.qpid.url.URLSyntaxException;
 
-public class AMQConnection extends Closeable implements CommonConnection, Referenceable
+public class AMQConnection extends Closeable implements CommonConnection, Referenceable,
+                                                        ClassLoadingAwareObjectInputStream.TrustedClassFilter
 {
     public static final String JNDI_ADDRESS_CONNECTION_URL = "connectionURL";
 
@@ -103,6 +106,9 @@ public class AMQConnection extends Close
 
     private final long _connectionNumber = CONN_NUMBER_GENERATOR.incrementAndGet();
 
+    private final List<String> _whiteListedClassHierarchies;
+    private final List<String> _blackListedClassHierarchies;
+
     /**
      * This is the "root" mutex that must be held when doing anything that could be impacted by failover. This must be
      * held by any child objects of this connection such as the session, producers and consumers.
@@ -504,6 +510,28 @@ public class AMQConnection extends Close
         }
 
         _connectionMetaData = new QpidConnectionMetaData();
+
+        if (connectionURL.getOption(ConnectionURL.OPTIONS_OBJECT_MESSAGE_CLASS_HIERARCHY_WHITE_LIST) != null)
+        {
+            String whiteListedClassHierarchiesString = connectionURL.getOption(ConnectionURL.OPTIONS_OBJECT_MESSAGE_CLASS_HIERARCHY_WHITE_LIST);
+            _whiteListedClassHierarchies = Arrays.asList(whiteListedClassHierarchiesString.split(","));
+        }
+        else
+        {
+            final String defaultWhiteListedClassHierarchiesString = System.getProperty(CommonProperties.QPID_SECURITY_OBJECT_MESSAGE_CLASS_HIERARCHY_WHITE_LIST, "*");
+            _whiteListedClassHierarchies = Arrays.asList(defaultWhiteListedClassHierarchiesString.split(","));
+        }
+
+        if (connectionURL.getOption(ConnectionURL.OPTIONS_OBJECT_MESSAGE_CLASS_HIERARCHY_BLACK_LIST) != null)
+        {
+            String blackListedClassHierarchiesString = connectionURL.getOption(ConnectionURL.OPTIONS_OBJECT_MESSAGE_CLASS_HIERARCHY_BLACK_LIST);
+            _blackListedClassHierarchies = Arrays.asList(blackListedClassHierarchiesString.split(","));
+        }
+        else
+        {
+            final String defaultBlackListedClassHierarchiesString = System.getProperty(CommonProperties.QPID_SECURITY_OBJECT_MESSAGE_CLASS_HIERARCHY_BLACK_LIST, "");
+            _blackListedClassHierarchies = Arrays.asList(defaultBlackListedClassHierarchiesString.split(","));
+        }
     }
 
     private void makeConnection() throws QpidException
@@ -1941,4 +1969,50 @@ public class AMQConnection extends Close
     {
         return _connectionSettings;
     }
+
+    @Override
+    public boolean isTrusted(Class<?> clazz)
+    {
+        while (clazz.isArray())
+        {
+            clazz = clazz.getComponentType();
+        }
+
+        if (clazz.isPrimitive())
+        {
+            return true;
+        }
+
+        while (clazz.isAnonymousClass() || clazz.isLocalClass())
+        {
+            clazz = clazz.getEnclosingClass();
+        }
+
+        String className = clazz.getCanonicalName();
+
+        for (String blackListedClassHierarchy : _blackListedClassHierarchies)
+        {
+            if ("*".equals(blackListedClassHierarchy))
+            {
+                return false;
+            }
+            else if (className != null && (className.equals(blackListedClassHierarchy) || className.startsWith(blackListedClassHierarchy + ".")))
+            {
+                return false;
+            }
+        }
+
+        for (String whiteListedClassHierarchy : _whiteListedClassHierarchies)
+        {
+            if ("*".equals(whiteListedClassHierarchy))
+            {
+                return true;
+            }
+            else if (className != null && (className.equals(whiteListedClassHierarchy) || className.startsWith(whiteListedClassHierarchy + ".")))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
 }

Modified: qpid/java/trunk/client/src/main/java/org/apache/qpid/client/AMQSession.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/client/src/main/java/org/apache/qpid/client/AMQSession.java?rev=1750359&r1=1750358&r2=1750359&view=diff
==============================================================================
--- qpid/java/trunk/client/src/main/java/org/apache/qpid/client/AMQSession.java (original)
+++ qpid/java/trunk/client/src/main/java/org/apache/qpid/client/AMQSession.java Mon Jun 27 15:21:41 2016
@@ -1252,7 +1252,7 @@ public abstract class AMQSession<C exten
     public ObjectMessage createObjectMessage() throws JMSException
     {
         checkNotClosed();
-         JMSObjectMessage msg = new JMSObjectMessage(getMessageDelegateFactory());
+         JMSObjectMessage msg = new JMSObjectMessage(getAMQConnection(), getMessageDelegateFactory());
          msg.setAMQSession(this);
          return msg;
     }

Modified: qpid/java/trunk/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java?rev=1750359&r1=1750358&r2=1750359&view=diff
==============================================================================
--- qpid/java/trunk/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java (original)
+++ qpid/java/trunk/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java Mon Jun 27 15:21:41 2016
@@ -38,6 +38,7 @@ public class JMSObjectMessage extends Ab
 {
     public static final String MIME_TYPE = "application/java-object-stream";
     private static final int DEFAULT_OUTPUT_BUFFER_SIZE = 256;
+    private final ClassLoadingAwareObjectInputStream.TrustedClassFilter _trustedClassFilter;
 
     private Serializable _readData;
     private ByteBuffer _data;
@@ -51,18 +52,20 @@ public class JMSObjectMessage extends Ab
      * Creates empty, writable message for use by producers
      * @param delegateFactory
      */
-    public JMSObjectMessage(AMQMessageDelegateFactory delegateFactory)
+    public JMSObjectMessage(final ClassLoadingAwareObjectInputStream.TrustedClassFilter trustedClassFilter, AMQMessageDelegateFactory delegateFactory)
     {
         super(delegateFactory, false);
+        _trustedClassFilter = trustedClassFilter;
     }
 
     /**
      * Creates read only message for delivery to consumers
      */
 
-      JMSObjectMessage(AMQMessageDelegate delegate, final ByteBuffer data) throws QpidException
+      JMSObjectMessage(final ClassLoadingAwareObjectInputStream.TrustedClassFilter trustedClassFilter, AMQMessageDelegate delegate, final ByteBuffer data) throws QpidException
       {
           super(delegate, data!=null);
+          _trustedClassFilter = trustedClassFilter;
 
           try
           {
@@ -193,15 +196,11 @@ public class JMSObjectMessage extends Ab
         Serializable result = null;
         if (data != null && data.hasRemaining())
         {
-            ClassLoadingAwareObjectInputStream in = new ClassLoadingAwareObjectInputStream(new ByteBufferInputStream(data));
-            try
+            try (ClassLoadingAwareObjectInputStream in = new ClassLoadingAwareObjectInputStream(new ByteBufferInputStream(data),
+                                                                                                _trustedClassFilter))
             {
                 result = (Serializable) in.readObject();
             }
-            finally
-            {
-                in.close();
-            }
         }
         return result;
     }

Modified: qpid/java/trunk/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java?rev=1750359&r1=1750358&r2=1750359&view=diff
==============================================================================
--- qpid/java/trunk/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java (original)
+++ qpid/java/trunk/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java Mon Jun 27 15:21:41 2016
@@ -21,15 +21,23 @@
 package org.apache.qpid.client.message;
 
 import org.apache.qpid.QpidException;
+import org.apache.qpid.client.util.ClassLoadingAwareObjectInputStream;
 
 import java.nio.ByteBuffer;
 
 public class JMSObjectMessageFactory extends AbstractJMSMessageFactory
 {
+    private final ClassLoadingAwareObjectInputStream.TrustedClassFilter _trustedClassFilter;
+
+    public JMSObjectMessageFactory(final ClassLoadingAwareObjectInputStream.TrustedClassFilter trustedClassFilter)
+    {
+        _trustedClassFilter = trustedClassFilter;
+    }
+
     protected AbstractJMSMessage createMessage(AbstractAMQMessageDelegate delegate, ByteBuffer data) throws
                                                                                                      QpidException
     {
-        return new JMSObjectMessage(delegate, data);
+        return new JMSObjectMessage(_trustedClassFilter, delegate, data);
     }
 
 }

Modified: qpid/java/trunk/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java?rev=1750359&r1=1750358&r2=1750359&view=diff
==============================================================================
--- qpid/java/trunk/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java (original)
+++ qpid/java/trunk/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java Mon Jun 27 15:21:41 2016
@@ -71,7 +71,7 @@ public class MessageFactoryRegistry
         mf.registerFactory("text/plain", new JMSTextMessageFactory());
         mf.registerFactory("text/xml", new JMSTextMessageFactory());
         mf.registerFactory(JMSBytesMessage.MIME_TYPE, new JMSBytesMessageFactory());
-        mf.registerFactory(JMSObjectMessage.MIME_TYPE, new JMSObjectMessageFactory());
+        mf.registerFactory(JMSObjectMessage.MIME_TYPE, new JMSObjectMessageFactory(session.getAMQConnection()));
         mf.registerFactory(JMSStreamMessage.MIME_TYPE, new JMSStreamMessageFactory());
         mf.registerFactory(AMQPEncodedMapMessage.MIME_TYPE, new AMQPEncodedMapMessageFactory());
         mf.registerFactory(AMQPEncodedListMessage.MIME_TYPE, new AMQPEncodedListMessageFactory());

Modified: qpid/java/trunk/client/src/main/java/org/apache/qpid/client/util/ClassLoadingAwareObjectInputStream.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/client/src/main/java/org/apache/qpid/client/util/ClassLoadingAwareObjectInputStream.java?rev=1750359&r1=1750358&r2=1750359&view=diff
==============================================================================
--- qpid/java/trunk/client/src/main/java/org/apache/qpid/client/util/ClassLoadingAwareObjectInputStream.java (original)
+++ qpid/java/trunk/client/src/main/java/org/apache/qpid/client/util/ClassLoadingAwareObjectInputStream.java Mon Jun 27 15:21:41 2016
@@ -44,11 +44,21 @@ public class ClassLoadingAwareObjectInpu
 
     /** <p>Maps primitive type names to corresponding class objects.</p> */
     private static final HashMap<String, Class> _primitives = new HashMap<String, Class>(8, 1.0F);
+    private final TrustedClassFilter _securityFilter;
 
+    /**
+     * Security Filter used to filter classes that the application deems to be insecure, this filter
+     * is not applied to the class instances for the primitive types.
+     */
+    public interface TrustedClassFilter
+    {
+        boolean isTrusted(Class<?> clazz);
+    }
 
-    public ClassLoadingAwareObjectInputStream(InputStream in) throws IOException
+    public ClassLoadingAwareObjectInputStream(InputStream in, TrustedClassFilter filter) throws IOException
     {
         super(in);
+        _securityFilter = filter;
     }
 
     @Override
@@ -58,8 +68,8 @@ public class ClassLoadingAwareObjectInpu
 
         // Here we use TTCL as our primary class loader to load the classes
         ClassLoader cl = Thread.currentThread().getContextClassLoader();
-
-        return load(classDesc.getName(), cl);
+        Class<?> clazz = load(classDesc.getName(), cl);
+        return checkSecurity(clazz);
     }
 
     @Override
@@ -75,14 +85,47 @@ public class ClassLoadingAwareObjectInpu
             cinterfaces[i] = load(interfaces[i], cl);
         }
 
+
+        Class<?> clazz = null;
+        Throwable failureCause = null;
+
         try
         {
-            return Proxy.getProxyClass(cinterfaces[0].getClassLoader(), cinterfaces);
+            clazz = Proxy.getProxyClass(cl, cinterfaces);
         }
         catch (IllegalArgumentException e)
         {
-            throw new ClassNotFoundException(null, e);
+            failureCause = e;
+
+            try
+            {
+                clazz = Proxy.getProxyClass(_ON_FAULT_CLASS_LOADER, cinterfaces);
+            }
+            catch (IllegalArgumentException e2)
+            {
+            }
         }
+
+        if (clazz != null)
+        {
+            return checkSecurity(clazz);
+        }
+
+        throw new ClassNotFoundException("Failed find class.", failureCause);
+    }
+
+    private Class<?> checkSecurity(Class<?> clazz) throws ClassNotFoundException
+    {
+        if (!clazz.isPrimitive() && _securityFilter != null)
+        {
+            if (!_securityFilter.isTrusted(clazz))
+            {
+                throw new ClassNotFoundException("Forbidden " + clazz + "! " +
+                                                 "This class is not trusted to be deserialized from ObjectMessage payloads.");
+            }
+        }
+
+        return clazz;
     }
 
     /**

Modified: qpid/java/trunk/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java?rev=1750359&r1=1750358&r2=1750359&view=diff
==============================================================================
--- qpid/java/trunk/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java (original)
+++ qpid/java/trunk/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java Mon Jun 27 15:21:41 2016
@@ -82,12 +82,13 @@ public interface ConnectionURL
     String OPTIONS_TEMPORARY_TOPIC_EXCHANGE = "temporaryTopicExchange";
     String OPTIONS_TEMPORARY_QUEUE_EXCHANGE = "temporaryQueueExchange";
     String OPTIONS_VERIFY_QUEUE_ON_SEND = "verifyQueueOnSend";
+    String OPTIONS_OBJECT_MESSAGE_CLASS_HIERARCHY_WHITE_LIST = "objectMessageClassHierarchyWhiteList";
+    String OPTIONS_OBJECT_MESSAGE_CLASS_HIERARCHY_BLACK_LIST = "objectMessageClassHierarchyBlackList";
 
     /**
      * This option specifies whether User-ID should be attached to each message sent over the connection
      */
     String OPTIONS_POPULATE_USER_ID = "populateJMSXUserID";
-
     byte  URL_0_8 = 1;
     byte  URL_0_10 = 2;
 

Modified: qpid/java/trunk/client/src/test/java/org/apache/qpid/client/message/TestMessageHelper.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/client/src/test/java/org/apache/qpid/client/message/TestMessageHelper.java?rev=1750359&r1=1750358&r2=1750359&view=diff
==============================================================================
--- qpid/java/trunk/client/src/test/java/org/apache/qpid/client/message/TestMessageHelper.java (original)
+++ qpid/java/trunk/client/src/test/java/org/apache/qpid/client/message/TestMessageHelper.java Mon Jun 27 15:21:41 2016
@@ -22,6 +22,8 @@ package org.apache.qpid.client.message;
 
 import javax.jms.JMSException;
 
+import org.apache.qpid.client.util.ClassLoadingAwareObjectInputStream;
+
 public class TestMessageHelper
 {
     public static JMSTextMessage newJMSTextMessage() throws JMSException
@@ -46,6 +48,13 @@ public class TestMessageHelper
 
     public static JMSObjectMessage newJMSObjectMessage()
     {
-        return new JMSObjectMessage(AMQMessageDelegateFactory.FACTORY_0_8);
+        return new JMSObjectMessage(new ClassLoadingAwareObjectInputStream.TrustedClassFilter()
+        {
+            @Override
+            public boolean isTrusted(final Class<?> clazz)
+            {
+                return true;
+            }
+        }, AMQMessageDelegateFactory.FACTORY_0_8);
     }
 }

Modified: qpid/java/trunk/client/src/test/java/org/apache/qpid/client/util/ClassLoadingAwareObjectInputStreamTest.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/client/src/test/java/org/apache/qpid/client/util/ClassLoadingAwareObjectInputStreamTest.java?rev=1750359&r1=1750358&r2=1750359&view=diff
==============================================================================
--- qpid/java/trunk/client/src/test/java/org/apache/qpid/client/util/ClassLoadingAwareObjectInputStreamTest.java (original)
+++ qpid/java/trunk/client/src/test/java/org/apache/qpid/client/util/ClassLoadingAwareObjectInputStreamTest.java Mon Jun 27 15:21:41 2016
@@ -20,17 +20,41 @@
  */
 package org.apache.qpid.client.util;
 
-import org.apache.qpid.test.utils.QpidTestCase;
-
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.ObjectOutputStream;
+import java.lang.reflect.Array;
 import java.util.Arrays;
 import java.util.List;
+import java.util.UUID;
+
+import org.apache.qpid.client.util.ClassLoadingAwareObjectInputStream.TrustedClassFilter;
+import org.apache.qpid.test.utils.QpidTestCase;
 
 public class ClassLoadingAwareObjectInputStreamTest extends QpidTestCase
 {
+    private final TrustedClassFilter ACCEPTS_ALL_FILTER = new TrustedClassFilter()
+    {
+
+        @Override
+        public boolean isTrusted(Class<?> clazz)
+        {
+            return true;
+        }
+    };
+
+    private final TrustedClassFilter ACCEPTS_NONE_FILTER = new TrustedClassFilter()
+    {
+
+        @Override
+        public boolean isTrusted(Class<?> clazz)
+        {
+            return false;
+        }
+    };
+
     private InputStream _in;
     private ClassLoadingAwareObjectInputStream _claOIS;
 
@@ -44,10 +68,16 @@ public class ClassLoadingAwareObjectInpu
         out.flush();
         out.close();
 
-
         _in = new ByteArrayInputStream(baos.toByteArray());
 
-        _claOIS = new ClassLoadingAwareObjectInputStream(_in);
+        _claOIS = new ClassLoadingAwareObjectInputStream(_in, null);
+    }
+
+    @Override
+    public void tearDown() throws Exception
+    {
+        _claOIS.close();
+        super.tearDown();
     }
 
     /**
@@ -77,10 +107,358 @@ public class ClassLoadingAwareObjectInpu
             _claOIS.resolveProxyClass(new String[]{"java.lang.String"});
             fail("should have thrown an exception");
         }
-        catch(ClassNotFoundException cnfe)
+        catch (ClassNotFoundException cnfe)
         {
             //expected, but must verify it is wrapping an IllegalArgumentException
             assertTrue(cnfe.getCause() instanceof IllegalArgumentException);
         }
     }
+
+
+    public void testReadObject() throws Exception
+    {
+        // Expect to succeed
+        doTestReadObject(new SimplePojo("testString"), ACCEPTS_ALL_FILTER);
+
+        // Expect to fail
+        try
+        {
+            doTestReadObject(new SimplePojo("testString"), ACCEPTS_NONE_FILTER);
+            fail("Should have failed to read");
+        }
+        catch (ClassNotFoundException cnfe)
+        {
+            // Expected
+        }
+    }
+
+    public void testReadObjectByte() throws Exception
+    {
+        doTestReadObject(Byte.valueOf((byte) 255), ACCEPTS_ALL_FILTER);
+    }
+
+    public void testReadObjectShort() throws Exception
+    {
+        doTestReadObject(Short.valueOf((short) 255), ACCEPTS_ALL_FILTER);
+    }
+
+    public void testReadObjectInteger() throws Exception
+    {
+        doTestReadObject(Integer.valueOf(255), ACCEPTS_ALL_FILTER);
+    }
+
+    public void testReadObjectLong() throws Exception
+    {
+        doTestReadObject(Long.valueOf(255l), ACCEPTS_ALL_FILTER);
+    }
+
+    public void testReadObjectFloat() throws Exception
+    {
+        doTestReadObject(Float.valueOf(255.0f), ACCEPTS_ALL_FILTER);
+    }
+
+    public void testReadObjectDouble() throws Exception
+    {
+        doTestReadObject(Double.valueOf(255.0), ACCEPTS_ALL_FILTER);
+    }
+
+    public void testReadObjectBoolean() throws Exception
+    {
+        doTestReadObject(Boolean.FALSE, ACCEPTS_ALL_FILTER);
+    }
+
+    public void testReadObjectString() throws Exception
+    {
+        doTestReadObject(new String("testString"), ACCEPTS_ALL_FILTER);
+    }
+
+    public void testReadObjectIntArray() throws Exception
+    {
+        doTestReadObject(new int[]{1, 2, 3}, ACCEPTS_ALL_FILTER);
+    }
+
+    public void testPrimitiveByteNotFiltered() throws Exception
+    {
+        doTestReadPrimitive((byte) 255, ACCEPTS_NONE_FILTER);
+    }
+
+    public void testPrimitiveShortNotFiltered() throws Exception
+    {
+        doTestReadPrimitive((short) 255, ACCEPTS_NONE_FILTER);
+    }
+
+    public void testPrimitiveIntegerNotFiltered() throws Exception
+    {
+        doTestReadPrimitive((int) 255, ACCEPTS_NONE_FILTER);
+    }
+
+    public void testPrimitiveLongNotFiltered() throws Exception
+    {
+        doTestReadPrimitive((long) 255, ACCEPTS_NONE_FILTER);
+    }
+
+    public void testPrimitiveFloatNotFiltered() throws Exception
+    {
+        doTestReadPrimitive((float) 255.0, ACCEPTS_NONE_FILTER);
+    }
+
+    public void testPrimitiveDoubleNotFiltered() throws Exception
+    {
+        doTestReadPrimitive((double) 255.0, ACCEPTS_NONE_FILTER);
+    }
+
+    public void testPrimitiveBooleanNotFiltered() throws Exception
+    {
+        doTestReadPrimitive(false, ACCEPTS_NONE_FILTER);
+    }
+
+    public void testPrimitveCharNotFiltered() throws Exception
+    {
+        doTestReadPrimitive('c', ACCEPTS_NONE_FILTER);
+    }
+
+    public void testReadObjectStringNotFiltered() throws Exception
+    {
+        doTestReadObject(new String("testString"), ACCEPTS_NONE_FILTER);
+    }
+
+    public void testReadObjectFailsWithUntrustedType() throws Exception
+    {
+        byte[] serialized = serializeObject(new SimplePojo("testPayload"));
+
+        TrustedClassFilter myFilter = new TrustedClassFilter()
+        {
+
+            @Override
+            public boolean isTrusted(Class<?> clazz)
+            {
+                return !clazz.equals(SimplePojo.class);
+            }
+        };
+
+        ByteArrayInputStream input = new ByteArrayInputStream(serialized);
+        try (ClassLoadingAwareObjectInputStream reader = new ClassLoadingAwareObjectInputStream(input, myFilter))
+        {
+            try
+            {
+                reader.readObject();
+                fail("Should not be able to read the payload.");
+            }
+            catch (ClassNotFoundException ex)
+            {
+            }
+        }
+
+        serialized = serializeObject(UUID.randomUUID());
+        input = new ByteArrayInputStream(serialized);
+        try (ClassLoadingAwareObjectInputStream reader = new ClassLoadingAwareObjectInputStream(input, myFilter))
+        {
+            try
+            {
+                reader.readObject();
+            }
+            catch (ClassNotFoundException ex)
+            {
+                fail("Should be able to read the payload.");
+            }
+        }
+    }
+
+    public void testReadObjectFailsWithUntrustedContentInTrustedType() throws Exception
+    {
+        byte[] serialized = serializeObject(new SimplePojo(UUID.randomUUID()));
+
+        TrustedClassFilter myFilter = new TrustedClassFilter()
+        {
+
+            @Override
+            public boolean isTrusted(Class<?> clazz)
+            {
+                return clazz.equals(SimplePojo.class);
+            }
+        };
+
+        ByteArrayInputStream input = new ByteArrayInputStream(serialized);
+        try (ClassLoadingAwareObjectInputStream reader = new ClassLoadingAwareObjectInputStream(input, myFilter))
+        {
+            try
+            {
+                reader.readObject();
+                fail("Should not be able to read the payload.");
+            }
+            catch (ClassNotFoundException ex)
+            {
+            }
+        }
+
+        serialized = serializeObject(UUID.randomUUID());
+        input = new ByteArrayInputStream(serialized);
+        try (ClassLoadingAwareObjectInputStream reader = new ClassLoadingAwareObjectInputStream(input, myFilter))
+        {
+            try
+            {
+                reader.readObject();
+                fail("Should not be able to read the payload.");
+            }
+            catch (ClassNotFoundException ex)
+            {
+            }
+        }
+    }
+
+    private void doTestReadObject(Object value, TrustedClassFilter filter) throws Exception
+    {
+        byte[] serialized = serializeObject(value);
+
+        ByteArrayInputStream input = new ByteArrayInputStream(serialized);
+        try (ClassLoadingAwareObjectInputStream reader = new ClassLoadingAwareObjectInputStream(input, filter))
+        {
+            Object result = reader.readObject();
+            assertNotNull(result);
+            assertEquals(value.getClass(), result.getClass());
+            if (value.getClass().isArray())
+            {
+                assertEquals(Array.getLength(value), Array.getLength(result));
+                for (int i = 0; i < Array.getLength(value); ++i)
+                {
+                    assertEquals(Array.get(value, i), Array.get(result, i));
+                }
+            }
+            else
+            {
+                assertEquals(value, result);
+            }
+        }
+    }
+
+    private byte[] serializeObject(Object value) throws IOException
+    {
+        byte[] result = new byte[0];
+
+        if (value != null)
+        {
+            try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                 ObjectOutputStream oos = new ObjectOutputStream(baos))
+            {
+
+                oos.writeObject(value);
+                oos.flush();
+                oos.close();
+
+                result = baos.toByteArray();
+            }
+        }
+
+        return result;
+    }
+
+
+    private void doTestReadPrimitive(Object value, TrustedClassFilter filter) throws Exception
+    {
+        byte[] serialized = serializePrimitive(value);
+
+        ByteArrayInputStream input = new ByteArrayInputStream(serialized);
+        try (ClassLoadingAwareObjectInputStream reader = new ClassLoadingAwareObjectInputStream(input, filter))
+        {
+            Object result = null;
+
+            if (value instanceof Byte)
+            {
+                result = reader.readByte();
+            }
+            else if (value instanceof Short)
+            {
+                result = reader.readShort();
+            }
+            else if (value instanceof Integer)
+            {
+                result = reader.readInt();
+            }
+            else if (value instanceof Long)
+            {
+                result = reader.readLong();
+            }
+            else if (value instanceof Float)
+            {
+                result = reader.readFloat();
+            }
+            else if (value instanceof Double)
+            {
+                result = reader.readDouble();
+            }
+            else if (value instanceof Boolean)
+            {
+                result = reader.readBoolean();
+            }
+            else if (value instanceof Character)
+            {
+                result = reader.readChar();
+            }
+            else
+            {
+                throw new IllegalArgumentException("unsuitable type for primitive deserialization");
+            }
+
+            assertNotNull(result);
+            assertEquals(value.getClass(), result.getClass());
+            assertEquals(value, result);
+        }
+    }
+
+    private byte[] serializePrimitive(Object value) throws IOException
+    {
+        byte[] result = new byte[0];
+
+        if (value != null)
+        {
+            try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                 ObjectOutputStream oos = new ObjectOutputStream(baos))
+            {
+
+                if (value instanceof Byte)
+                {
+                    oos.writeByte((byte) value);
+                }
+                else if (value instanceof Short)
+                {
+                    oos.writeShort((short) value);
+                }
+                else if (value instanceof Integer)
+                {
+                    oos.writeInt((int) value);
+                }
+                else if (value instanceof Long)
+                {
+                    oos.writeLong((long) value);
+                }
+                else if (value instanceof Float)
+                {
+                    oos.writeFloat((float) value);
+                }
+                else if (value instanceof Double)
+                {
+                    oos.writeDouble((double) value);
+                }
+                else if (value instanceof Boolean)
+                {
+                    oos.writeBoolean((boolean) value);
+                }
+                else if (value instanceof Character)
+                {
+                    oos.writeChar((char) value);
+                }
+                else
+                {
+                    throw new IllegalArgumentException("unsuitable type for primitive serialization");
+                }
+
+                oos.flush();
+                oos.close();
+
+                result = baos.toByteArray();
+            }
+        }
+
+        return result;
+    }
 }

Added: qpid/java/trunk/client/src/test/java/org/apache/qpid/client/util/SimplePojo.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/client/src/test/java/org/apache/qpid/client/util/SimplePojo.java?rev=1750359&view=auto
==============================================================================
--- qpid/java/trunk/client/src/test/java/org/apache/qpid/client/util/SimplePojo.java (added)
+++ qpid/java/trunk/client/src/test/java/org/apache/qpid/client/util/SimplePojo.java Mon Jun 27 15:21:41 2016
@@ -0,0 +1,91 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.util;
+
+import java.io.Serializable;
+
+public class SimplePojo implements Serializable
+{
+
+    private static final long serialVersionUID = 3258560248864895099L;
+
+    private Object _payload;
+
+    public SimplePojo()
+    {
+    }
+
+    public SimplePojo(Object payload)
+    {
+        _payload = payload;
+    }
+
+    public Object getPayload()
+    {
+        return _payload;
+    }
+
+    public void setPayload(Object payload)
+    {
+        _payload = payload;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((_payload == null) ? 0 : _payload.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (this == obj)
+        {
+            return true;
+        }
+        if (obj == null)
+        {
+            return false;
+        }
+        if (getClass() != obj.getClass())
+        {
+            return false;
+        }
+
+        SimplePojo other = (SimplePojo) obj;
+        if (_payload == null)
+        {
+            if (other._payload != null)
+            {
+                return false;
+            }
+        }
+        else if (!_payload.equals(other._payload))
+        {
+            return false;
+        }
+
+        return true;
+    }
+}

Modified: qpid/java/trunk/common/src/main/java/org/apache/qpid/configuration/CommonProperties.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/common/src/main/java/org/apache/qpid/configuration/CommonProperties.java?rev=1750359&r1=1750358&r2=1750359&view=diff
==============================================================================
--- qpid/java/trunk/common/src/main/java/org/apache/qpid/configuration/CommonProperties.java (original)
+++ qpid/java/trunk/common/src/main/java/org/apache/qpid/configuration/CommonProperties.java Mon Jun 27 15:21:41 2016
@@ -62,6 +62,9 @@ public class CommonProperties
     public static final String QPID_SECURITY_TLS_CIPHER_SUITE_BLACK_LIST = "qpid.security.tls.cipherSuiteBlackList";
     public static final String QPID_SECURITY_TLS_CIPHER_SUITE_BLACK_LIST_DEFAULT = "";
 
+    public static final String QPID_SECURITY_OBJECT_MESSAGE_CLASS_HIERARCHY_WHITE_LIST = "qpid.security.objectMessage.classHierarchyWhiteList";
+    public static final String QPID_SECURITY_OBJECT_MESSAGE_CLASS_HIERARCHY_BLACK_LIST = "qpid.security.objectMessage.classHierarchyBlackList";
+
     /** The name of the version properties file to load from the class path. */
     public static final String VERSION_RESOURCE = "qpidversion.properties";
 

Added: qpid/java/trunk/systests/src/test/java/org/apache/qpid/client/message/JmsObjectMessageTest.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/systests/src/test/java/org/apache/qpid/client/message/JmsObjectMessageTest.java?rev=1750359&view=auto
==============================================================================
--- qpid/java/trunk/systests/src/test/java/org/apache/qpid/client/message/JmsObjectMessageTest.java (added)
+++ qpid/java/trunk/systests/src/test/java/org/apache/qpid/client/message/JmsObjectMessageTest.java Mon Jun 27 15:21:41 2016
@@ -0,0 +1,340 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.qpid.client.message;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageFormatException;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Queue;
+import javax.jms.Session;
+
+import org.apache.qpid.configuration.CommonProperties;
+import org.apache.qpid.jms.ConnectionURL;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+public class JmsObjectMessageTest extends QpidBrokerTestCase
+{
+    public static final int TIMEOUT = 10000;
+    public static final int TEST_VALUE = 37;
+    private MessageConsumer _consumer;
+    private MessageProducer _producer;
+
+    public void testObjectMessage() throws Exception
+    {
+        Map<String, String> connectionOptions = new HashMap<>();
+        connectionOptions.put(ConnectionURL.OPTIONS_OBJECT_MESSAGE_CLASS_HIERARCHY_WHITE_LIST, "*");
+        final Connection c = getConnectionWithOptions(connectionOptions);
+        c.start();
+        Session s = c.createSession(false, Session.AUTO_ACKNOWLEDGE);
+        Queue destination = getTestQueue();
+        _consumer = s.createConsumer(destination);
+        _producer = s.createProducer(destination);
+
+        sendTestObjectMessage(s, _producer);
+        Message receivedMessage = _consumer.receive(TIMEOUT);
+        assertNotNull("did not receive message within timeout", receivedMessage);
+        assertTrue("message is of wrong type", receivedMessage instanceof ObjectMessage);
+        ObjectMessage receivedObjectMessage = (ObjectMessage) receivedMessage;
+        Object payloadObject = receivedObjectMessage.getObject();
+        assertTrue("payload is of wrong type", payloadObject instanceof HashMap);
+        HashMap<String,Integer> payload = (HashMap<String, Integer>) payloadObject;
+        assertEquals("payload has wrong value", (Integer)TEST_VALUE, payload.get("value"));
+    }
+
+    public void testNotWhiteListedByConnectionUrlObjectMessage() throws Exception
+    {
+        Map<String, String> connectionOptions = new HashMap<>();
+        connectionOptions.put(ConnectionURL.OPTIONS_OBJECT_MESSAGE_CLASS_HIERARCHY_WHITE_LIST, "org.apache.qpid");
+        final Connection c = getConnectionWithOptions(connectionOptions);
+        c.start();
+        Session s = c.createSession(false, Session.AUTO_ACKNOWLEDGE);
+        Queue destination = getTestQueue();
+        MessageConsumer consumer = s.createConsumer(destination);
+        MessageProducer producer = s.createProducer(destination);
+
+        sendTestObjectMessage(s, producer);
+        Message receivedMessage = consumer.receive(TIMEOUT);
+        assertNotNull("did not receive message within timeout", receivedMessage);
+        assertTrue("message is of wrong type", receivedMessage instanceof ObjectMessage);
+        ObjectMessage receivedObjectMessage = (ObjectMessage) receivedMessage;
+        try
+        {
+            receivedObjectMessage.getObject();
+            fail("should not deserialize class");
+        }
+        catch (MessageFormatException e)
+        {
+            // pass
+        }
+    }
+
+    public void testWhiteListedClassByConnectionUrlObjectMessage() throws Exception
+    {
+        Map<String, String> connectionOptions = new HashMap<>();
+        connectionOptions.put(ConnectionURL.OPTIONS_OBJECT_MESSAGE_CLASS_HIERARCHY_WHITE_LIST, "java.util.HashMap,java.lang");
+        final Connection c = getConnectionWithOptions(connectionOptions);
+        c.start();
+        Session s = c.createSession(false, Session.AUTO_ACKNOWLEDGE);
+        Queue destination = getTestQueue();
+        MessageConsumer consumer = s.createConsumer(destination);
+        MessageProducer producer = s.createProducer(destination);
+
+        sendTestObjectMessage(s, producer);
+        Message receivedMessage = consumer.receive(TIMEOUT);
+        assertNotNull("did not receive message within timeout", receivedMessage);
+        assertTrue("message is of wrong type", receivedMessage instanceof ObjectMessage);
+        ObjectMessage receivedObjectMessage = (ObjectMessage) receivedMessage;
+        HashMap<String, Integer> object = (HashMap<String, Integer>) receivedObjectMessage.getObject();
+        assertEquals("Unexpected value", (Integer) TEST_VALUE, object.get("value"));
+    }
+
+    public void testBlackListedClassByConnectionUrlObjectMessage() throws Exception
+    {
+        Map<String, String> connectionOptions = new HashMap<>();
+        connectionOptions.put(ConnectionURL.OPTIONS_OBJECT_MESSAGE_CLASS_HIERARCHY_WHITE_LIST, "java");
+        connectionOptions.put(ConnectionURL.OPTIONS_OBJECT_MESSAGE_CLASS_HIERARCHY_BLACK_LIST, "java.lang.Integer");
+        final Connection c = getConnectionWithOptions(connectionOptions);
+        c.start();
+        Session s = c.createSession(false, Session.AUTO_ACKNOWLEDGE);
+        Queue destination = getTestQueue();
+        MessageConsumer consumer = s.createConsumer(destination);
+        MessageProducer producer = s.createProducer(destination);
+
+        sendTestObjectMessage(s, producer);
+        Message receivedMessage = consumer.receive(TIMEOUT);
+        assertNotNull("did not receive message within timeout", receivedMessage);
+        assertTrue("message is of wrong type", receivedMessage instanceof ObjectMessage);
+        ObjectMessage receivedObjectMessage = (ObjectMessage) receivedMessage;
+
+        try
+        {
+            receivedObjectMessage.getObject();
+            fail("Should not be allowed to deserialize black listed class");
+        }
+        catch (JMSException e)
+        {
+            // pass
+        }
+    }
+
+    public void testNonWhiteListedBySystemPropertyObjectMessage() throws Exception
+    {
+        setSystemProperty(CommonProperties.QPID_SECURITY_OBJECT_MESSAGE_CLASS_HIERARCHY_WHITE_LIST, "org.apache.qpid");
+        final Connection c = getConnection();
+        c.start();
+        Session s = c.createSession(false, Session.AUTO_ACKNOWLEDGE);
+        Queue destination = getTestQueue();
+        MessageConsumer consumer = s.createConsumer(destination);
+        MessageProducer producer = s.createProducer(destination);
+
+        sendTestObjectMessage(s, producer);
+        Message receivedMessage = consumer.receive(TIMEOUT);
+        assertNotNull("did not receive message within timeout", receivedMessage);
+        assertTrue("message is of wrong type", receivedMessage instanceof ObjectMessage);
+        ObjectMessage receivedObjectMessage = (ObjectMessage) receivedMessage;
+        try
+        {
+            receivedObjectMessage.getObject();
+            fail("should not deserialize class");
+        }
+        catch (MessageFormatException e)
+        {
+            // pass
+        }
+    }
+
+    public void testWhiteListedAnonymousClassByConnectionUrlObjectMessage() throws Exception
+    {
+        Map<String, String> connectionOptions = new HashMap<>();
+        connectionOptions.put(ConnectionURL.OPTIONS_OBJECT_MESSAGE_CLASS_HIERARCHY_WHITE_LIST, JmsObjectMessageTest.class.getCanonicalName());
+        final Connection c = getConnectionWithOptions(connectionOptions);
+        doTestWhiteListedEnclosedClassTest(c, createAnonymousObject(TEST_VALUE));
+    }
+
+    public void testBlackListedAnonymousClassByConnectionUrlObjectMessage() throws Exception
+    {
+        Map<String, String> connectionOptions = new HashMap<>();
+        connectionOptions.put(ConnectionURL.OPTIONS_OBJECT_MESSAGE_CLASS_HIERARCHY_WHITE_LIST, JmsObjectMessageTest.class.getPackage().getName());
+        connectionOptions.put(ConnectionURL.OPTIONS_OBJECT_MESSAGE_CLASS_HIERARCHY_BLACK_LIST, JmsObjectMessageTest.class.getCanonicalName());
+
+        final Connection c = getConnectionWithOptions(connectionOptions);
+        doTestBlackListedEnclosedClassTest(c, createAnonymousObject(TEST_VALUE));
+    }
+
+    public void testWhiteListedNestedClassByConnectionUrlObjectMessage() throws Exception
+    {
+        Map<String, String> connectionOptions = new HashMap<>();
+        connectionOptions.put(ConnectionURL.OPTIONS_OBJECT_MESSAGE_CLASS_HIERARCHY_WHITE_LIST, JmsObjectMessageTest.NestedClass.class.getCanonicalName());
+        final Connection c = getConnectionWithOptions(connectionOptions);
+        doTestWhiteListedEnclosedClassTest(c, new NestedClass(TEST_VALUE));
+    }
+
+    public void testBlackListedNestedClassByConnectionUrlObjectMessage() throws Exception
+    {
+        Map<String, String> connectionOptions = new HashMap<>();
+        connectionOptions.put(ConnectionURL.OPTIONS_OBJECT_MESSAGE_CLASS_HIERARCHY_WHITE_LIST, JmsObjectMessageTest.class.getCanonicalName());
+        connectionOptions.put(ConnectionURL.OPTIONS_OBJECT_MESSAGE_CLASS_HIERARCHY_BLACK_LIST, NestedClass.class.getCanonicalName());
+
+        final Connection c = getConnectionWithOptions(connectionOptions);
+        doTestBlackListedEnclosedClassTest(c, new NestedClass(TEST_VALUE));
+    }
+
+    private void doTestWhiteListedEnclosedClassTest(Connection c, Serializable content) throws JMSException
+    {
+        c.start();
+        Session s = c.createSession(false, Session.AUTO_ACKNOWLEDGE);
+        Queue destination = getTestQueue();
+        MessageConsumer consumer = s.createConsumer(destination);
+        MessageProducer producer = s.createProducer(destination);
+
+        final ObjectMessage sendMessage = s.createObjectMessage();
+        sendMessage.setObject(content);
+        producer.send(sendMessage);
+
+        Message receivedMessage = consumer.receive(TIMEOUT);
+        assertNotNull("did not receive message within timeout", receivedMessage);
+        assertTrue("message is of wrong type", receivedMessage instanceof ObjectMessage);
+        Object receivedObject = ((ObjectMessage)receivedMessage).getObject();
+        assertEquals("Received object has unexpected class", content.getClass(), receivedObject.getClass());
+        assertEquals("Received object has unexpected content", content, receivedObject);
+    }
+
+    private void doTestBlackListedEnclosedClassTest(final Connection c, final Serializable content) throws JMSException
+    {
+        c.start();
+        Session s = c.createSession(false, Session.AUTO_ACKNOWLEDGE);
+        Queue destination = getTestQueue();
+        MessageConsumer consumer = s.createConsumer(destination);
+        MessageProducer producer = s.createProducer(destination);
+
+        final ObjectMessage sendMessage = s.createObjectMessage();
+        sendMessage.setObject(content);
+        producer.send(sendMessage);
+
+        Message receivedMessage = consumer.receive(TIMEOUT);
+        assertNotNull("did not receive message within timeout", receivedMessage);
+        assertTrue("message is of wrong type", receivedMessage instanceof ObjectMessage);
+        try
+        {
+            ((ObjectMessage)receivedMessage).getObject();
+            fail("Exception not thrown");
+        }
+        catch (MessageFormatException e)
+        {
+            // pass
+        }
+    }
+
+    private void sendTestObjectMessage(final Session s, final MessageProducer producer) throws JMSException
+    {
+        HashMap<String, Integer> messageContent = new HashMap<>();
+        messageContent.put("value", TEST_VALUE);
+        Message objectMessage =  s.createObjectMessage(messageContent);
+        producer.send(objectMessage);
+    }
+
+    public static Serializable createAnonymousObject(final int field)
+    {
+        return new Serializable()
+        {
+            private int _field = field;
+
+            @Override
+            public int hashCode()
+            {
+                return _field;
+            }
+
+            @Override
+            public boolean equals(final Object o)
+            {
+                if (this == o)
+                {
+                    return true;
+                }
+                if (o == null || getClass() != o.getClass())
+                {
+                    return false;
+                }
+
+                final Serializable that = (Serializable) o;
+
+                return getFieldValueByReflection(that).equals(_field);
+            }
+
+            private Object getFieldValueByReflection(final Serializable that)
+            {
+                try
+                {
+                    final Field f = that.getClass().getDeclaredField("_field");
+                    f.setAccessible(true);
+                    return f.get(that);
+                }
+                catch (NoSuchFieldException | IllegalAccessException e)
+                {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+    }
+
+    public static class NestedClass implements Serializable
+    {
+        private final int _field;
+
+        public NestedClass(final int field)
+        {
+            _field = field;
+        }
+
+        @Override
+        public boolean equals(final Object o)
+        {
+            if (this == o)
+            {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass())
+            {
+                return false;
+            }
+
+            final NestedClass that = (NestedClass) o;
+
+            return _field == that._field;
+
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return _field;
+        }
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org