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