This closes #707

This commit is contained in:
Martyn Taylor 2016-08-09 11:22:48 +01:00
commit 2539e6f2ab
72 changed files with 2999 additions and 272 deletions

View File

@ -19,6 +19,7 @@ package org.apache.activemq.artemis.api.core;
import org.apache.activemq.artemis.core.client.ActiveMQClientMessageBundle;
import org.apache.activemq.artemis.utils.Base64;
import org.apache.activemq.artemis.utils.JsonLoader;
import org.apache.activemq.artemis.utils.ObjectInputStreamWithClassLoader;
import org.apache.activemq.artemis.utils.StringEscapeUtils;
import javax.json.Json;
@ -32,7 +33,6 @@ import javax.json.JsonValue;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeDataSupport;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.StringReader;
import java.util.HashMap;
import java.util.List;
@ -155,7 +155,8 @@ public final class JsonUtil {
CompositeData[] cds = new CompositeData[data.length];
for (int i1 = 0; i1 < data.length; i1++) {
String dataConverted = convertJsonValue(data[i1], String.class).toString();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(Base64.decode(dataConverted)));
ObjectInputStreamWithClassLoader ois = new ObjectInputStreamWithClassLoader(new ByteArrayInputStream(Base64.decode(dataConverted)));
ois.setWhiteList("java.util,java.lang,javax.management");
cds[i1] = (CompositeDataSupport) ois.readObject();
}
innerVal = cds;

View File

@ -25,23 +25,79 @@ import java.lang.reflect.Proxy;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.List;
public class ObjectInputStreamWithClassLoader extends ObjectInputStream {
// Constants ------------------------------------------------------------------------------------
/**
* Value used to indicate that all classes should be white or black listed,
*/
public static final String CATCH_ALL_WILDCARD = "*";
public static final String WHITELIST_PROPERTY = "org.apache.activemq.artemis.jms.deserialization.whitelist";
public static final String BLACKLIST_PROPERTY = "org.apache.activemq.artemis.jms.deserialization.blacklist";
// Attributes -----------------------------------------------------------------------------------
private List<String> whiteList = new ArrayList<>();
private List<String> blackList = new ArrayList<>();
// Static ---------------------------------------------------------------------------------------
// Constructors ---------------------------------------------------------------------------------
public ObjectInputStreamWithClassLoader(final InputStream in) throws IOException {
super(in);
String whiteList = System.getProperty(WHITELIST_PROPERTY, null);
setWhiteList(whiteList);
String blackList = System.getProperty(BLACKLIST_PROPERTY, null);
setBlackList(blackList);
}
// Public ---------------------------------------------------------------------------------------
/**
* @return the whiteList configured on this policy instance.
*/
public String getWhiteList() {
return StringUtil.joinStringList(whiteList, ",");
}
/**
* @return the blackList configured on this policy instance.
*/
public String getBlackList() {
return StringUtil.joinStringList(blackList, ",");
}
/**
* Replaces the currently configured whiteList with a comma separated
* string containing the new whiteList. Null or empty string denotes
* no whiteList entries, {@value #CATCH_ALL_WILDCARD} indicates that
* all classes are whiteListed.
*
* @param whiteList the whiteList that this policy is configured to recognize.
*/
public void setWhiteList(String whiteList) {
this.whiteList = StringUtil.splitStringList(whiteList, ",");
}
/**
* Replaces the currently configured blackList with a comma separated
* string containing the new blackList. Null or empty string denotes
* no blacklist entries, {@value #CATCH_ALL_WILDCARD} indicates that
* all classes are blacklisted.
*
* @param blackList the blackList that this policy is configured to recognize.
*/
public void setBlackList(String blackList) {
this.blackList = StringUtil.splitStringList(blackList, ",");
}
// Package protected ----------------------------------------------------------------------------
// Protected ------------------------------------------------------------------------------------
@ -97,14 +153,13 @@ public class ObjectInputStreamWithClassLoader extends ObjectInputStream {
Class clazz = Class.forName(name, false, loader);
// sanity check only.. if a classLoader can't find a clazz, it will throw an exception
if (clazz == null) {
return super.resolveClass(desc);
}
else {
return clazz;
clazz = super.resolveClass(desc);
}
return checkSecurity(clazz);
}
catch (ClassNotFoundException e) {
return super.resolveClass(desc);
return checkSecurity(super.resolveClass(desc));
}
}
@ -130,7 +185,7 @@ public class ObjectInputStreamWithClassLoader extends ObjectInputStream {
classObjs[i] = cl;
}
try {
return Proxy.getProxyClass(hasNonPublicInterface ? nonPublicLoader : latestLoader, classObjs);
return checkSecurity(Proxy.getProxyClass(hasNonPublicInterface ? nonPublicLoader : latestLoader, classObjs));
}
catch (IllegalArgumentException e) {
throw new ClassNotFoundException(null, e);
@ -156,6 +211,81 @@ public class ObjectInputStreamWithClassLoader extends ObjectInputStream {
}
}
private Class<?> checkSecurity(Class<?> clazz) throws ClassNotFoundException {
Class<?> target = clazz;
while (target.isArray()) {
target = target.getComponentType();
}
while (target.isAnonymousClass() || target.isLocalClass()) {
target = target.getEnclosingClass();
}
if (!target.isPrimitive()) {
if (!isTrustedType(target)) {
throw new ClassNotFoundException("Forbidden " + clazz + "! " +
"This class is not trusted to be deserialized under the current configuration. " +
"Please refer to the documentation for more information on how to configure trusted classes.");
}
}
return clazz;
}
private boolean isTrustedType(Class<?> clazz) {
if (clazz == null) {
return true;
}
String className = clazz.getCanonicalName();
if (className == null) {
// Shouldn't happen as we pre-processed things, but just in case..
className = clazz.getName();
}
for (String blackListEntry : blackList) {
if (CATCH_ALL_WILDCARD.equals(blackListEntry)) {
return false;
}
else if (isClassOrPackageMatch(className, blackListEntry)) {
return false;
}
}
for (String whiteListEntry : whiteList) {
if (CATCH_ALL_WILDCARD.equals(whiteListEntry)) {
return true;
}
else if (isClassOrPackageMatch(className, whiteListEntry)) {
return true;
}
}
// Failing outright rejection or allow from above
// reject only if the whiteList is not empty.
return whiteList.size() == 0;
}
private boolean isClassOrPackageMatch(String className, String listEntry) {
if (className == null) {
return false;
}
// Check if class is an exact match of the entry
if (className.equals(listEntry)) {
return true;
}
// Check if class is from a [sub-]package matching the entry
int entryLength = listEntry.length();
if (className.length() > entryLength && className.startsWith(listEntry) && '.' == className.charAt(entryLength)) {
return true;
}
return false;
}
// Inner classes --------------------------------------------------------------------------------
}

View File

@ -0,0 +1,58 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.utils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class StringUtil {
/**
* Convert a list of Strings into a single String
* @param strList the string list
* @param delimit the delimiter used to separate each string entry in the list
* @return the converted string
*/
public static String joinStringList(List<String> strList, String delimit) {
Iterator<String> entries = strList.iterator();
StringBuilder builder = new StringBuilder();
while (entries.hasNext()) {
builder.append(entries.next());
if (entries.hasNext()) {
builder.append(delimit);
}
}
return builder.toString();
}
/**
* Convert a String into a list of String
* @param strList the String
* @param delimit used to separate items within the string.
* @return the string list
*/
public static List<String> splitStringList(String strList, String delimit) {
ArrayList<String> list = new ArrayList<>();
if (strList != null && !strList.isEmpty()) {
list.addAll(Arrays.asList(strList.split(delimit)));
}
return list;
}
}

View File

@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.util;
import org.apache.activemq.artemis.utils.StringUtil;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
public class StringUtilTest extends Assert {
@Test
public void testJoinStringList() throws Exception {
List<String> strList = new ArrayList<>();
strList.add("a");
strList.add("bc");
strList.add("def");
String result = StringUtil.joinStringList(strList, ",");
assertEquals("a,bc,def", result);
List<String> newList = StringUtil.splitStringList(result, ",");
assertEquals(strList.size(), newList.size());
String result2 = StringUtil.joinStringList(newList, ",");
assertEquals(result, result2);
}
@Test
public void testSplitStringList() throws Exception {
String listStr = "white,blue,yellow,green";
List<String> result = StringUtil.splitStringList(listStr, ",");
assertEquals(4, result.size());
assertEquals("white", result.get(0));
assertEquals("blue", result.get(1));
assertEquals("yellow", result.get(2));
assertEquals("green", result.get(3));
String result2 = StringUtil.joinStringList(result, ",");
assertEquals(listStr, result2);
}
}

View File

@ -130,15 +130,20 @@ public class ActiveMQConnection extends ActiveMQConnectionForContextImpl impleme
private ActiveMQConnectionFactory factoryReference;
private final ConnectionFactoryOptions options;
// Constructors ---------------------------------------------------------------------------------
public ActiveMQConnection(final String username,
public ActiveMQConnection(final ConnectionFactoryOptions options,
final String username,
final String password,
final int connectionType,
final String clientID,
final int dupsOKBatchSize,
final int transactionBatchSize,
final ClientSessionFactory sessionFactory) {
this.options = options;
this.username = username;
this.password = password;
@ -651,10 +656,10 @@ public class ActiveMQConnection extends ActiveMQConnectionForContextImpl impleme
ClientSession session,
int type) {
if (isXA) {
return new ActiveMQXASession(this, transacted, true, acknowledgeMode, session, type);
return new ActiveMQXASession(options, this, transacted, true, acknowledgeMode, session, type);
}
else {
return new ActiveMQSession(this, transacted, false, acknowledgeMode, session, type);
return new ActiveMQSession(options, this, transacted, false, acknowledgeMode, session, type);
}
}
@ -693,6 +698,14 @@ public class ActiveMQConnection extends ActiveMQConnectionForContextImpl impleme
return started;
}
public String getDeserializationBlackList() {
return this.factoryReference.getDeserializationBlackList();
}
public String getDeserializationWhiteList() {
return this.factoryReference.getDeserializationWhiteList();
}
// Inner classes --------------------------------------------------------------------------------
private static class JMSFailureListener implements SessionFailureListener {

View File

@ -61,7 +61,7 @@ import org.apache.activemq.artemis.utils.ClassloadingUtil;
* <p>ActiveMQ Artemis implementation of a JMS ConnectionFactory.</p>
* <p>This connection factory will use defaults defined by {@link DefaultConnectionProperties}.
*/
public class ActiveMQConnectionFactory implements Externalizable, Referenceable, ConnectionFactory, XAConnectionFactory, AutoCloseable {
public class ActiveMQConnectionFactory implements ConnectionFactoryOptions, Externalizable, Referenceable, ConnectionFactory, XAConnectionFactory, AutoCloseable {
private ServerLocator serverLocator;
@ -79,6 +79,10 @@ public class ActiveMQConnectionFactory implements Externalizable, Referenceable,
private String protocolManagerFactoryStr;
private String deserializationBlackList;
private String deserializationWhiteList;
@Override
public void writeExternal(ObjectOutput out) throws IOException {
URI uri = toURI();
@ -150,6 +154,22 @@ public class ActiveMQConnectionFactory implements Externalizable, Referenceable,
}
}
public String getDeserializationBlackList() {
return deserializationBlackList;
}
public void setDeserializationBlackList(String blackList) {
this.deserializationBlackList = blackList;
}
public String getDeserializationWhiteList() {
return deserializationWhiteList;
}
public void setDeserializationWhiteList(String whiteList) {
this.deserializationWhiteList = whiteList;
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
String url = in.readUTF();
@ -744,24 +764,24 @@ public class ActiveMQConnectionFactory implements Externalizable, Referenceable,
if (isXA) {
if (type == ActiveMQConnection.TYPE_GENERIC_CONNECTION) {
connection = new ActiveMQXAConnection(username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, factory);
connection = new ActiveMQXAConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, factory);
}
else if (type == ActiveMQConnection.TYPE_QUEUE_CONNECTION) {
connection = new ActiveMQXAConnection(username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, factory);
connection = new ActiveMQXAConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, factory);
}
else if (type == ActiveMQConnection.TYPE_TOPIC_CONNECTION) {
connection = new ActiveMQXAConnection(username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, factory);
connection = new ActiveMQXAConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, factory);
}
}
else {
if (type == ActiveMQConnection.TYPE_GENERIC_CONNECTION) {
connection = new ActiveMQConnection(username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, factory);
connection = new ActiveMQConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, factory);
}
else if (type == ActiveMQConnection.TYPE_QUEUE_CONNECTION) {
connection = new ActiveMQConnection(username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, factory);
connection = new ActiveMQConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, factory);
}
else if (type == ActiveMQConnection.TYPE_TOPIC_CONNECTION) {
connection = new ActiveMQConnection(username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, factory);
connection = new ActiveMQConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, factory);
}
}

View File

@ -121,6 +121,10 @@ public class ActiveMQMessage implements javax.jms.Message {
}
public static ActiveMQMessage createMessage(final ClientMessage message, final ClientSession session) {
return createMessage(message, session, null);
}
public static ActiveMQMessage createMessage(final ClientMessage message, final ClientSession session, final ConnectionFactoryOptions options) {
int type = message.getType();
ActiveMQMessage msg;
@ -142,7 +146,7 @@ public class ActiveMQMessage implements javax.jms.Message {
break;
}
case ActiveMQObjectMessage.TYPE: {
msg = new ActiveMQObjectMessage(message, session);
msg = new ActiveMQObjectMessage(message, session, options);
break;
}
case ActiveMQStreamMessage.TYPE: // 6
@ -202,7 +206,6 @@ public class ActiveMQMessage implements javax.jms.Message {
*/
protected ActiveMQMessage(final byte type, final ClientSession session) {
message = session.createMessage(type, true, 0, System.currentTimeMillis(), (byte) 4);
}
protected ActiveMQMessage(final ClientSession session) {

View File

@ -31,6 +31,7 @@ import org.apache.activemq.artemis.api.core.ActiveMQInterruptedException;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.client.ClientConsumer;
import org.apache.activemq.artemis.api.core.client.ClientMessage;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.api.core.client.MessageHandler;
import org.apache.activemq.artemis.api.jms.ActiveMQJMSConstants;
import org.apache.activemq.artemis.core.client.ActiveMQClientLogger;
@ -41,6 +42,8 @@ import org.apache.activemq.artemis.core.client.impl.ClientSessionInternal;
*/
public final class ActiveMQMessageConsumer implements QueueReceiver, TopicSubscriber {
private final ConnectionFactoryOptions options;
private final ClientConsumer consumer;
private MessageListener listener;
@ -63,13 +66,16 @@ public final class ActiveMQMessageConsumer implements QueueReceiver, TopicSubscr
// Constructors --------------------------------------------------
protected ActiveMQMessageConsumer(final ActiveMQConnection connection,
protected ActiveMQMessageConsumer(final ConnectionFactoryOptions options,
final ActiveMQConnection connection,
final ActiveMQSession session,
final ClientConsumer consumer,
final boolean noLocal,
final ActiveMQDestination destination,
final String selector,
final SimpleString autoDeleteQueueName) throws JMSException {
this.options = options;
this.connection = connection;
this.session = session;
@ -107,7 +113,7 @@ public final class ActiveMQMessageConsumer implements QueueReceiver, TopicSubscr
public void setMessageListener(final MessageListener listener) throws JMSException {
this.listener = listener;
coreListener = listener == null ? null : new JMSMessageListenerWrapper(connection, session, consumer, listener, ackMode);
coreListener = listener == null ? null : new JMSMessageListenerWrapper(options, connection, session, consumer, listener, ackMode);
try {
consumer.setMessageHandler(coreListener);
@ -211,8 +217,11 @@ public final class ActiveMQMessageConsumer implements QueueReceiver, TopicSubscr
ActiveMQMessage jmsMsg = null;
if (coreMessage != null) {
boolean needSession = ackMode == Session.CLIENT_ACKNOWLEDGE || ackMode == ActiveMQJMSConstants.INDIVIDUAL_ACKNOWLEDGE;
jmsMsg = ActiveMQMessage.createMessage(coreMessage, needSession ? session.getCoreSession() : null);
ClientSession coreSession = session.getCoreSession();
boolean needSession = ackMode == Session.CLIENT_ACKNOWLEDGE ||
ackMode == ActiveMQJMSConstants.INDIVIDUAL_ACKNOWLEDGE ||
coreMessage.getType() == ActiveMQObjectMessage.TYPE;
jmsMsg = ActiveMQMessage.createMessage(coreMessage, needSession ? coreSession : null, options);
try {
jmsMsg.doBeforeReceive();

View File

@ -49,6 +49,8 @@ import org.apache.activemq.artemis.utils.UUIDGenerator;
*/
public class ActiveMQMessageProducer implements MessageProducer, QueueSender, TopicPublisher {
private final ConnectionFactoryOptions options;
private final ActiveMQConnection connection;
private final SimpleString connID;
@ -71,7 +73,9 @@ public class ActiveMQMessageProducer implements MessageProducer, QueueSender, To
protected ActiveMQMessageProducer(final ActiveMQConnection connection,
final ClientProducer producer,
final ActiveMQDestination defaultDestination,
final ClientSession clientSession) throws JMSException {
final ClientSession clientSession,
final ConnectionFactoryOptions options) throws JMSException {
this.options = options;
this.connection = connection;
connID = connection.getClientID() != null ? new SimpleString(connection.getClientID()) : connection.getUID();
@ -434,7 +438,7 @@ public class ActiveMQMessageProducer implements MessageProducer, QueueSender, To
activeMQJmsMessage = new ActiveMQMapMessage((MapMessage) jmsMessage, clientSession);
}
else if (jmsMessage instanceof ObjectMessage) {
activeMQJmsMessage = new ActiveMQObjectMessage((ObjectMessage) jmsMessage, clientSession);
activeMQJmsMessage = new ActiveMQObjectMessage((ObjectMessage) jmsMessage, clientSession, options);
}
else if (jmsMessage instanceof StreamMessage) {
activeMQJmsMessage = new ActiveMQStreamMessage((StreamMessage) jmsMessage, clientSession);

View File

@ -21,7 +21,6 @@ import javax.jms.MessageFormatException;
import javax.jms.ObjectMessage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
@ -48,25 +47,30 @@ public class ActiveMQObjectMessage extends ActiveMQMessage implements ObjectMess
// keep a snapshot of the Serializable Object as a byte[] to provide Object isolation
private byte[] data;
private final ConnectionFactoryOptions options;
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
protected ActiveMQObjectMessage(final ClientSession session) {
protected ActiveMQObjectMessage(final ClientSession session, ConnectionFactoryOptions options) {
super(ActiveMQObjectMessage.TYPE, session);
this.options = options;
}
protected ActiveMQObjectMessage(final ClientMessage message, final ClientSession session) {
protected ActiveMQObjectMessage(final ClientMessage message, final ClientSession session, ConnectionFactoryOptions options) {
super(message, session);
this.options = options;
}
/**
* A copy constructor for foreign JMS ObjectMessages.
*/
public ActiveMQObjectMessage(final ObjectMessage foreign, final ClientSession session) throws JMSException {
public ActiveMQObjectMessage(final ObjectMessage foreign, final ClientSession session, ConnectionFactoryOptions options) throws JMSException {
super(foreign, ActiveMQObjectMessage.TYPE, session);
setObject(foreign.getObject());
this.options = options;
}
// Public --------------------------------------------------------
@ -135,7 +139,15 @@ public class ActiveMQObjectMessage extends ActiveMQMessage implements ObjectMess
return null;
}
try (ObjectInputStream ois = new ObjectInputStreamWithClassLoader(new ByteArrayInputStream(data))) {
try (ObjectInputStreamWithClassLoader ois = new ObjectInputStreamWithClassLoader(new ByteArrayInputStream(data))) {
String blackList = getDeserializationBlackList();
if (blackList != null) {
ois.setBlackList(blackList);
}
String whiteList = getDeserializationWhiteList();
if (whiteList != null) {
ois.setWhiteList(whiteList);
}
Serializable object = (Serializable) ois.readObject();
return object;
}
@ -174,4 +186,22 @@ public class ActiveMQObjectMessage extends ActiveMQMessage implements ObjectMess
return false;
}
}
private String getDeserializationBlackList() {
if (options == null) {
return null;
}
else {
return options.getDeserializationBlackList();
}
}
private String getDeserializationWhiteList() {
if (options == null) {
return null;
}
else {
return options.getDeserializationWhiteList();
}
}
}

View File

@ -40,6 +40,8 @@ public final class ActiveMQQueueBrowser implements QueueBrowser {
// Attributes -----------------------------------------------------------------------------------
private final ConnectionFactoryOptions options;
private final ClientSession session;
private ClientConsumer consumer;
@ -50,9 +52,11 @@ public final class ActiveMQQueueBrowser implements QueueBrowser {
// Constructors ---------------------------------------------------------------------------------
protected ActiveMQQueueBrowser(final ActiveMQQueue queue,
protected ActiveMQQueueBrowser(final ConnectionFactoryOptions options,
final ActiveMQQueue queue,
final String messageSelector,
final ClientSession session) throws JMSException {
this.options = options;
this.session = session;
this.queue = queue;
if (messageSelector != null) {
@ -137,7 +141,7 @@ public final class ActiveMQQueueBrowser implements QueueBrowser {
if (hasMoreElements()) {
ClientMessage next = current;
current = null;
msg = ActiveMQMessage.createMessage(next, session);
msg = ActiveMQMessage.createMessage(next, session, options);
try {
msg.doBeforeReceive();
}

View File

@ -77,6 +77,8 @@ public class ActiveMQSession implements QueueSession, TopicSession {
private static SimpleString REJECTING_FILTER = new SimpleString("_AMQX=-1");
private final ConnectionFactoryOptions options;
private final ActiveMQConnection connection;
private final ClientSession session;
@ -95,12 +97,15 @@ public class ActiveMQSession implements QueueSession, TopicSession {
// Constructors --------------------------------------------------
protected ActiveMQSession(final ActiveMQConnection connection,
protected ActiveMQSession(final ConnectionFactoryOptions options,
final ActiveMQConnection connection,
final boolean transacted,
final boolean xa,
final int ackMode,
final ClientSession session,
final int sessionType) {
this.options = options;
this.connection = connection;
this.ackMode = ackMode;
@ -141,14 +146,14 @@ public class ActiveMQSession implements QueueSession, TopicSession {
public ObjectMessage createObjectMessage() throws JMSException {
checkClosed();
return new ActiveMQObjectMessage(session);
return new ActiveMQObjectMessage(session, options);
}
@Override
public ObjectMessage createObjectMessage(final Serializable object) throws JMSException {
checkClosed();
ActiveMQObjectMessage msg = new ActiveMQObjectMessage(session);
ActiveMQObjectMessage msg = new ActiveMQObjectMessage(session, options);
msg.setObject(object);
@ -308,7 +313,7 @@ public class ActiveMQSession implements QueueSession, TopicSession {
ClientProducer producer = session.createProducer(jbd == null ? null : jbd.getSimpleAddress());
return new ActiveMQMessageProducer(connection, producer, jbd, session);
return new ActiveMQMessageProducer(connection, producer, jbd, session, options);
}
catch (ActiveMQException e) {
throw JMSExceptionHelper.convertFromActiveMQException(e);
@ -522,6 +527,14 @@ public class ActiveMQSession implements QueueSession, TopicSession {
return internalCreateSharedConsumer(localTopic, name, messageSelector, ConsumerDurability.DURABLE);
}
public String getDeserializationBlackList() {
return connection.getDeserializationBlackList();
}
public String getDeserializationWhiteList() {
return connection.getDeserializationWhiteList();
}
enum ConsumerDurability {
DURABLE, NON_DURABLE;
}
@ -587,7 +600,7 @@ public class ActiveMQSession implements QueueSession, TopicSession {
consumer = session.createConsumer(queueName, null, false);
ActiveMQMessageConsumer jbc = new ActiveMQMessageConsumer(connection, this, consumer, false, dest, selectorString, autoDeleteQueueName);
ActiveMQMessageConsumer jbc = new ActiveMQMessageConsumer(options, connection, this, consumer, false, dest, selectorString, autoDeleteQueueName);
consumers.add(jbc);
@ -739,7 +752,7 @@ public class ActiveMQSession implements QueueSession, TopicSession {
}
}
ActiveMQMessageConsumer jbc = new ActiveMQMessageConsumer(connection, this, consumer, noLocal, dest, selectorString, autoDeleteQueueName);
ActiveMQMessageConsumer jbc = new ActiveMQMessageConsumer(options, connection, this, consumer, noLocal, dest, selectorString, autoDeleteQueueName);
consumers.add(jbc);
@ -806,7 +819,7 @@ public class ActiveMQSession implements QueueSession, TopicSession {
throw JMSExceptionHelper.convertFromActiveMQException(e);
}
return new ActiveMQQueueBrowser((ActiveMQQueue) jbq, filterString, session);
return new ActiveMQQueueBrowser(options, (ActiveMQQueue) jbq, filterString, session);
}

View File

@ -34,14 +34,15 @@ import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
*/
public final class ActiveMQXAConnection extends ActiveMQConnection implements XATopicConnection, XAQueueConnection {
public ActiveMQXAConnection(final String username,
public ActiveMQXAConnection(final ConnectionFactoryOptions options,
final String username,
final String password,
final int connectionType,
final String clientID,
final int dupsOKBatchSize,
final int transactionBatchSize,
final ClientSessionFactory sessionFactory) {
super(username, password, connectionType, clientID, dupsOKBatchSize, transactionBatchSize, sessionFactory);
super(options, username, password, connectionType, clientID, dupsOKBatchSize, transactionBatchSize, sessionFactory);
}
@Override

View File

@ -31,12 +31,13 @@ public class ActiveMQXASession extends ActiveMQSession implements XAQueueSession
* @param session
* @param sessionType
*/
protected ActiveMQXASession(ActiveMQConnection connection,
protected ActiveMQXASession(final ConnectionFactoryOptions options,
ActiveMQConnection connection,
boolean transacted,
boolean xa,
int ackMode,
ClientSession session,
int sessionType) {
super(connection, transacted, xa, ackMode, session, sessionType);
super(options, connection, transacted, xa, ackMode, session, sessionType);
}
}

View File

@ -0,0 +1,33 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.jms.client;
/** Common interface to be used to share common parameters between the RA and client JMS.
* Initially developed to carry on Serialization packages white list but it could eventually be expanded. */
public interface ConnectionFactoryOptions {
String getDeserializationBlackList();
void setDeserializationBlackList(String blackList);
String getDeserializationWhiteList();
void setDeserializationWhiteList(String whiteList);
}

View File

@ -28,6 +28,7 @@ import org.apache.activemq.artemis.core.client.impl.ClientSessionInternal;
public class JMSMessageListenerWrapper implements MessageHandler {
private final ConnectionFactoryOptions options;
private final ActiveMQConnection connection;
private final ActiveMQSession session;
@ -40,11 +41,14 @@ public class JMSMessageListenerWrapper implements MessageHandler {
private final boolean individualACK;
protected JMSMessageListenerWrapper(final ActiveMQConnection connection,
protected JMSMessageListenerWrapper(final ConnectionFactoryOptions options,
final ActiveMQConnection connection,
final ActiveMQSession session,
final ClientConsumer consumer,
final MessageListener listener,
final int ackMode) {
this.options = options;
this.connection = connection;
this.session = session;
@ -64,7 +68,7 @@ public class JMSMessageListenerWrapper implements MessageHandler {
*/
@Override
public void onMessage(final ClientMessage message) {
ActiveMQMessage msg = ActiveMQMessage.createMessage(message, session.getCoreSession());
ActiveMQMessage msg = ActiveMQMessage.createMessage(message, session.getCoreSession(), options);
if (individualACK) {
msg.setIndividualAcknowledge();

View File

@ -177,4 +177,12 @@ public interface ConnectionFactoryConfiguration extends EncodingSupport {
String getProtocolManagerFactoryStr();
JMSFactoryType getFactoryType();
String getDeserializationBlackList();
void setDeserializationBlackList(String blackList);
String getDeserializationWhiteList();
void setDeserializationWhiteList(String whiteList);
}

View File

@ -118,6 +118,10 @@ public class ConnectionFactoryConfigurationImpl implements ConnectionFactoryConf
private JMSFactoryType factoryType = JMSFactoryType.CF;
private String deserializationBlackList;
private String deserializationWhiteList;
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
@ -614,6 +618,10 @@ public class ConnectionFactoryConfigurationImpl implements ConnectionFactoryConf
factoryType = JMSFactoryType.valueOf(buffer.readInt());
protocolManagerFactoryStr = BufferHelper.readNullableSimpleStringAsString(buffer);
deserializationBlackList = BufferHelper.readNullableSimpleStringAsString(buffer);
deserializationWhiteList = BufferHelper.readNullableSimpleStringAsString(buffer);
}
@Override
@ -700,6 +708,10 @@ public class ConnectionFactoryConfigurationImpl implements ConnectionFactoryConf
buffer.writeInt(factoryType.intValue());
BufferHelper.writeAsNullableSimpleString(buffer, protocolManagerFactoryStr);
BufferHelper.writeAsNullableSimpleString(buffer, deserializationBlackList);
BufferHelper.writeAsNullableSimpleString(buffer, deserializationWhiteList);
}
@Override
@ -809,7 +821,11 @@ public class ConnectionFactoryConfigurationImpl implements ConnectionFactoryConf
DataConstants.SIZE_INT +
// factoryType
BufferHelper.sizeOfNullableSimpleString(protocolManagerFactoryStr);
BufferHelper.sizeOfNullableSimpleString(protocolManagerFactoryStr) +
BufferHelper.sizeOfNullableSimpleString(deserializationBlackList) +
BufferHelper.sizeOfNullableSimpleString(deserializationWhiteList);
return size;
}
@ -825,6 +841,26 @@ public class ConnectionFactoryConfigurationImpl implements ConnectionFactoryConf
return factoryType;
}
@Override
public String getDeserializationBlackList() {
return deserializationBlackList;
}
@Override
public void setDeserializationBlackList(String blackList) {
this.deserializationBlackList = blackList;
}
@Override
public String getDeserializationWhiteList() {
return this.deserializationWhiteList;
}
@Override
public void setDeserializationWhiteList(String whiteList) {
this.deserializationWhiteList = whiteList;
}
@Override
public ConnectionFactoryConfiguration setCompressLargeMessages(boolean compressLargeMessage) {
this.compressLargeMessage = compressLargeMessage;

View File

@ -24,18 +24,18 @@ import javax.jms.ObjectMessage;
import javax.jms.StreamMessage;
import javax.jms.TextMessage;
import org.apache.activemq.artemis.core.protocol.proton.converter.jms.ServerDestination;
import org.apache.activemq.artemis.core.protocol.proton.converter.jms.ServerJMSObjectMessage;
import org.apache.activemq.artemis.core.protocol.proton.converter.message.JMSVendor;
import org.apache.activemq.artemis.jms.client.ActiveMQDestination;
import org.apache.activemq.artemis.core.buffers.impl.ResetLimitWrappedActiveMQBuffer;
import org.apache.activemq.artemis.core.protocol.proton.converter.jms.ServerDestination;
import org.apache.activemq.artemis.core.protocol.proton.converter.jms.ServerJMSBytesMessage;
import org.apache.activemq.artemis.core.protocol.proton.converter.jms.ServerJMSMapMessage;
import org.apache.activemq.artemis.core.protocol.proton.converter.jms.ServerJMSMessage;
import org.apache.activemq.artemis.core.protocol.proton.converter.jms.ServerJMSObjectMessage;
import org.apache.activemq.artemis.core.protocol.proton.converter.jms.ServerJMSStreamMessage;
import org.apache.activemq.artemis.core.protocol.proton.converter.jms.ServerJMSTextMessage;
import org.apache.activemq.artemis.core.protocol.proton.converter.message.JMSVendor;
import org.apache.activemq.artemis.core.server.ServerMessage;
import org.apache.activemq.artemis.core.server.impl.ServerMessageImpl;
import org.apache.activemq.artemis.jms.client.ActiveMQDestination;
import org.apache.activemq.artemis.utils.IDGenerator;
public class ActiveMQJMSVendor implements JMSVendor {
@ -116,12 +116,9 @@ public class ActiveMQJMSVendor implements JMSVendor {
return new ServerJMSMapMessage(wrapped, deliveryCount);
case org.apache.activemq.artemis.api.core.Message.TEXT_TYPE:
return new ServerJMSTextMessage(wrapped, deliveryCount);
case org.apache.activemq.artemis.api.core.Message.OBJECT_TYPE:
return new ServerJMSObjectMessage(wrapped, deliveryCount);
default:
return new ServerJMSMessage(wrapped, deliveryCount);
}
}
@Override

View File

@ -901,6 +901,36 @@ public class ActiveMQResourceAdapter implements ResourceAdapter, Serializable {
raProperties.setProtocolManagerFactoryStr(protocolManagerFactoryStr);
}
public String getDeserializationBlackList() {
if (ActiveMQResourceAdapter.trace) {
ActiveMQRALogger.LOGGER.trace("getDeserializationBlackList()");
}
return raProperties.getDeserializationBlackList();
}
public void setDeserializationBlackList(String deserializationBlackList) {
if (ActiveMQResourceAdapter.trace) {
ActiveMQRALogger.LOGGER.trace("setDeserializationBlackList(" + deserializationBlackList + ")");
}
raProperties.setDeserializationBlackList(deserializationBlackList);
}
public String getDeserializationWhiteList() {
if (ActiveMQResourceAdapter.trace) {
ActiveMQRALogger.LOGGER.trace("getDeserializationWhiteList()");
}
return raProperties.getDeserializationWhiteList();
}
public void setDeserializationWhiteList(String deserializationWhiteList) {
if (ActiveMQResourceAdapter.trace) {
ActiveMQRALogger.LOGGER.trace("setDeserializationWhiteList(" + deserializationWhiteList + ")");
}
raProperties.setDeserializationWhiteList(deserializationWhiteList);
}
/**
* Get min large message size
*
@ -2004,6 +2034,14 @@ public class ActiveMQResourceAdapter implements ResourceAdapter, Serializable {
if (val5 != null) {
cf.setProtocolManagerFactoryStr(val5);
}
val5 = overrideProperties.getDeserializationBlackList() != null ? overrideProperties.getDeserializationBlackList() : raProperties.getDeserializationBlackList();
if (val5 != null) {
cf.setDeserializationBlackList(val5);
}
val5 = overrideProperties.getDeserializationWhiteList() != null ? overrideProperties.getDeserializationWhiteList() : raProperties.getDeserializationWhiteList();
if (val5 != null) {
cf.setDeserializationWhiteList(val5);
}
}
public void setManagedConnectionFactory(ActiveMQRAManagedConnectionFactory activeMQRAManagedConnectionFactory) {

View File

@ -19,7 +19,9 @@ package org.apache.activemq.artemis.ra;
import java.util.List;
import java.util.Map;
public class ConnectionFactoryProperties {
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
public class ConnectionFactoryProperties implements ConnectionFactoryOptions {
/**
* Trace enabled
@ -120,6 +122,10 @@ public class ConnectionFactoryProperties {
private String protocolManagerFactoryStr;
private String deserializationBlackList;
private String deserializationWhiteList;
/**
* @return the transportType
*/
@ -689,6 +695,28 @@ public class ConnectionFactoryProperties {
this.protocolManagerFactoryStr = protocolManagerFactoryStr;
}
@Override
public String getDeserializationBlackList() {
return deserializationBlackList;
}
@Override
public void setDeserializationBlackList(String deserializationBlackList) {
this.deserializationBlackList = deserializationBlackList;
hasBeenUpdated = true;
}
@Override
public String getDeserializationWhiteList() {
return this.deserializationWhiteList;
}
@Override
public void setDeserializationWhiteList(String deserializationWhiteList) {
this.deserializationWhiteList = deserializationWhiteList;
hasBeenUpdated = true;
}
public boolean isHasBeenUpdated() {
return hasBeenUpdated;
}
@ -960,6 +988,20 @@ public class ConnectionFactoryProperties {
}
else if (!connectionParameters.equals(other.connectionParameters))
return false;
if (deserializationBlackList == null) {
if (other.deserializationBlackList != null)
return false;
}
else if (!deserializationBlackList.equals(other.deserializationBlackList))
return false;
if (deserializationWhiteList == null) {
if (other.deserializationWhiteList != null)
return false;
}
else if (!deserializationWhiteList.equals(other.deserializationWhiteList))
return false;
return true;
}
@ -1010,6 +1052,8 @@ public class ConnectionFactoryProperties {
result = prime * result + ((groupID == null) ? 0 : groupID.hashCode());
result = prime * result + ((connectorClassName == null) ? 0 : connectorClassName.hashCode());
result = prime * result + ((connectionParameters == null) ? 0 : connectionParameters.hashCode());
result = prime * result + ((deserializationBlackList == null) ? 0 : deserializationBlackList.hashCode());
result = prime * result + ((deserializationWhiteList == null) ? 0 : deserializationWhiteList.hashCode());
return result;
}
}

View File

@ -315,7 +315,7 @@ public class ActiveMQActivation {
try {
cf = factory.getServerLocator().createSessionFactory();
session = setupSession(cf);
ActiveMQMessageHandler handler = new ActiveMQMessageHandler(this, ra.getTM(), (ClientSessionInternal) session, cf, i);
ActiveMQMessageHandler handler = new ActiveMQMessageHandler(factory, this, ra.getTM(), (ClientSessionInternal) session, cf, i);
handler.setup();
handlers.add(handler);
}

View File

@ -40,6 +40,7 @@ import org.apache.activemq.artemis.core.client.impl.ClientSessionFactoryInternal
import org.apache.activemq.artemis.core.client.impl.ClientSessionInternal;
import org.apache.activemq.artemis.jms.client.ActiveMQDestination;
import org.apache.activemq.artemis.jms.client.ActiveMQMessage;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.apache.activemq.artemis.ra.ActiveMQRALogger;
import org.apache.activemq.artemis.ra.ActiveMQResourceAdapter;
import org.apache.activemq.artemis.service.extensions.ServiceUtils;
@ -68,6 +69,8 @@ public class ActiveMQMessageHandler implements MessageHandler, FailoverEventList
*/
private MessageEndpoint endpoint;
private final ConnectionFactoryOptions options;
private final ActiveMQActivation activation;
private boolean useLocalTx;
@ -84,11 +87,13 @@ public class ActiveMQMessageHandler implements MessageHandler, FailoverEventList
private volatile boolean connected;
public ActiveMQMessageHandler(final ActiveMQActivation activation,
public ActiveMQMessageHandler(final ConnectionFactoryOptions options,
final ActiveMQActivation activation,
final TransactionManager tm,
final ClientSessionInternal session,
final ClientSessionFactory cf,
final int sessionNr) {
this.options = options;
this.activation = activation;
this.session = session;
this.cf = cf;
@ -286,7 +291,7 @@ public class ActiveMQMessageHandler implements MessageHandler, FailoverEventList
ActiveMQRALogger.LOGGER.trace("onMessage(" + message + ")");
}
ActiveMQMessage msg = ActiveMQMessage.createMessage(message, session);
ActiveMQMessage msg = ActiveMQMessage.createMessage(message, session, options);
boolean beforeDelivery = false;
try {

View File

@ -34,6 +34,9 @@ public class MessageServiceConfiguration {
private String inVmId = "0";
private boolean useLinkHeaders = false;
private String deserializationWhiteList;
private String deserializationBlackList;
@XmlElement(name = "server-in-vm-id")
public String getInVmId() {
return inVmId;
@ -132,4 +135,20 @@ public class MessageServiceConfiguration {
public void setConsumerWindowSize(int consumerWindowSize) {
this.consumerWindowSize = consumerWindowSize;
}
public String getDeserializationWhiteList() {
return deserializationWhiteList;
}
public void setDeserializationWhiteList(String deserializationWhiteList) {
this.deserializationWhiteList = deserializationWhiteList;
}
public String getDeserializationBlackList() {
return deserializationBlackList;
}
public void setDeserializationBlackList(String deserializationBlackList) {
this.deserializationBlackList = deserializationBlackList;
}
}

View File

@ -23,6 +23,7 @@ import java.net.URL;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.xml.bind.JAXBContext;
@ -32,6 +33,7 @@ import org.apache.activemq.artemis.api.core.client.ServerLocator;
import org.apache.activemq.artemis.core.client.impl.ServerLocatorImpl;
import org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnectorFactory;
import org.apache.activemq.artemis.core.remoting.impl.invm.TransportConstants;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.apache.activemq.artemis.rest.queue.DestinationSettings;
import org.apache.activemq.artemis.rest.queue.QueueServiceManager;
import org.apache.activemq.artemis.rest.topic.TopicServiceManager;
@ -46,8 +48,8 @@ import org.apache.activemq.artemis.utils.XMLUtil;
public class MessageServiceManager {
protected ExecutorService threadPool;
protected QueueServiceManager queueManager = new QueueServiceManager();
protected TopicServiceManager topicManager = new TopicServiceManager();
protected QueueServiceManager queueManager;
protected TopicServiceManager topicManager;
protected TimeoutTask timeoutTask;
protected int timeoutTaskInterval = 1;
protected MessageServiceConfiguration configuration = new MessageServiceConfiguration();
@ -55,6 +57,13 @@ public class MessageServiceManager {
protected String configResourcePath;
protected BindingRegistry registry;
private ClientSessionFactory consumerSessionFactory;
public MessageServiceManager(ConnectionFactoryOptions jmsOptions) {
queueManager = new QueueServiceManager(jmsOptions);
topicManager = new TopicServiceManager(jmsOptions);
}
public BindingRegistry getRegistry() {
return registry;
}
@ -147,7 +156,7 @@ public class MessageServiceManager {
consumerLocator.setConsumerWindowSize(configuration.getConsumerWindowSize());
}
ClientSessionFactory consumerSessionFactory = consumerLocator.createSessionFactory();
consumerSessionFactory = consumerLocator.createSessionFactory();
ActiveMQRestLogger.LOGGER.debug("Created ClientSessionFactory: " + consumerSessionFactory);
ServerLocator defaultLocator = new ServerLocatorImpl(false, new TransportConfiguration(InVMConnectorFactory.class.getName(), transportConfig));
@ -197,5 +206,13 @@ public class MessageServiceManager {
if (topicManager != null)
topicManager.stop();
topicManager = null;
this.timeoutTask.stop();
threadPool.shutdown();
try {
threadPool.awaitTermination(5000, TimeUnit.SECONDS);
}
catch (InterruptedException e) {
}
this.consumerSessionFactory.close();
}
}

View File

@ -17,6 +17,7 @@
package org.apache.activemq.artemis.rest.integration;
import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.jboss.resteasy.plugins.server.tjws.TJWSEmbeddedJaxrsServer;
import org.apache.activemq.artemis.rest.MessageServiceManager;
import org.jboss.resteasy.test.TestPortProvider;
@ -25,13 +26,14 @@ public class EmbeddedRestActiveMQ {
protected TJWSEmbeddedJaxrsServer tjws = new TJWSEmbeddedJaxrsServer();
protected EmbeddedActiveMQ embeddedActiveMQ;
protected MessageServiceManager manager = new MessageServiceManager();
protected MessageServiceManager manager = new MessageServiceManager(null);
public EmbeddedRestActiveMQ() {
public EmbeddedRestActiveMQ(ConnectionFactoryOptions jmsOptions) {
int port = TestPortProvider.getPort();
tjws.setPort(port);
tjws.setRootResourcePath("");
tjws.setSecurityDomain(null);
manager = new MessageServiceManager(jmsOptions);
initEmbeddedActiveMQ();
}

View File

@ -16,11 +16,16 @@
*/
package org.apache.activemq.artemis.rest.integration;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.apache.activemq.artemis.jms.server.embedded.EmbeddedJMS;
import org.apache.activemq.artemis.spi.core.naming.BindingRegistry;
public class EmbeddedRestActiveMQJMS extends EmbeddedRestActiveMQ {
public EmbeddedRestActiveMQJMS(ConnectionFactoryOptions jmsOptions) {
super(jmsOptions);
}
@Override
protected void initEmbeddedActiveMQ() {
embeddedActiveMQ = new EmbeddedJMS();

View File

@ -20,22 +20,28 @@ import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.apache.activemq.artemis.rest.MessageServiceManager;
import org.apache.activemq.artemis.utils.ObjectInputStreamWithClassLoader;
import org.jboss.resteasy.spi.Registry;
public class RestMessagingBootstrapListener implements ServletContextListener {
public class RestMessagingBootstrapListener implements ServletContextListener, ConnectionFactoryOptions {
MessageServiceManager manager;
private String deserializationBlackList;
private String deserializationWhiteList;
@Override
public void contextInitialized(ServletContextEvent contextEvent) {
ServletContext context = contextEvent.getServletContext();
String configfile = context.getInitParameter("rest.messaging.config.file");
Registry registry = (Registry) context.getAttribute(Registry.class.getName());
if (registry == null) {
throw new RuntimeException("You must install RESTEasy as a Bootstrap Listener and it must be listed before this class");
}
manager = new MessageServiceManager();
String configfile = context.getInitParameter("rest.messaging.config.file");
deserializationBlackList = context.getInitParameter(ObjectInputStreamWithClassLoader.BLACKLIST_PROPERTY);
deserializationWhiteList = context.getInitParameter(ObjectInputStreamWithClassLoader.WHITELIST_PROPERTY);
manager = new MessageServiceManager(this);
if (configfile != null) {
manager.setConfigResourcePath(configfile);
@ -56,4 +62,24 @@ public class RestMessagingBootstrapListener implements ServletContextListener {
manager.stop();
}
}
@Override
public String getDeserializationBlackList() {
return deserializationBlackList;
}
@Override
public void setDeserializationBlackList(String blackList) {
deserializationBlackList = blackList;
}
@Override
public String getDeserializationWhiteList() {
return deserializationWhiteList;
}
@Override
public void setDeserializationWhiteList(String whiteList) {
deserializationWhiteList = whiteList;
}
}

View File

@ -19,6 +19,7 @@ package org.apache.activemq.artemis.rest.queue;
import org.apache.activemq.artemis.api.core.Message;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.client.ClientMessage;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.apache.activemq.artemis.rest.HttpHeaderProperty;
import org.apache.activemq.artemis.rest.ActiveMQRestLogger;
@ -51,13 +52,13 @@ public abstract class ConsumedMessage {
}
}
public static ConsumedMessage createConsumedMessage(ClientMessage message) {
public static ConsumedMessage createConsumedMessage(ClientMessage message, ConnectionFactoryOptions options) {
Boolean aBoolean = message.getBooleanProperty(POSTED_AS_HTTP_MESSAGE);
if (aBoolean != null && aBoolean.booleanValue()) {
return new ConsumedHttpMessage(message);
}
else if (message.getType() == Message.OBJECT_TYPE) {
return new ConsumedObjectMessage(message);
return new ConsumedObjectMessage(message, options);
}
else {
throw new IllegalArgumentException("ClientMessage must be an HTTP message or an Object message: " + message + " type: " + message.getType());

View File

@ -18,19 +18,22 @@ package org.apache.activemq.artemis.rest.queue;
import org.apache.activemq.artemis.api.core.Message;
import org.apache.activemq.artemis.api.core.client.ClientMessage;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.apache.activemq.artemis.utils.ObjectInputStreamWithClassLoader;
import javax.ws.rs.core.Response;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
public class ConsumedObjectMessage extends ConsumedMessage {
protected Object readObject;
private ConnectionFactoryOptions options;
public ConsumedObjectMessage(ClientMessage message) {
public ConsumedObjectMessage(ClientMessage message, ConnectionFactoryOptions options) {
super(message);
if (message.getType() != Message.OBJECT_TYPE)
throw new IllegalArgumentException("Client message must be an OBJECT_TYPE");
this.options = options;
}
@Override
@ -43,7 +46,11 @@ public class ConsumedObjectMessage extends ConsumedMessage {
message.getBodyBuffer().readBytes(body);
ByteArrayInputStream bais = new ByteArrayInputStream(body);
try {
ObjectInputStream ois = new ObjectInputStream(bais);
ObjectInputStreamWithClassLoader ois = new ObjectInputStreamWithClassLoader(bais);
if (options != null) {
ois.setWhiteList(options.getDeserializationWhiteList());
ois.setBlackList(options.getDeserializationBlackList());
}
readObject = ois.readObject();
}
catch (Exception e) {

View File

@ -21,6 +21,7 @@ import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.api.core.client.ActiveMQClient;
import org.apache.activemq.artemis.api.core.client.ServerLocator;
import org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnectorFactory;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.apache.activemq.artemis.rest.util.LinkStrategy;
import org.apache.activemq.artemis.rest.util.TimeoutTask;
import org.apache.activemq.artemis.spi.core.naming.BindingRegistry;
@ -40,6 +41,12 @@ public abstract class DestinationServiceManager {
protected LinkStrategy linkStrategy;
protected BindingRegistry registry;
protected ConnectionFactoryOptions jmsOptions;
public DestinationServiceManager(ConnectionFactoryOptions jmsOptions) {
this.jmsOptions = jmsOptions;
}
public BindingRegistry getRegistry() {
return registry;
}
@ -157,4 +164,8 @@ public abstract class DestinationServiceManager {
public abstract void start() throws Exception;
public abstract void stop();
public ConnectionFactoryOptions getJmsOptions() {
return jmsOptions;
}
}

View File

@ -33,6 +33,7 @@ import org.apache.activemq.artemis.api.core.client.ClientConsumer;
import org.apache.activemq.artemis.api.core.client.ClientMessage;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.apache.activemq.artemis.rest.ActiveMQRestLogger;
import org.apache.activemq.artemis.rest.util.Constants;
import org.apache.activemq.artemis.rest.util.LinkStrategy;
@ -179,7 +180,7 @@ public class QueueConsumer {
return builder.build();
}
previousIndex = index;
lastConsumed = ConsumedMessage.createConsumedMessage(message);
lastConsumed = ConsumedMessage.createConsumedMessage(message, this.getJmsOptions());
String token = Long.toString(lastConsumed.getMessageID());
Response response = getMessageResponse(lastConsumed, info, basePath, token).build();
if (autoAck)
@ -187,7 +188,9 @@ public class QueueConsumer {
return response;
}
catch (Exception e) {
throw new RuntimeException(e);
Response errorResponse = Response.serverError().entity(e.getMessage())
.status(Response.Status.INTERNAL_SERVER_ERROR).build();
return errorResponse;
}
}
@ -264,4 +267,8 @@ public class QueueConsumer {
String uri = builder.build().toString();
serviceManager.getLinkStrategy().setLinkHeader(response, "consumer", "consumer", uri, MediaType.APPLICATION_XML);
}
public ConnectionFactoryOptions getJmsOptions() {
return serviceManager.getJmsOptions();
}
}

View File

@ -145,6 +145,7 @@ public class QueueDestinationsResource {
PushConsumerResource push = new PushConsumerResource();
push.setDestination(queueName);
push.setSessionFactory(manager.getConsumerSessionFactory());
push.setJmsOptions(manager.getJmsOptions());
queueResource.setPushConsumers(push);
PostMessage sender = null;

View File

@ -21,6 +21,7 @@ import java.util.List;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.apache.activemq.artemis.rest.queue.push.PushStore;
import org.apache.activemq.artemis.rest.queue.push.FilePushStore;
@ -30,6 +31,10 @@ public class QueueServiceManager extends DestinationServiceManager {
protected List<QueueDeployment> queues = new ArrayList<>();
protected QueueDestinationsResource destination;
public QueueServiceManager(ConnectionFactoryOptions jmsOptions) {
super(jmsOptions);
}
public List<QueueDeployment> getQueues() {
return queues;
}

View File

@ -20,6 +20,7 @@ import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.client.ClientConsumer;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.apache.activemq.artemis.rest.ActiveMQRestLogger;
import org.apache.activemq.artemis.rest.queue.push.xml.PushRegistration;
import org.apache.activemq.artemis.utils.SelectorTranslator;
@ -38,16 +39,20 @@ public class PushConsumer {
protected PushStrategy strategy;
protected PushStore store;
private ConnectionFactoryOptions jmsOptions;
public PushConsumer(ClientSessionFactory factory,
String destination,
String id,
PushRegistration registration,
PushStore store) {
PushStore store,
ConnectionFactoryOptions jmsOptions) {
this.factory = factory;
this.destination = destination;
this.id = id;
this.registration = registration;
this.store = store;
this.jmsOptions = jmsOptions;
}
public PushStrategy getStrategy() {
@ -79,6 +84,7 @@ public class PushConsumer {
strategy = new UriStrategy();
}
strategy.setRegistration(registration);
strategy.setJmsOptions(jmsOptions);
strategy.start();
sessions = new ArrayList<>();

View File

@ -33,6 +33,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.apache.activemq.artemis.rest.queue.push.xml.PushRegistration;
import org.apache.activemq.artemis.rest.ActiveMQRestLogger;
@ -45,6 +46,8 @@ public class PushConsumerResource {
protected final AtomicLong sessionCounter = new AtomicLong(1);
protected PushStore pushStore;
private ConnectionFactoryOptions jmsOptions;
public void start() {
}
@ -66,7 +69,7 @@ public class PushConsumerResource {
public void addRegistration(PushRegistration reg) throws Exception {
if (reg.isEnabled() == false)
return;
PushConsumer consumer = new PushConsumer(sessionFactory, destination, reg.getId(), reg, pushStore);
PushConsumer consumer = new PushConsumer(sessionFactory, destination, reg.getId(), reg, pushStore, jmsOptions);
consumer.start();
consumers.put(reg.getId(), consumer);
}
@ -80,7 +83,7 @@ public class PushConsumerResource {
String genId = sessionCounter.getAndIncrement() + "-" + startup;
registration.setId(genId);
registration.setDestination(destination);
PushConsumer consumer = new PushConsumer(sessionFactory, destination, genId, registration, pushStore);
PushConsumer consumer = new PushConsumer(sessionFactory, destination, genId, registration, pushStore, jmsOptions);
try {
consumer.start();
if (registration.isDurable() && pushStore != null) {
@ -142,4 +145,8 @@ public class PushConsumerResource {
public void setDestination(String destination) {
this.destination = destination;
}
public void setJmsOptions(ConnectionFactoryOptions jmsOptions) {
this.jmsOptions = jmsOptions;
}
}

View File

@ -17,6 +17,7 @@
package org.apache.activemq.artemis.rest.queue.push;
import org.apache.activemq.artemis.api.core.client.ClientMessage;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.apache.activemq.artemis.rest.queue.push.xml.PushRegistration;
public interface PushStrategy {
@ -36,4 +37,6 @@ public interface PushStrategy {
void start() throws Exception;
void stop() throws Exception;
void setJmsOptions(ConnectionFactoryOptions jmsOptions);
}

View File

@ -19,6 +19,7 @@ package org.apache.activemq.artemis.rest.queue.push;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.apache.activemq.artemis.rest.queue.push.xml.BasicAuth;
import org.apache.activemq.artemis.rest.queue.push.xml.PushRegistration;
import org.apache.activemq.artemis.rest.util.HttpMessageHelper;
@ -59,6 +60,8 @@ public class UriStrategy implements PushStrategy {
protected String method;
protected String contentType;
protected ConnectionFactoryOptions jmsOptions;
UriStrategy() {
connManager.setDefaultMaxPerRoute(100);
connManager.setMaxTotal(1000);
@ -105,6 +108,11 @@ public class UriStrategy implements PushStrategy {
connManager.shutdown();
}
@Override
public void setJmsOptions(ConnectionFactoryOptions jmsOptions) {
this.jmsOptions = jmsOptions;
}
@Override
public boolean push(ClientMessage message) {
ActiveMQRestLogger.LOGGER.debug("Pushing " + message);
@ -120,7 +128,7 @@ public class UriStrategy implements PushStrategy {
ActiveMQRestLogger.LOGGER.debug("Setting XmlHttpHeader: " + header.getName() + "=" + header.getValue());
request.header(header.getName(), header.getValue());
}
HttpMessageHelper.buildMessage(message, request, contentType);
HttpMessageHelper.buildMessage(message, request, contentType, jmsOptions);
ClientResponse<?> res = null;
try {
ActiveMQRestLogger.LOGGER.debug(method + " " + uri);

View File

@ -19,6 +19,7 @@ package org.apache.activemq.artemis.rest.topic;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.apache.activemq.artemis.rest.queue.push.PushStore;
import org.apache.activemq.artemis.rest.ActiveMQRestLogger;
import org.apache.activemq.artemis.rest.queue.push.PushConsumer;
@ -30,8 +31,9 @@ public class PushSubscription extends PushConsumer {
String destination,
String id,
PushRegistration registration,
PushStore store) {
super(factory, destination, id, registration, store);
PushStore store,
ConnectionFactoryOptions jmsOptions) {
super(factory, destination, id, registration, store, jmsOptions);
}
@Override

View File

@ -20,6 +20,7 @@ import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.apache.activemq.artemis.rest.queue.push.PushConsumer;
import org.apache.activemq.artemis.rest.ActiveMQRestLogger;
@ -47,6 +48,12 @@ public class PushSubscriptionsResource {
protected final AtomicLong sessionCounter = new AtomicLong(1);
protected TopicPushStore pushStore;
private ConnectionFactoryOptions jmsOptions;
public PushSubscriptionsResource(ConnectionFactoryOptions jmsOptions) {
this.jmsOptions = jmsOptions;
}
public void stop() {
for (PushConsumer consumer : consumers.values()) {
consumer.stop();
@ -92,7 +99,7 @@ public class PushSubscriptionsResource {
if (!query.isExists()) {
createSession = createSubscription(destination, reg.isDurable());
}
PushSubscription consumer = new PushSubscription(sessionFactory, reg.getDestination(), reg.getId(), reg, pushStore);
PushSubscription consumer = new PushSubscription(sessionFactory, reg.getDestination(), reg.getId(), reg, pushStore, jmsOptions);
try {
consumer.start();
}
@ -133,7 +140,7 @@ public class PushSubscriptionsResource {
registration.setTopic(destination);
ClientSession createSession = createSubscription(genId, registration.isDurable());
try {
PushSubscription consumer = new PushSubscription(sessionFactory, genId, genId, registration, pushStore);
PushSubscription consumer = new PushSubscription(sessionFactory, genId, genId, registration, pushStore, jmsOptions);
try {
consumer.start();
if (registration.isDurable() && pushStore != null) {

View File

@ -137,7 +137,7 @@ public class TopicDestinationsResource {
subscriptionsResource.setDestination(topicName);
subscriptionsResource.setSessionFactory(manager.getConsumerSessionFactory());
PushSubscriptionsResource push = new PushSubscriptionsResource();
PushSubscriptionsResource push = new PushSubscriptionsResource(manager.getJmsOptions());
push.setDestination(topicName);
push.setSessionFactory(manager.getConsumerSessionFactory());
topicResource.setPushSubscriptions(push);

View File

@ -18,6 +18,7 @@ package org.apache.activemq.artemis.rest.topic;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.apache.activemq.artemis.rest.queue.DestinationServiceManager;
import java.util.ArrayList;
@ -29,6 +30,10 @@ public class TopicServiceManager extends DestinationServiceManager {
protected List<TopicDeployment> topics = new ArrayList<>();
protected TopicDestinationsResource destination;
public TopicServiceManager(ConnectionFactoryOptions jmsOptions) {
super(jmsOptions);
}
public TopicPushStore getPushStore() {
return pushStore;
}

View File

@ -18,15 +18,15 @@ package org.apache.activemq.artemis.rest.util;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.client.ClientMessage;
import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions;
import org.apache.activemq.artemis.rest.HttpHeaderProperty;
import org.apache.activemq.artemis.rest.ActiveMQRestLogger;
import org.apache.activemq.artemis.utils.ObjectInputStreamWithClassLoader;
import org.jboss.resteasy.client.ClientRequest;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.util.List;
import java.util.Map.Entry;
@ -39,39 +39,7 @@ public class HttpMessageHelper {
return lowerKey.toLowerCase().startsWith("content") || lowerKey.toLowerCase().equals("link");
}
public static void buildMessage(ClientMessage message, Response.ResponseBuilder builder) {
for (SimpleString key : message.getPropertyNames()) {
String k = key.toString();
String headerName = HttpHeaderProperty.fromPropertyName(k);
if (headerName == null) {
continue;
}
builder.header(headerName, message.getStringProperty(k));
}
int size = message.getBodySize();
if (size > 0) {
byte[] body = new byte[size];
message.getBodyBuffer().readBytes(body);
Boolean aBoolean = message.getBooleanProperty(POSTED_AS_HTTP_MESSAGE);
if (aBoolean != null && aBoolean.booleanValue()) {
builder.entity(body);
}
else {
ByteArrayInputStream bais = new ByteArrayInputStream(body);
Object obj = null;
try {
ObjectInputStream ois = new ObjectInputStream(bais);
obj = ois.readObject();
}
catch (Exception e) {
throw new RuntimeException(e);
}
builder.entity(obj);
}
}
}
public static void buildMessage(ClientMessage message, ClientRequest request, String contentType) {
public static void buildMessage(ClientMessage message, ClientRequest request, String contentType, ConnectionFactoryOptions jmsOptions) {
for (SimpleString key : message.getPropertyNames()) {
String k = key.toString();
String headerName = HttpHeaderProperty.fromPropertyName(k);
@ -105,12 +73,17 @@ public class HttpMessageHelper {
ByteArrayInputStream bais = new ByteArrayInputStream(body);
Object obj = null;
try {
ObjectInputStream ois = new ObjectInputStream(bais);
ObjectInputStreamWithClassLoader ois = new ObjectInputStreamWithClassLoader(bais);
if (jmsOptions != null) {
ois.setBlackList(jmsOptions.getDeserializationBlackList());
ois.setWhiteList(jmsOptions.getDeserializationWhiteList());
}
obj = ois.readObject();
ActiveMQRestLogger.LOGGER.debug("**** Building Message from object: " + obj.toString());
request.body(contentType, obj);
}
catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}

View File

@ -78,7 +78,9 @@ public class TimeoutTask implements Runnable {
public synchronized void stop() {
running = false;
thread.interrupt();
if (thread != null) {
thread.interrupt();
}
}
public synchronized int getInterval() {

View File

@ -29,7 +29,7 @@ import org.jboss.resteasy.test.TestPortProvider;
public class Embedded {
protected MessageServiceManager manager = new MessageServiceManager();
protected MessageServiceManager manager = new MessageServiceManager(null);
protected MessageServiceConfiguration config = new MessageServiceConfiguration();
protected ActiveMQServer activeMQServer;
protected TJWSEmbeddedJaxrsServer tjws = new TJWSEmbeddedJaxrsServer();

View File

@ -48,7 +48,7 @@ public class EmbeddedTest {
@BeforeClass
public static void startEmbedded() throws Exception {
server = new EmbeddedRestActiveMQJMS();
server = new EmbeddedRestActiveMQJMS(null);
server.getManager().setConfigResourcePath("activemq-rest.xml");
SecurityConfiguration securityConfiguration = new SecurityConfiguration();
securityConfiguration.addUser("guest", "guest");

View File

@ -52,7 +52,7 @@ public class PersistentPushQueueConsumerTest {
activeMQServer.start();
deployment = EmbeddedContainer.start();
manager = new MessageServiceManager();
manager = new MessageServiceManager(null);
manager.start();
deployment.getRegistry().addSingletonResource(manager.getQueueManager().getDestination());
deployment.getRegistry().addSingletonResource(manager.getTopicManager().getDestination());

View File

@ -72,7 +72,7 @@ public class PersistentPushTopicConsumerTest {
public static void startup() throws Exception {
deployment = EmbeddedContainer.start();
manager = new MessageServiceManager();
manager = new MessageServiceManager(null);
manager.start();
deployment.getRegistry().addSingletonResource(manager.getQueueManager().getDestination());
deployment.getRegistry().addSingletonResource(manager.getTopicManager().getDestination());

View File

@ -257,21 +257,21 @@ For more information on configuring the SSL transport, please see [Configuring t
## User credentials
Apache ActiveMQ Artemis ships with two security manager implementations:
- The legacy, deprecated `ActiveMQSecurityManager` that reads user credentials, i.e. user names, passwords and role
- The legacy, deprecated `ActiveMQSecurityManager` that reads user credentials, i.e. user names, passwords and role
information from properties files on the classpath called `artemis-users.properties` and `artemis-roles.properties`.
- The flexible, pluggable `ActiveMQJAASSecurityManager` which supports any standard JAAS login module. Artemis ships
- The flexible, pluggable `ActiveMQJAASSecurityManager` which supports any standard JAAS login module. Artemis ships
with several login modules which will be discussed further down. This is the default security manager.
### JAAS Security Manager
When using JAAS much of the configuration depends on which login module is used. However, there are a few commonalities
for every case. The first place to look is in `bootstrap.xml`. Here is an example using the `PropertiesLogin` JAAS login
for every case. The first place to look is in `bootstrap.xml`. Here is an example using the `PropertiesLogin` JAAS login
module which reads user, password, and role information from properties files:
<jaas-security domain="PropertiesLogin"/>
No matter what login module you're using, you'll need to specify it here in `bootstrap.xml`. The `domain` attribute
here refers to the relevant login module entry in `login.config`. For example:
@ -282,7 +282,7 @@ here refers to the relevant login module entry in `login.config`. For example:
org.apache.activemq.jaas.properties.role="artemis-roles.properties";
};
The `login.config` file is a standard JAAS configuration file. You can read more about this file on
The `login.config` file is a standard JAAS configuration file. You can read more about this file on
[Oracle's website](https://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/tutorials/LoginConfigFile.html).
In short, the file defines:
@ -295,18 +295,18 @@ In short, the file defines:
- a list of configuration options specific to the login module implementation
By default, the location and name of `login.config` is specified on the Artemis command-line which is set by
By default, the location and name of `login.config` is specified on the Artemis command-line which is set by
`etc/artemis.profile` on linux and `etc\artemis.profile.cmd` on Windows.
#### Dual Authentication
The JAAS Security Manager also supports another configuration parameter - `certificate-domain`. This is useful when you
The JAAS Security Manager also supports another configuration parameter - `certificate-domain`. This is useful when you
want to authenticate clients connecting with SSL connections based on their SSL certificates (e.g. using the `CertificateLoginModule`
discussed below) but you still want to authenticate clients connecting with non-SSL connections with, e.g., username and
password. Here's an example of what would go in `bootstrap.xml`:
<jaas-security domain="PropertiesLogin" certificate-domain="CertLogin"/>
And here's the corresponding `login.config`:
PropertiesLogin {
@ -315,7 +315,7 @@ And here's the corresponding `login.config`:
org.apache.activemq.jaas.properties.user="artemis-users.properties"
org.apache.activemq.jaas.properties.role="artemis-roles.properties";
};
CertLogin {
org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule required
debug=true
@ -329,8 +329,8 @@ using `CertLogin` and any client connecting without SSL will be authenticated us
### JAAS Login Modules
#### GuestLoginModule
Allows users without credentials (and, depending on how it is configured, possibly also users with invalid credentials)
to access the broker. Normally, the guest login module is chained with another login module, such as a properties login
Allows users without credentials (and, depending on how it is configured, possibly also users with invalid credentials)
to access the broker. Normally, the guest login module is chained with another login module, such as a properties login
module. It is implemented by `org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule`.
- `org.apache.activemq.jaas.guest.user` - the user name to assign; default is "guest"
@ -340,7 +340,7 @@ module. It is implemented by `org.apache.activemq.artemis.spi.core.security.jaas
- `credentialsInvalidate` - boolean flag; if `true`, reject login requests that include a password (i.e. guest login
succeeds only when the user does not provide a password); default is `false`
- `debug` - boolean flag; if `true`, enable debugging; this is used only for testing or debugging; normally, it
- `debug` - boolean flag; if `true`, enable debugging; this is used only for testing or debugging; normally, it
should be set to `false`, or omitted; default is `false`
There are two basic use cases for the guest login module, as follows:
@ -349,8 +349,8 @@ There are two basic use cases for the guest login module, as follows:
- Guests with no credentials only.
The following snippet shows how to configure a JAAS login entry for the use case where users with no credentials or
invalid credentials are logged in as guests. In this example, the guest login module is used in combination with the
The following snippet shows how to configure a JAAS login entry for the use case where users with no credentials or
invalid credentials are logged in as guests. In this example, the guest login module is used in combination with the
properties login module.
activemq-domain {
@ -358,7 +358,7 @@ properties login module.
debug=true
org.apache.activemq.jaas.properties.user="artemis-users.properties"
org.apache.activemq.jaas.properties.role="artemis-roles.properties";
org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule sufficient
debug=true
org.apache.activemq.jaas.guest.user="anyone"
@ -367,18 +367,18 @@ properties login module.
Depending on the user login data, authentication proceeds as follows:
- User logs in with a valid password — the properties login module successfully authenticates the user and returns
- User logs in with a valid password — the properties login module successfully authenticates the user and returns
immediately. The guest login module is not invoked.
- User logs in with an invalid password — the properties login module fails to authenticate the user, and authentication
- User logs in with an invalid password — the properties login module fails to authenticate the user, and authentication
proceeds to the guest login module. The guest login module successfully authenticates the user and returns the guest principal.
- User logs in with a blank password — the properties login module fails to authenticate the user, and authentication
- User logs in with a blank password — the properties login module fails to authenticate the user, and authentication
proceeds to the guest login module. The guest login module successfully authenticates the user and returns the guest principal.
The following snipped shows how to configure a JAAS login entry for the use case where only those users with no
credentials are logged in as guests. To support this use case, you must set the credentialsInvalidate option to true in
the configuration of the guest login module. You should also note that, compared with the preceding example, the order
The following snipped shows how to configure a JAAS login entry for the use case where only those users with no
credentials are logged in as guests. To support this use case, you must set the credentialsInvalidate option to true in
the configuration of the guest login module. You should also note that, compared with the preceding example, the order
of the login modules is reversed and the flag attached to the properties login module is changed to requisite.
activemq-guest-when-no-creds-only-domain {
@ -387,7 +387,7 @@ of the login modules is reversed and the flag attached to the properties login m
credentialsInvalidate=true
org.apache.activemq.jaas.guest.user="guest"
org.apache.activemq.jaas.guest.role="guests";
org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule requisite
debug=true
org.apache.activemq.jaas.properties.user="artemis-users.properties"
@ -396,20 +396,20 @@ of the login modules is reversed and the flag attached to the properties login m
Depending on the user login data, authentication proceeds as follows:
- User logs in with a valid password — the guest login module fails to authenticate the user (because the user has
presented a password while the credentialsInvalidate option is enabled) and authentication proceeds to the properties
- User logs in with a valid password — the guest login module fails to authenticate the user (because the user has
presented a password while the credentialsInvalidate option is enabled) and authentication proceeds to the properties
login module. The properties login module successfully authenticates the user and returns.
- User logs in with an invalid password — the guest login module fails to authenticate the user and authentication proceeds
to the properties login module. The properties login module also fails to authenticate the user. The nett result is
to the properties login module. The properties login module also fails to authenticate the user. The nett result is
authentication failure.
- User logs in with a blank password — the guest login module successfully authenticates the user and returns immediately.
The properties login module is not invoked.
#### PropertiesLoginModule
The JAAS properties login module provides a simple store of authentication data, where the relevant user data is stored
in a pair of flat files. This is convenient for demonstrations and testing, but for an enterprise system, the integration
The JAAS properties login module provides a simple store of authentication data, where the relevant user data is stored
in a pair of flat files. This is convenient for demonstrations and testing, but for an enterprise system, the integration
with LDAP is preferable. It is implemented by `org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule`.
- `org.apache.activemq.jaas.properties.user` - the path to the file which contains user and password properties
@ -418,148 +418,148 @@ with LDAP is preferable. It is implemented by `org.apache.activemq.artemis.spi.c
- `reload` - boolean flag; whether or not to reload the properties files when a modification occurs; default is `false`
- `debug` - boolean flag; if `true`, enable debugging; this is used only for testing or debugging; normally, it
- `debug` - boolean flag; if `true`, enable debugging; this is used only for testing or debugging; normally, it
should be set to `false`, or omitted; default is `false`
In the context of the properties login module, the `artemis-users.properties` file consists of a list of properties of the
form, `UserName=Password`. For example, to define the users `system`, `user`, and `guest`, you could create a file like
In the context of the properties login module, the `artemis-users.properties` file consists of a list of properties of the
form, `UserName=Password`. For example, to define the users `system`, `user`, and `guest`, you could create a file like
the following:
system=manager
user=password
guest=password
The `artemis-roles.properties` file consists of a list of properties of the form, `Role=UserList`, where UserList is a
comma-separated list of users. For example, to define the roles `admins`, `users`, and `guests`, you could create a file
The `artemis-roles.properties` file consists of a list of properties of the form, `Role=UserList`, where UserList is a
comma-separated list of users. For example, to define the roles `admins`, `users`, and `guests`, you could create a file
like the following:
admins=system
users=system,user
guests=guest
#### LDAPLoginModule
The LDAP login module enables you to perform authentication and authorization by checking the incoming credentials against
user data stored in a central X.500 directory server. For systems that already have an X.500 directory server in place,
this means that you can rapidly integrate ActiveMQ Artemis with the existing security database and user accounts can be
The LDAP login module enables you to perform authentication and authorization by checking the incoming credentials against
user data stored in a central X.500 directory server. For systems that already have an X.500 directory server in place,
this means that you can rapidly integrate ActiveMQ Artemis with the existing security database and user accounts can be
managed using the X.500 system. It is implemented by `org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule`.
- `initialContextFactory` - must always be set to `com.sun.jndi.ldap.LdapCtxFactory`
- `initialContextFactory` - must always be set to `com.sun.jndi.ldap.LdapCtxFactory`
- `connectionURL` - specify the location of the directory server using an ldap URL, ldap://Host:Port. You can
- `connectionURL` - specify the location of the directory server using an ldap URL, ldap://Host:Port. You can
optionally qualify this URL, by adding a forward slash, `/`, followed by the DN of a particular node in the directory
tree. For example, ldap://ldapserver:10389/ou=system.
- `authentication` - specifies the authentication method used when binding to the LDAP server. Can take either of
the values, `simple` (username and password) or `none` (anonymous).
- `connectionUsername` - the DN of the user that opens the connection to the directory server. For example,
tree. For example, ldap://ldapserver:10389/ou=system.
- `authentication` - specifies the authentication method used when binding to the LDAP server. Can take either of
the values, `simple` (username and password) or `none` (anonymous).
- `connectionUsername` - the DN of the user that opens the connection to the directory server. For example,
`uid=admin,ou=system`. Directory servers generally require clients to present username/password credentials in order
to open a connection.
- `connectionPassword` - the password that matches the DN from `connectionUsername`. In the directory server,
in the DIT, the password is normally stored as a `userPassword` attribute in the corresponding directory entry.
- `connectionProtocol` - currently, the only supported value is a blank string. In future, this option will allow
you to select the Secure Socket Layer (SSL) for the connection to the directory server. This option must be set
explicitly to an empty string, because it has no default value.
- `userBase` - selects a particular subtree of the DIT to search for user entries. The subtree is specified by a
to open a connection.
- `connectionPassword` - the password that matches the DN from `connectionUsername`. In the directory server,
in the DIT, the password is normally stored as a `userPassword` attribute in the corresponding directory entry.
- `connectionProtocol` - currently, the only supported value is a blank string. In future, this option will allow
you to select the Secure Socket Layer (SSL) for the connection to the directory server. This option must be set
explicitly to an empty string, because it has no default value.
- `userBase` - selects a particular subtree of the DIT to search for user entries. The subtree is specified by a
DN, which specifes the base node of the subtree. For example, by setting this option to `ou=User,ou=ActiveMQ,ou=system`,
the search for user entries is restricted to the subtree beneath the `ou=User,ou=ActiveMQ,ou=system` node.
- `userSearchMatching` - specifies an LDAP search filter, which is applied to the subtree selected by `userBase`.
Before passing to the LDAP search operation, the string value you provide here is subjected to string substitution,
as implemented by the `java.text.MessageFormat` class. Essentially, this means that the special string, `{0}`, is
substituted by the username, as extracted from the incoming client credentials.
After substitution, the string is interpreted as an LDAP search filter, where the LDAP search filter syntax is
defined by the IETF standard, RFC 2254. A short introduction to the search filter syntax is available from Oracle's
JNDI tutorial, [Search Filters](http://download.oracle.com/javase/jndi/tutorial/basics/directory/filter.html).
the search for user entries is restricted to the subtree beneath the `ou=User,ou=ActiveMQ,ou=system` node.
- `userSearchMatching` - specifies an LDAP search filter, which is applied to the subtree selected by `userBase`.
Before passing to the LDAP search operation, the string value you provide here is subjected to string substitution,
as implemented by the `java.text.MessageFormat` class. Essentially, this means that the special string, `{0}`, is
substituted by the username, as extracted from the incoming client credentials.
After substitution, the string is interpreted as an LDAP search filter, where the LDAP search filter syntax is
defined by the IETF standard, RFC 2254. A short introduction to the search filter syntax is available from Oracle's
JNDI tutorial, [Search Filters](http://download.oracle.com/javase/jndi/tutorial/basics/directory/filter.html).
For example, if this option is set to `(uid={0})` and the received username is `jdoe`, the search filter becomes
`(uid=jdoe)` after string substitution. If the resulting search filter is applied to the subtree selected by the
`(uid=jdoe)` after string substitution. If the resulting search filter is applied to the subtree selected by the
user base, `ou=User,ou=ActiveMQ,ou=system`, it would match the entry, `uid=jdoe,ou=User,ou=ActiveMQ,ou=system`
(and possibly more deeply nested entries, depending on the specified search depth—see the `userSearchSubtree` option).
- `userSearchSubtree` - specify the search depth for user entries, relative to the node specified by `userBase`.
This option is a boolean. `false` indicates it will try to match one of the child entries of the `userBase` node
(maps to `javax.naming.directory.SearchControls.ONELEVEL_SCOPE`). `true` indicates it will try to match any entry
belonging to the subtree of the `userBase` node (maps to `javax.naming.directory.SearchControls.SUBTREE_SCOPE`).
- `userRoleName` - specifies the name of the multi-valued attribute of the user entry that contains a list of
(and possibly more deeply nested entries, depending on the specified search depth—see the `userSearchSubtree` option).
- `userSearchSubtree` - specify the search depth for user entries, relative to the node specified by `userBase`.
This option is a boolean. `false` indicates it will try to match one of the child entries of the `userBase` node
(maps to `javax.naming.directory.SearchControls.ONELEVEL_SCOPE`). `true` indicates it will try to match any entry
belonging to the subtree of the `userBase` node (maps to `javax.naming.directory.SearchControls.SUBTREE_SCOPE`).
- `userRoleName` - specifies the name of the multi-valued attribute of the user entry that contains a list of
role names for the user (where the role names are interpreted as group names by the broker's authorization plug-in).
If you omit this option, no role names are extracted from the user entry.
- `roleBase` - if you want to store role data directly in the directory server, you can use a combination of role
options (`roleBase`, `roleSearchMatching`, `roleSearchSubtree`, and `roleName`) as an alternative to (or in addition
to) specifying the `userRoleName` option. This option selects a particular subtree of the DIT to search for role/group
entries. The subtree is specified by a DN, which specifes the base node of the subtree. For example, by setting this
option to `ou=Group,ou=ActiveMQ,ou=system`, the search for role/group entries is restricted to the subtree beneath
the `ou=Group,ou=ActiveMQ,ou=system` node.
If you omit this option, no role names are extracted from the user entry.
- `roleBase` - if you want to store role data directly in the directory server, you can use a combination of role
options (`roleBase`, `roleSearchMatching`, `roleSearchSubtree`, and `roleName`) as an alternative to (or in addition
to) specifying the `userRoleName` option. This option selects a particular subtree of the DIT to search for role/group
entries. The subtree is specified by a DN, which specifes the base node of the subtree. For example, by setting this
option to `ou=Group,ou=ActiveMQ,ou=system`, the search for role/group entries is restricted to the subtree beneath
the `ou=Group,ou=ActiveMQ,ou=system` node.
- `roleName` - specifies the attribute type of the role entry that contains the name of the role/group (e.g. C, O,
OU, etc.). If you omit this option, the role search feature is effectively disabled.
- `roleSearchMatching` - specifies an LDAP search filter, which is applied to the subtree selected by `roleBase`.
OU, etc.). If you omit this option, the role search feature is effectively disabled.
- `roleSearchMatching` - specifies an LDAP search filter, which is applied to the subtree selected by `roleBase`.
This works in a similar manner to the `userSearchMatching` option, except that it supports two substitution strings,
as follows:
- `{0}` - substitutes the full DN of the matched user entry (that is, the result of the user search). For
as follows:
- `{0}` - substitutes the full DN of the matched user entry (that is, the result of the user search). For
example, for the user, `jdoe`, the substituted string could be `uid=jdoe,ou=User,ou=ActiveMQ,ou=system`.
- `{1}` - substitutes the received username. For example, `jdoe`.
For example, if this option is set to `(member=uid={1})` and the received username is `jdoe`, the search filter
becomes `(member=uid=jdoe)` after string substitution (assuming ApacheDS search filter syntax). If the resulting
search filter is applied to the subtree selected by the role base, `ou=Group,ou=ActiveMQ,ou=system`, it matches all
role entries that have a `member` attribute equal to `uid=jdoe` (the value of a `member` attribute is a DN).
This option must always be set, even if role searching is disabled, because it has no default value.
- `{1}` - substitutes the received username. For example, `jdoe`.
For example, if this option is set to `(member=uid={1})` and the received username is `jdoe`, the search filter
becomes `(member=uid=jdoe)` after string substitution (assuming ApacheDS search filter syntax). If the resulting
search filter is applied to the subtree selected by the role base, `ou=Group,ou=ActiveMQ,ou=system`, it matches all
role entries that have a `member` attribute equal to `uid=jdoe` (the value of a `member` attribute is a DN).
This option must always be set, even if role searching is disabled, because it has no default value.
If you use OpenLDAP, the syntax of the search filter is `(member:=uid=jdoe)`.
- `roleSearchSubtree` - specify the search depth for role entries, relative to the node specified by `roleBase`.
- `roleSearchSubtree` - specify the search depth for role entries, relative to the node specified by `roleBase`.
This option can take boolean values, as follows:
- `false` (default) - try to match one of the child entries of the roleBase node (maps to
`javax.naming.directory.SearchControls.ONELEVEL_SCOPE`).
- `true` — try to match any entry belonging to the subtree of the roleBase node (maps to
- `false` (default) - try to match one of the child entries of the roleBase node (maps to
`javax.naming.directory.SearchControls.ONELEVEL_SCOPE`).
- `true` — try to match any entry belonging to the subtree of the roleBase node (maps to
`javax.naming.directory.SearchControls.SUBTREE_SCOPE`).
- `debug` - boolean flag; if `true`, enable debugging; this is used only for testing or debugging; normally, it
- `debug` - boolean flag; if `true`, enable debugging; this is used only for testing or debugging; normally, it
should be set to `false`, or omitted; default is `false`
Add user entries under the node specified by the `userBase` option. When creating a new user entry in the directory,
choose an object class that supports the `userPassword` attribute (for example, the `person` or `inetOrgPerson` object
classes are typically suitable). After creating the user entry, add the `userPassword` attribute, to hold the user's
Add user entries under the node specified by the `userBase` option. When creating a new user entry in the directory,
choose an object class that supports the `userPassword` attribute (for example, the `person` or `inetOrgPerson` object
classes are typically suitable). After creating the user entry, add the `userPassword` attribute, to hold the user's
password.
If you want to store role data in dedicated role entries (where each node represents a particular role), create a role
entry as follows. Create a new child of the `roleBase` node, where the `objectClass` of the child is `groupOfNames`. Set
the `cn` (or whatever attribute type is specified by `roleName`) of the new child node equal to the name of the
role/group. Define a `member` attribute for each member of the role/group, setting the `member` value to the DN of the
corresponding user (where the DN is specified either fully, `uid=jdoe,ou=User,ou=ActiveMQ,ou=system`, or partially,
If you want to store role data in dedicated role entries (where each node represents a particular role), create a role
entry as follows. Create a new child of the `roleBase` node, where the `objectClass` of the child is `groupOfNames`. Set
the `cn` (or whatever attribute type is specified by `roleName`) of the new child node equal to the name of the
role/group. Define a `member` attribute for each member of the role/group, setting the `member` value to the DN of the
corresponding user (where the DN is specified either fully, `uid=jdoe,ou=User,ou=ActiveMQ,ou=system`, or partially,
`uid=jdoe`).
If you want to add roles to user entries, you would need to customize the directory schema, by adding a suitable
If you want to add roles to user entries, you would need to customize the directory schema, by adding a suitable
attribute type to the user entry's object class. The chosen attribute type must be capable of handling multiple values.
#### CertificateLoginModule
The JAAS certificate authentication login module must be used in combination with SSL and the clients must be configured
with their own certificate. In this scenario, authentication is actually performed during the SSL/TLS handshake, not
with their own certificate. In this scenario, authentication is actually performed during the SSL/TLS handshake, not
directly by the JAAS certificate authentication plug-in. The role of the plug-in is as follows:
- To further constrain the set of acceptable users, because only the user DNs explicitly listed in the relevant
- To further constrain the set of acceptable users, because only the user DNs explicitly listed in the relevant
properties file are eligible to be authenticated.
- To associate a list of groups with the received user identity, facilitating integration with the authorization feature.
- To require the presence of an incoming certificate (by default, the SSL/TLS layer is configured to treat the
- To require the presence of an incoming certificate (by default, the SSL/TLS layer is configured to treat the
presence of a client certificate as optional).
The JAAS certificate login module stores a collection of certificate DNs in a pair of flat files. The files associate a
The JAAS certificate login module stores a collection of certificate DNs in a pair of flat files. The files associate a
username and a list of group IDs with each DN.
The certificate login module is implemented by the following class:
@ -578,13 +578,13 @@ The following `CertLogin` login entry shows how to configure certificate login m
In the preceding example, the JAAS realm is configured to use a single `org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule`
login module. The options supported by this login module are as follows:
- `debug` - boolean flag; if true, enable debugging; this is used only for testing or debugging; normally,
- `debug` - boolean flag; if true, enable debugging; this is used only for testing or debugging; normally,
it should be set to `false`, or omitted; default is `false`
- `org.apache.activemq.jaas.textfiledn.user` - specifies the location of the user properties file (relative to the
- `org.apache.activemq.jaas.textfiledn.user` - specifies the location of the user properties file (relative to the
directory containing the login configuration file).
- `org.apache.activemq.jaas.textfiledn.role` - specifies the location of the role properties file (relative to the
- `org.apache.activemq.jaas.textfiledn.role` - specifies the location of the role properties file (relative to the
directory containing the login configuration file).
- `reload` - boolean flag; whether or not to reload the properties files when a modification occurs; default is `false`
@ -597,29 +597,29 @@ the following:
user=CN=humble user,O=Progress,C=US
guest=CN=anon,O=Progress,C=DE
Each username is mapped to a subject DN, encoded as a string (where the string encoding is specified by RFC 2253). For
Each username is mapped to a subject DN, encoded as a string (where the string encoding is specified by RFC 2253). For
example, the system username is mapped to the `CN=system,O=Progress,C=US` subject DN. When performing authentication,
the plug-in extracts the subject DN from the received certificate, converts it to the standard string format, and
compares it with the subject DNs in the `users.properties` file by testing for string equality. Consequently, you must
the plug-in extracts the subject DN from the received certificate, converts it to the standard string format, and
compares it with the subject DNs in the `users.properties` file by testing for string equality. Consequently, you must
be careful to ensure that the subject DNs appearing in the `users.properties` file are an exact match for the subject
DNs extracted from the user certificates.
Note: Technically, there is some residual ambiguity in the DN string format. For example, the `domainComponent` attribute
could be represented in a string either as the string, `DC`, or as the OID, `0.9.2342.19200300.100.1.25`. Normally, you do
not need to worry about this ambiguity. But it could potentially be a problem, if you changed the underlying
could be represented in a string either as the string, `DC`, or as the OID, `0.9.2342.19200300.100.1.25`. Normally, you do
not need to worry about this ambiguity. But it could potentially be a problem, if you changed the underlying
implementation of the Java security layer.
The easiest way to obtain the subject DNs from the user certificates is by invoking the `keytool` utility to print the
The easiest way to obtain the subject DNs from the user certificates is by invoking the `keytool` utility to print the
certificate contents. To print the contents of a certificate in a keystore, perform the following steps:
1. Export the certificate from the keystore file into a temporary file. For example, to export the certificate with
1. Export the certificate from the keystore file into a temporary file. For example, to export the certificate with
alias `broker-localhost` from the `broker.ks` keystore file, enter the following command:
keytool -export -file broker.export -alias broker-localhost -keystore broker.ks -storepass password
After running this command, the exported certificate is in the file, `broker.export`.
1. Print out the contents of the exported certificate. For example, to print out the contents of `broker.export`,
1. Print out the contents of the exported certificate. For example, to print out the contents of `broker.export`,
enter the following command:
keytool -printcert -file broker.export
@ -634,11 +634,11 @@ certificate contents. To print the contents of a certificate in a keystore, perf
MD5: 3F:6C:0C:89:A8:80:29:CC:F5:2D:DA:5C:D7:3F:AB:37
SHA1: F0:79:0D:04:38:5A:46:CE:86:E1:8A:20:1F:7B:AB:3A:46:E4:34:5C
The string following `Owner:` gives the subject DN. The format used to enter the subject DN depends on your
The string following `Owner:` gives the subject DN. The format used to enter the subject DN depends on your
platform. The `Owner:` string above could be represented as either `CN=localhost,\ OU=broker,\ O=Unknown,\ L=Unknown,\ ST=Unknown,\ C=Unknown`
or `CN=localhost,OU=broker,O=Unknown,L=Unknown,ST=Unknown,C=Unknown`.
The `roles.properties` file consists of a list of properties of the form, `Role=UserList`, where `UserList` is a
The `roles.properties` file consists of a list of properties of the form, `Role=UserList`, where `UserList` is a
comma-separated list of users. For example, to define the roles `admins`, `users`, and `guests`, you could create a file
like the following:
@ -646,7 +646,7 @@ like the following:
users=system,user
guests=guest
The simplest way to make the login configuration available to JAAS is to add the directory containing the file,
The simplest way to make the login configuration available to JAAS is to add the directory containing the file,
`login.config`, to your CLASSPATH.
@ -690,3 +690,91 @@ You will have to configure a few extra properties desribed as below.
- `trustStorePath` - The path of the trust store file. This is needed only if `clientAuth` is `true`.
- `trustStorePassword` - The trust store's password.
## Controlling JMS ObjectMessage deserialization
Artemis provides a simple class filtering mechanism with which a user can specify which
packages are to be trusted and which are not. Objects whose classes are from trusted packages
can be deserialized without problem, whereas those from 'not trusted' packages will be denied
deserialization.
Artemis keeps a `black list` to keep track of packages that are not trusted and a `white list`
for trusted packages. By default both lists are empty, meaning any serializable object is
allowed to be deserialized. If an object whose class matches one of the packages in black list,
it is not allowed to be deserialized. If it matches one in the white list
the object can be deserialized. If a package appears in both black list and white list,
the one in black list takes precedence. If a class neither matches with `black list`
nor with the `white list`, the class deserialization will be denied
unless the white list is empty (meaning the user doesn't specify the white list at all).
A class is considered as a 'match' if
- its full name exactly matches one of the entries in the list.
- its package matches one of the entries in the list or is a sub-package of one of the entries.
For example, if a class full name is "org.apache.pkg1.Class1", some matching entries could be:
- `org.apache.pkg1.Class1` - exact match.
- `org.apache.pkg1` - exact package match.
- `org.apache` -- sub package match.
A `*` means 'match-all' in a black or white list.
### Specifying black list and white list via Connection Factories
To specify the white and black lists one can append properties `deserializationBlackList` and `deserializationWhiteList` respectively
to a Connection Factory's url string. For example:
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("vm://0?deserializationBlackList=org.apache.pkg1,org.some.pkg2");
The above statement creates a factory that has a black list contains two forbidden packages, "org.apache.pkg1" and "org.some.pkg2",
separated by a comma.
You can also set the values via ActiveMQConnectionFactory's API:
public void setDeserializationBlackList(String blackList);
public void setDeserializationWhiteList(String whiteList);
Again the parameters are comma separated list of package/class names.
### Specifying black list and white list via system properties
There are two system properties available for specifying black list and white list:
- `org.apache.activemq.artemis.jms.deserialization.whitelist` - comma separated list of entries for the white list.
- `org.apache.activemq.artemis.jms.deserialization.blacklist` - comma separated list of entries for the black list.
Once defined, all JMS object message deserialization in the VM is subject to checks against the two lists. However if you create a ConnectionFactory
and set a new set of black/white lists on it, the new values will override the system properties.
### Specifying black list and white list for resource adapters
Message beans using a JMS resource adapter to receive messages can also control their object deserialization via properly configuring relevant
properties for their resource adapters. There are two properties that you can configure with connection factories in a resource adapter:
- `deserializationBlackList` - comma separated values for black list
- `deserializationWhiteList` - comma separated values for white list
These properties, once specified, are eventually set on the corresponding internal factories.
### Specifying black list and white list for REST interface
Apache Artemis REST interface ([Rest](rest.md)) allows interactions between jms client and rest clients.
It uses JMS ObjectMessage to wrap the actual user data between the 2 types of clients and deserialization
is needed during this process. If you want to control the deserialization for REST, you need to set the
black/white lists for it separately as Apache Artemis REST Interface is deployed as a web application.
You need to put the black/white lists in its web.xml, as context parameters, as follows
<web-app>
<context-param>
<param-name>org.apache.activemq.artemis.jms.deserialization.whitelist</param-name>
<param-value>some.allowed.class</param-value>
</context-param>
<context-param>
<param-name>org.apache.activemq.artemis.jms.deserialization.blacklist</param-name>
<param-value>some.forbidden.class</param-value>
</context-param>
...
</web-app>
The param-value for each list is a comma separated string value representing the list.

View File

@ -349,6 +349,26 @@
<artifactId>artemis-test-support</artifactId>
<version>${project.version}</version>
</dependency>
<!-- rest test -->
<dependency>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-all</artifactId>
<version>${jetty.version}</version>
<type>jar</type>
<classifier>uber</classifier>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq.rest</groupId>
<artifactId>artemis-rest</artifactId>
<version>1.4.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
@ -356,9 +376,44 @@
<testResource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>**/rest/*.xml</exclude>
</excludes>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>process-test-classes</phase>
<id>bwlist-rest-test-war</id>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptor>src/test/resources/rest/bwlist-rest-test-asm.xml</descriptor>
<finalName>rest-test-bwlist</finalName>
<appendAssemblyId>false</appendAssemblyId>
<outputDirectory>target/test-classes/rest/</outputDirectory>
</configuration>
</execution>
<execution>
<phase>process-test-classes</phase>
<id>rest-test-war</id>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptor>src/test/resources/rest/rest-test-asm.xml</descriptor>
<finalName>rest-test</finalName>
<appendAssemblyId>false</appendAssemblyId>
<outputDirectory>target/test-classes/rest/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>

View File

@ -16,22 +16,34 @@
*/
package org.apache.activemq.artemis.tests.integration.jms;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger;
import org.apache.activemq.artemis.tests.integration.jms.serializables.TestClass1;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.apache.activemq.artemis.utils.ObjectInputStreamWithClassLoader;
import org.apache.activemq.artemis.utils.RandomUtil;
import org.apache.activemq.artemis.core.config.ha.SharedStoreMasterPolicyConfiguration;
import org.junit.Before;
import org.junit.Test;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.QueueBrowser;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.junit.Assert;
@ -213,6 +225,217 @@ public class ActiveMQConnectionFactoryTest extends ActiveMQTestBase {
cf.close();
}
@Test
public void testDeserializationOptions() throws Exception {
testDeserializationOptions(false, false);
}
@Test
public void testDeserializationOptionsJndi() throws Exception {
testDeserializationOptions(true, false);
}
@Test
public void testDeserializationOptionsBrowser() throws Exception {
testDeserializationOptions(false, true);
}
@Test
public void testDeserializationOptionsJndiBrowser() throws Exception {
testDeserializationOptions(true, true);
}
private void testDeserializationOptions(boolean useJndi, boolean useBrowser) throws Exception {
String qname = "SerialTestQueue";
SimpleString qaddr = new SimpleString("jms.queue." + qname);
liveService.createQueue(qaddr, qaddr, null, true, false);
//default ok
String blackList = null;
String whiteList = null;
Object obj = receiveObjectMessage(blackList, whiteList, qname, new TestClass1(), useJndi, useBrowser);
assertTrue("Object is " + obj, obj instanceof TestClass1);
//not in the white list
blackList = "java.lang";
whiteList = "some.other.package1";
obj = receiveObjectMessage(blackList, whiteList, qname, new TestClass1(), useJndi, useBrowser);
assertTrue("Object is " + obj, obj instanceof JMSException);
//but String always trusted
obj = receiveObjectMessage(blackList, whiteList, qname, new String("hello"), useJndi, useBrowser);
assertTrue("java.lang.String always trusted ", "hello".equals(obj));
//in the blacklist
blackList = "org.apache.activemq.artemis.tests.integration.jms.serializables";
whiteList = "org.apache.activemq.artemis.tests.integration.jms.serializables";
obj = receiveObjectMessage(blackList, whiteList, qname, new TestClass1(), useJndi, useBrowser);
assertTrue("Object is " + obj, obj instanceof JMSException);
//black list parent package
blackList = "org.apache.activemq.artemis";
whiteList = "org.apache.activemq.artemis.tests.integration.jms.serializables";
obj = receiveObjectMessage(blackList, whiteList, qname, new TestClass1(), useJndi, useBrowser);
assertTrue("Object is " + obj, obj instanceof JMSException);
//in white list
blackList = "some.other.package";
whiteList = "org.apache.activemq.artemis.tests.integration.jms.serializables";
obj = receiveObjectMessage(blackList, whiteList, qname, new TestClass1(), useJndi, useBrowser);
assertTrue("Object is " + obj, obj instanceof TestClass1);
//parent in white list
blackList = "some.other.package";
whiteList = "org.apache.activemq.artemis.tests.integration.jms";
obj = receiveObjectMessage(blackList, whiteList, qname, new TestClass1(), useJndi, useBrowser);
assertTrue("Object is " + obj, obj instanceof TestClass1);
//sub package in white list
blackList = "some.other.package";
whiteList = "org.apache.activemq.artemis.tests.integration.jms.serializables.pkg1";
obj = receiveObjectMessage(blackList, whiteList, qname, new TestClass1(), useJndi, useBrowser);
assertTrue("Object is " + obj, obj instanceof JMSException);
//wild card white list but black listed
blackList = "org.apache.activemq.artemis.tests.integration.jms.serializables";
whiteList = "*";
obj = receiveObjectMessage(blackList, whiteList, qname, new TestClass1(), useJndi, useBrowser);
assertTrue("Object is " + obj, obj instanceof JMSException);
//wild card white list and not black listed
blackList = "some.other.package";
whiteList = "*";
obj = receiveObjectMessage(blackList, whiteList, qname, new TestClass1(), useJndi, useBrowser);
assertTrue("Object is " + obj, obj instanceof TestClass1);
//wild card black list
blackList = "*";
whiteList = "*";
obj = receiveObjectMessage(blackList, whiteList, qname, new TestClass1(), useJndi, useBrowser);
assertTrue("Object is " + obj, obj instanceof JMSException);
}
@Test
public void testSystemPropertyBlackWhiteListDefault() throws Exception {
System.setProperty(ObjectInputStreamWithClassLoader.BLACKLIST_PROPERTY, "*");
System.setProperty(ObjectInputStreamWithClassLoader.WHITELIST_PROPERTY, "some.other.package");
String qname = "SerialTestQueue";
SimpleString qaddr = new SimpleString("jms.queue." + qname);
liveService.createQueue(qaddr, qaddr, null, true, false);
try {
String blackList = null;
String whiteList = null;
Object obj = receiveObjectMessage(blackList, whiteList, qname, new TestClass1(), false, false);
assertTrue("Object is " + obj, obj instanceof JMSException);
//but String always trusted
obj = receiveObjectMessage(blackList, whiteList, qname, new String("hello"), false, false);
assertTrue("java.lang.String always trusted " + obj, "hello".equals(obj));
//override
blackList = "some.other.package";
whiteList = "org.apache.activemq.artemis.tests.integration";
obj = receiveObjectMessage(blackList, whiteList, qname, new TestClass1(), false, false);
assertTrue("Object is " + obj, obj instanceof TestClass1);
//but String always trusted
obj = receiveObjectMessage(blackList, whiteList, qname, new String("hello"), false, false);
assertTrue("java.lang.String always trusted " + obj, "hello".equals(obj));
}
finally {
System.clearProperty(ObjectInputStreamWithClassLoader.BLACKLIST_PROPERTY);
System.clearProperty(ObjectInputStreamWithClassLoader.WHITELIST_PROPERTY);
}
}
private Object receiveObjectMessage(String blackList, String whiteList, String qname,
Serializable obj, boolean useJndi, boolean useBrowser) throws Exception {
sendObjectMessage(qname, obj);
StringBuilder query = new StringBuilder("");
if (blackList != null) {
query.append("?");
query.append("deserializationBlackList=");
query.append(blackList);
if (whiteList != null) {
query.append("&");
query.append("deserializationWhiteList=");
query.append(whiteList);
}
}
else {
if (whiteList != null) {
query.append("?deserializationWhiteList=");
query.append(whiteList);
}
}
System.out.println("query string: " + query);
ActiveMQConnectionFactory factory = null;
if (useJndi) {
Hashtable<String, Object> props = new Hashtable<>();
props.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory");
props.put("connectionFactory.VmConnectionFactory", "vm://0" + query);
Context ctx = new InitialContext(props);
factory = (ActiveMQConnectionFactory) ctx.lookup("VmConnectionFactory");
}
else {
factory = new ActiveMQConnectionFactory("vm://0" + query);
}
Connection connection = null;
try {
connection = factory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(qname);
Object result = null;
if (useBrowser) {
QueueBrowser browser = session.createBrowser(queue);
ObjectMessage objMessage = (ObjectMessage) browser.getEnumeration().nextElement();
//drain message before triggering deserialization
MessageConsumer consumer = session.createConsumer(queue);
consumer.receive(5000);
result = objMessage.getObject();
}
else {
MessageConsumer consumer = session.createConsumer(queue);
ObjectMessage objMessage = (ObjectMessage) consumer.receive(5000);
assertNotNull(objMessage);
result = objMessage.getObject();
}
return result;
}
catch (Exception e) {
return e;
}
finally {
if (connection != null) {
try {
connection.close();
}
catch (JMSException e) {
return e;
}
}
}
}
private void sendObjectMessage(String qname, Serializable obj) throws Exception {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("vm://0");
Connection connection = factory.createConnection();
try {
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue q = session.createQueue(qname);
MessageProducer producer = session.createProducer(q);
ObjectMessage objMessage = session.createObjectMessage();
objMessage.setObject(obj);
producer.send(objMessage);
}
finally {
connection.close();
}
}
private void testSettersThrowException(final ActiveMQConnectionFactory cf) {
String clientID = RandomUtil.randomString();

View File

@ -0,0 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.tests.integration.jms.serializables;
import java.io.Serializable;
public class TestClass1 implements Serializable {
}

View File

@ -26,15 +26,23 @@ import org.apache.activemq.artemis.api.core.client.SessionFailureListener;
import org.apache.activemq.artemis.core.client.impl.ClientSessionFactoryInternal;
import org.apache.activemq.artemis.core.postoffice.Binding;
import org.apache.activemq.artemis.core.postoffice.impl.LocalQueueBinding;
import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
import org.apache.activemq.artemis.ra.ActiveMQResourceAdapter;
import org.apache.activemq.artemis.ra.inflow.ActiveMQActivation;
import org.apache.activemq.artemis.ra.inflow.ActiveMQActivationSpec;
import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger;
import org.junit.Test;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.Session;
import javax.resource.ResourceException;
import javax.resource.spi.InvalidPropertyException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@ -78,6 +86,103 @@ public class ActiveMQMessageHandlerTest extends ActiveMQRATestBase {
qResourceAdapter.stop();
}
@Test
public void testObjectMessageReceiveSerializationControl() throws Exception {
String blackList = "org.apache.activemq.artemis.tests.integration.ra";
String whiteList = "*";
testDeserialization(blackList, whiteList, false);
}
@Test
public void testObjectMessageReceiveSerializationControl1() throws Exception {
String blackList = "some.other.pkg";
String whiteList = "org.apache.activemq.artemis.tests.integration.ra";
testDeserialization(blackList, whiteList, true);
}
@Test
public void testObjectMessageReceiveSerializationControl2() throws Exception {
String blackList = "*";
String whiteList = "org.apache.activemq.artemis.tests.integration.ra";
testDeserialization(blackList, whiteList, false);
}
@Test
public void testObjectMessageReceiveSerializationControl3() throws Exception {
String blackList = "org.apache.activemq.artemis.tests";
String whiteList = "org.apache.activemq.artemis.tests.integration.ra";
testDeserialization(blackList, whiteList, false);
}
@Test
public void testObjectMessageReceiveSerializationControl4() throws Exception {
String blackList = null;
String whiteList = "some.other.pkg";
testDeserialization(blackList, whiteList, false);
}
@Test
public void testObjectMessageReceiveSerializationControl5() throws Exception {
String blackList = null;
String whiteList = null;
testDeserialization(blackList, whiteList, true);
}
private void testDeserialization(String blackList, String whiteList, boolean shouldSucceed) throws Exception {
ActiveMQResourceAdapter qResourceAdapter = newResourceAdapter();
qResourceAdapter.setDeserializationBlackList(blackList);
qResourceAdapter.setDeserializationWhiteList(whiteList);
MyBootstrapContext ctx = new MyBootstrapContext();
qResourceAdapter.start(ctx);
ActiveMQActivationSpec spec = new ActiveMQActivationSpec();
spec.setResourceAdapter(qResourceAdapter);
spec.setUseJNDI(false);
spec.setDestinationType("javax.jms.Queue");
spec.setDestination(MDBQUEUE);
qResourceAdapter.setConnectorClassName(INVM_CONNECTOR_FACTORY);
CountDownLatch latch = new CountDownLatch(1);
DummyMessageEndpoint endpoint = new DummyMessageEndpoint(latch);
DummyMessageEndpointFactory endpointFactory = new DummyMessageEndpointFactory(endpoint, false);
qResourceAdapter.endpointActivation(endpointFactory, spec);
//send using jms
ActiveMQConnectionFactory jmsFactory = new ActiveMQConnectionFactory("vm://0");
Connection connection = jmsFactory.createConnection();
try {
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue jmsQueue = session.createQueue(MDBQUEUE);
ObjectMessage objMsg = session.createObjectMessage();
objMsg.setObject(new DummySerializable());
MessageProducer producer = session.createProducer(jmsQueue);
producer.send(objMsg);
}
finally {
connection.close();
}
latch.await(5, TimeUnit.SECONDS);
assertNotNull(endpoint.lastMessage);
ObjectMessage objMsg = (ObjectMessage) endpoint.lastMessage;
try {
Object obj = objMsg.getObject();
assertTrue("deserialization should fail but got: " + obj, shouldSucceed);
assertTrue(obj instanceof DummySerializable);
}
catch (JMSException e) {
assertFalse("got unexpected exception: " + e, shouldSucceed);
}
qResourceAdapter.endpointDeactivation(endpointFactory, spec);
qResourceAdapter.stop();
}
@Test
public void testSimpleMessageReceivedOnQueueManyMessages() throws Exception {
ActiveMQResourceAdapter qResourceAdapter = newResourceAdapter();
@ -863,4 +968,7 @@ public class ActiveMQMessageHandlerTest extends ActiveMQRATestBase {
}
}
}
static class DummySerializable implements Serializable {
}
}

View File

@ -0,0 +1,79 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.tests.integration.rest;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
@XmlRootElement(name = "order")
public class Order implements Serializable {
private static final long serialVersionUID = -3462346058107018735L;
private String name;
private String amount;
private String item;
public Order() {
}
public Order(String name, String amount, String item) {
assert (name != null);
assert (amount != null);
assert (item != null);
this.name = name;
this.amount = amount;
this.item = item;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAmount() {
return amount;
}
public void setAmount(String amount) {
this.amount = amount;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof Order)) {
return false;
}
Order order = (Order) other;
return name.equals(order.name) && amount.equals(order.amount) && item.equals(order.item);
}
@Override
public int hashCode() {
return name.hashCode() + amount.hashCode() + item.hashCode();
}
}

View File

@ -0,0 +1,192 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.tests.integration.rest;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.io.Serializable;
import java.io.StringReader;
import org.apache.activemq.artemis.jms.client.ActiveMQDestination;
import org.apache.activemq.artemis.jms.client.ActiveMQJMSConnectionFactory;
import org.apache.activemq.artemis.rest.HttpHeaderProperty;
import org.apache.activemq.artemis.tests.integration.rest.util.RestAMQConnection;
import org.apache.activemq.artemis.tests.integration.rest.util.RestMessageContext;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class RestDeserializationTest extends RestTestBase {
private RestAMQConnection restConnection;
@Before
public void setUp() throws Exception {
super.setUp();
createJettyServer("localhost", 12345);
}
@After
public void tearDown() throws Exception {
if (restConnection != null) {
restConnection.close();
}
super.tearDown();
}
@Test
public void testWithoutBlackWhiteListQueue() throws Exception {
deployAndconfigureRESTService("rest-test.war");
Order order = new Order();
order.setName("Bill");
order.setItem("iPhone4");
order.setAmount("$199.99");
jmsSendMessage(order, "orders", true);
String received = restReceiveQueueMessage("orders");
Object object = xmlToObject(received);
assertEquals(order, object);
}
@Test
public void testWithoutBlackWhiteListTopic() throws Exception {
deployAndconfigureRESTService("rest-test.war");
RestMessageContext topicContext = restConnection.createTopicContext("ordersTopic");
topicContext.initPullConsumers();
Order order = new Order();
order.setName("Bill");
order.setItem("iPhone4");
order.setAmount("$199.99");
jmsSendMessage(order, "ordersTopic", false);
String received = topicContext.pullMessage();
Object object = xmlToObject(received);
assertEquals(order, object);
}
@Test
public void testBlackWhiteListQueuePull() throws Exception {
deployAndconfigureRESTService("rest-test-bwlist.war");
Order order = new Order();
order.setName("Bill");
order.setItem("iPhone4");
order.setAmount("$199.99");
jmsSendMessage(order, "orders", true);
try {
String received = restReceiveQueueMessage("orders");
fail("Object should be rejected by blacklist, but " + received);
}
catch (IllegalStateException e) {
String error = e.getMessage();
assertTrue(error, error.contains("ClassNotFoundException"));
}
}
@Test
public void testBlackWhiteListTopicPull() throws Exception {
deployAndconfigureRESTService("rest-test-bwlist.war");
RestMessageContext topicContext = restConnection.createTopicContext("ordersTopic");
topicContext.initPullConsumers();
Order order = new Order();
order.setName("Bill");
order.setItem("iPhone4");
order.setAmount("$199.99");
jmsSendMessage(order, "ordersTopic", false);
try {
String received = topicContext.pullMessage();
fail("object should have been rejected but: " + received);
}
catch (IllegalStateException e) {
String error = e.getMessage();
assertTrue(error, error.contains("ClassNotFoundException"));
}
}
private void deployAndconfigureRESTService(String warFileName) throws Exception {
jmsServer.createTopic(false, "ordersTopic", (String[]) null);
File warFile = getResourceFile("/rest/" + warFileName, warFileName);
deployWebApp("/restapp", warFile);
server.start();
String uri = server.getURI().toASCIIString();
System.out.println("Sever started with uri: " + uri);
restConnection = new RestAMQConnection(uri);
}
private Object xmlToObject(String xmlString) throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(Order.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StringReader reader = new StringReader(xmlString);
return unmarshaller.unmarshal(reader);
}
private String restReceiveQueueMessage(String destName) throws Exception {
RestMessageContext restContext = restConnection.createQueueContext(destName);
String val = restContext.pullMessage();
return val;
}
private void jmsSendMessage(Serializable value, String destName, boolean isQueue) throws JMSException {
ConnectionFactory factory = new ActiveMQJMSConnectionFactory("tcp://localhost:61616");
String jmsDest;
if (isQueue) {
jmsDest = "jms.queue." + destName;
}
else {
jmsDest = "jms.topic." + destName;
}
Destination destination = ActiveMQDestination.fromAddress(jmsDest);
Connection conn = factory.createConnection();
try {
Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(destination);
ObjectMessage message = session.createObjectMessage();
message.setStringProperty(HttpHeaderProperty.CONTENT_TYPE, "application/xml");
message.setObject(value);
producer.send(message);
}
finally {
conn.close();
}
}
}

View File

@ -0,0 +1,97 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.tests.integration.rest;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import org.apache.activemq.artemis.tests.util.JMSTestBase;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
import shaded.org.apache.commons.io.FileUtils;
public class RestTestBase extends JMSTestBase {
@Rule
public TemporaryFolder testFolder = new TemporaryFolder();
protected Server server;
protected File webAppDir;
protected HandlerList handlers;
@Before
public void setUp() throws Exception {
super.setUp();
webAppDir = testFolder.newFolder("test-apps");
}
@After
public void tearDown() throws Exception {
if (server != null) {
try {
server.stop();
}
catch (Throwable t) {
t.printStackTrace();
}
}
super.tearDown();
}
public Server createJettyServer(String host, int port) throws Exception {
server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setHost(host);
connector.setPort(port);
server.setConnectors(new Connector[]{connector});
handlers = new HandlerList();
server.setHandler(handlers);
return server;
}
public WebAppContext deployWebApp(String contextPath, File warFile) {
WebAppContext webapp = new WebAppContext();
if (contextPath.startsWith("/")) {
webapp.setContextPath(contextPath);
}
else {
webapp.setContextPath("/" + contextPath);
}
webapp.setWar(warFile.getAbsolutePath());
handlers.addHandler(webapp);
return webapp;
}
public File getResourceFile(String resPath, String warName) throws IOException {
InputStream input = RestTestBase.class.getResourceAsStream(resPath);
File result = new File(webAppDir, warName);
FileUtils.copyInputStreamToFile(input, result);
return result;
}
}

View File

@ -0,0 +1,76 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.tests.integration.rest.util;
import java.io.IOException;
import org.apache.http.Header;
import org.apache.http.client.methods.CloseableHttpResponse;
public class QueueRestMessageContext extends RestMessageContext {
public static final String PREFIX_QUEUE = "/queues/jms.queue.";
public QueueRestMessageContext(RestAMQConnection restAMQConnection, String queue) throws IOException {
super(restAMQConnection, queue);
}
@Override
protected String getDestLink() {
return PREFIX_QUEUE + destination;
}
@Override
protected String getPullConsumerUri() {
return getDestLink() + "/pull-consumers";
}
@Override
public void initPullConsumers() throws IOException {
String pullUri = getPullConsumerUri();
CloseableHttpResponse response = null;
if (!this.autoAck) {
response = connection.post(pullUri, "application/x-www-form-urlencoded", "autoAck=false");
}
else {
response = connection.post(pullUri);
}
try {
int code = ResponseUtil.getHttpCode(response);
if (code == 201) {
Header header = response.getFirstHeader("Location");
contextMap.put(KEY_PULL_CONSUMERS_LOC, header.getValue());
header = response.getFirstHeader(KEY_MSG_CONSUME_NEXT);
contextMap.put(KEY_MSG_CONSUME_NEXT, header.getValue());
header = response.getFirstHeader(KEY_MSG_ACK_NEXT);
if (header != null) {
contextMap.put(KEY_MSG_ACK_NEXT, header.getValue());
}
}
}
finally {
response.close();
}
}
@Override
protected String getPushLink(String pushTarget) {
return PREFIX_QUEUE + pushTarget;
}
}

View File

@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.tests.integration.rest.util;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
public class ResponseUtil {
public static int getHttpCode(CloseableHttpResponse response) {
return response.getStatusLine().getStatusCode();
}
public static String getDetails(CloseableHttpResponse response) throws IOException {
HttpEntity entity = response.getEntity();
return EntityUtils.toString(entity);
}
}

View File

@ -0,0 +1,104 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.tests.integration.rest.util;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
public class RestAMQConnection implements Closeable {
private CloseableHttpClient httpClient = HttpClients.createDefault();
private String targetUri;
private List<RestMessageContext> contexts = new ArrayList<>();
public RestAMQConnection(String targetUri) {
this.targetUri = targetUri;
}
public QueueRestMessageContext createQueueContext(String queue) throws Exception {
QueueRestMessageContext ctx = new QueueRestMessageContext(this, queue);
contexts.add(ctx);
return ctx;
}
public TopicRestMessageContext createTopicContext(String topic) throws Exception {
TopicRestMessageContext ctx = new TopicRestMessageContext(this, topic, false);
contexts.add(ctx);
return ctx;
}
@Override
public void close() throws IOException {
for (RestMessageContext ctx : contexts) {
ctx.close();
}
httpClient.close();
}
private String getFullLink(String link) {
if (link.startsWith("http:")) {
return link;
}
return targetUri + link;
}
public CloseableHttpResponse request(String destLink) throws IOException {
String fullLink = getFullLink(destLink);
HttpGet request = new HttpGet(fullLink);
CloseableHttpResponse resp = httpClient.execute(request);
return resp;
}
public CloseableHttpResponse post(String uri, String contentType, String body) throws IOException {
String fullLink = getFullLink(uri);
HttpPost post = new HttpPost(fullLink);
StringEntity entity = new StringEntity(body,
ContentType.create(contentType));
post.setEntity(entity);
CloseableHttpResponse resp = httpClient.execute(post);
return resp;
}
public CloseableHttpResponse post(String uri) throws IOException {
String fullLink = getFullLink(uri);
HttpPost post = new HttpPost(fullLink);
CloseableHttpResponse resp = httpClient.execute(post);
return resp;
}
public void delete(String uri) throws IOException {
String consumerUri = getFullLink(uri);
HttpDelete delete = new HttpDelete(consumerUri);
CloseableHttpResponse resp = httpClient.execute(delete);
}
public String getTargetUri() {
return this.targetUri;
}
}

View File

@ -0,0 +1,271 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.tests.integration.rest.util;
import java.io.Closeable;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
public abstract class RestMessageContext implements Closeable {
public static final String KEY_MSG_CREATE = "msg-create";
public static final String KEY_MSG_CREATE_ID = "msg-create-with-id";
public static final String KEY_MSG_PULL = "msg-pull-consumers";
public static final String KEY_MSG_PUSH = "msg-push-consumers";
public static final String KEY_MSG_PULL_SUB = "msg-pull-subscriptions";
public static final String KEY_MSG_PUSH_SUB = "msg-push-subscriptions";
public static final String KEY_MSG_CREATE_NEXT = "msg-create-next";
public static final String KEY_PULL_CONSUMERS_LOC = "pull-consumers-location";
public static final String KEY_MSG_CONSUME_NEXT = "msg-consume-next";
public static final String KEY_MSG_CONSUMER = "msg-consumer";
public static final String KEY_MSG_ACK_NEXT = "msg-acknowledge-next";
public static final String KEY_MSG_ACK = "msg-acknowledgement";
protected RestAMQConnection connection;
protected String destination;
protected Map<String, String> contextMap = new HashMap<>();
// consumer options
protected boolean autoAck;
protected boolean pushConsumer;
public RestMessageContext(RestAMQConnection restAMQConnection, String dest) throws IOException {
this(restAMQConnection, dest, true, false);
}
public RestMessageContext(RestAMQConnection restAMQConnection, String dest, boolean isAutoAck, boolean isPush) throws IOException {
this.connection = restAMQConnection;
this.destination = dest;
this.autoAck = isAutoAck;
this.pushConsumer = isPush;
prepareSelf();
}
private void prepareSelf() throws IOException {
String destLink = getDestLink();
CloseableHttpResponse response = connection.request(destLink);
int code = ResponseUtil.getHttpCode(response);
if (code != 200) {
System.out.println("failed to init " + destLink);
System.out.println("reason: " + ResponseUtil.getDetails(response));
}
try {
Header header = response.getFirstHeader(KEY_MSG_CREATE);
contextMap.put(KEY_MSG_CREATE, header.getValue());
header = response.getFirstHeader(KEY_MSG_CREATE_ID);
contextMap.put(KEY_MSG_CREATE_ID, header.getValue());
header = response.getFirstHeader(KEY_MSG_PULL);
if (header != null) {
contextMap.put(KEY_MSG_PULL, header.getValue());
}
header = response.getFirstHeader(KEY_MSG_PUSH);
if (header != null) {
contextMap.put(KEY_MSG_PUSH, header.getValue());
}
header = response.getFirstHeader(KEY_MSG_PULL_SUB);
if (header != null) {
contextMap.put(KEY_MSG_PULL_SUB, header.getValue());
}
header = response.getFirstHeader(KEY_MSG_PUSH_SUB);
if (header != null) {
contextMap.put(KEY_MSG_PUSH_SUB, header.getValue());
}
}
finally {
response.close();
}
}
protected abstract String getDestLink();
protected abstract String getPullConsumerUri();
protected abstract String getPushLink(String pushTarget);
public int postMessage(String content, String type) throws IOException {
String postUri;
String nextMsgUri = contextMap.get(KEY_MSG_CREATE_NEXT);
if (nextMsgUri == null) {
postUri = contextMap.get(KEY_MSG_CREATE);
}
else {
postUri = nextMsgUri;
}
CloseableHttpResponse response = connection.post(postUri, type, content);
int code = -1;
try {
code = ResponseUtil.getHttpCode(response);
// check redirection
if (code == 307) {
Header redirLoc = response.getFirstHeader("Location");
contextMap.put(KEY_MSG_CREATE_NEXT, redirLoc.getValue());
code = postMessage(content, type);// do it again.
}
else if (code == 201) {
Header header = response.getFirstHeader(KEY_MSG_CREATE_NEXT);
contextMap.put(KEY_MSG_CREATE_NEXT, header.getValue());
}
}
finally {
response.close();
}
return code;
}
public abstract void initPullConsumers() throws IOException;
public boolean acknowledgement(boolean ackValue) throws IOException {
String ackUri = contextMap.get(KEY_MSG_ACK);
if (ackUri != null) {
CloseableHttpResponse response = connection.post(ackUri, "application/x-www-form-urlencoded",
"acknowledge=" + ackValue);
int code = ResponseUtil.getHttpCode(response);
if (code == 200) {
contextMap.put(KEY_MSG_ACK_NEXT, response.getFirstHeader(KEY_MSG_ACK_NEXT).getValue());
}
return true;
}
return false;
}
public String pullMessage() throws IOException {
String message = null;
String msgPullUri = null;
if (autoAck) {
msgPullUri = contextMap.get(KEY_MSG_CONSUME_NEXT);
if (msgPullUri == null) {
initPullConsumers();
msgPullUri = contextMap.get(KEY_MSG_CONSUME_NEXT);
}
}
else {
msgPullUri = contextMap.get(KEY_MSG_ACK_NEXT);
if (msgPullUri == null) {
initPullConsumers();
msgPullUri = contextMap.get(KEY_MSG_ACK_NEXT);
}
}
CloseableHttpResponse response = connection.post(msgPullUri);
int code = ResponseUtil.getHttpCode(response);
try {
if (code == 200) {
// success
HttpEntity entity = response.getEntity();
long len = entity.getContentLength();
if (len != -1 && len < 1024000) {
message = EntityUtils.toString(entity);
}
else {
// drop message
System.err.println("Mesage too large, drop it " + len);
}
Header header = response.getFirstHeader(KEY_MSG_CONSUMER);
contextMap.put(KEY_MSG_CONSUMER, header.getValue());
if (!autoAck) {
header = response.getFirstHeader(KEY_MSG_ACK);
contextMap.put(KEY_MSG_ACK, header.getValue());
}
else {
header = response.getFirstHeader(KEY_MSG_CONSUME_NEXT);
contextMap.put(KEY_MSG_CONSUME_NEXT, header.getValue());
}
}
else if (code == 503) {
if (autoAck) {
contextMap.put(KEY_MSG_CONSUME_NEXT, response.getFirstHeader(KEY_MSG_CONSUME_NEXT).getValue());
}
else {
contextMap.put(KEY_MSG_ACK_NEXT, response.getFirstHeader(KEY_MSG_ACK_NEXT).getValue());
}
Header header = response.getFirstHeader("Retry-After");
if (header != null) {
long retryDelay = Long.valueOf(response.getFirstHeader("Retry-After").getValue());
try {
Thread.sleep(retryDelay);
}
catch (InterruptedException e) {
e.printStackTrace();
}
message = pullMessage();
}
}
else {
throw new IllegalStateException("error: " + ResponseUtil.getDetails(response));
}
}
finally {
response.close();
}
return message;
}
@Override
public void close() {
String consumerUri = contextMap.get(KEY_MSG_CONSUMER);
if (consumerUri != null) {
try {
connection.delete(consumerUri);
contextMap.remove(KEY_MSG_CONSUMER);
}
catch (ClientProtocolException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
public void setUpPush(String pushTarget) throws Exception {
String pushLink = this.contextMap.get(KEY_MSG_PUSH);
String pushRegXml = "<push-registration>" +
"<link rel=\"destination\" href=\"" +
this.connection.getTargetUri() +
this.getPushLink(pushTarget) + "\"/>" +
"</push-registration>";
CloseableHttpResponse response = connection.post(pushLink, "application/xml", pushRegXml);
int code = ResponseUtil.getHttpCode(response);
try {
if (code != 201) {
System.out.println("Failed to push " + pushRegXml);
System.out.println("Location: " + pushLink);
throw new Exception("Failed to register push " + ResponseUtil.getDetails(response));
}
}
finally {
response.close();
}
}
}

View File

@ -0,0 +1,83 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.tests.integration.rest.util;
import java.io.IOException;
import org.apache.http.Header;
import org.apache.http.client.methods.CloseableHttpResponse;
public class TopicRestMessageContext extends RestMessageContext {
public static final String PREFIX_TOPIC = "/topics/jms.topic.";
private boolean durableSub;
public TopicRestMessageContext(RestAMQConnection restAMQConnection, String topic, boolean durable) throws IOException {
super(restAMQConnection, topic);
this.durableSub = durable;
}
@Override
protected String getDestLink() {
return PREFIX_TOPIC + destination;
}
@Override
protected String getPullConsumerUri() {
return getDestLink() + "/pull-subscriptions";
}
@Override
public void initPullConsumers() throws IOException {
String pullUri = getPullConsumerUri();
CloseableHttpResponse response = null;
if (this.durableSub || !this.autoAck) {
String extraOpt = "durable=" + this.durableSub + "&autoAck=" + this.autoAck;
response = connection.post(pullUri, "application/x-www-form-urlencoded", extraOpt);
}
else {
response = connection.post(pullUri);
}
int code = ResponseUtil.getHttpCode(response);
try {
if (code == 201) {
Header header = response.getFirstHeader("Location");
contextMap.put(KEY_PULL_CONSUMERS_LOC, header.getValue());
header = response.getFirstHeader(KEY_MSG_CONSUME_NEXT);
contextMap.put(KEY_MSG_CONSUME_NEXT, header.getValue());
header = response.getFirstHeader(KEY_MSG_ACK_NEXT);
if (header != null) {
contextMap.put(KEY_MSG_ACK_NEXT, header.getValue());
}
}
else {
throw new IllegalStateException("Failed to init pull consumer " + ResponseUtil.getDetails(response));
}
}
finally {
response.close();
}
}
@Override
protected String getPushLink(String pushTarget) {
return PREFIX_TOPIC + pushTarget;
}
}

View File

@ -0,0 +1,37 @@
<!--
~ 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.
-->
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>rest-test-bwlist</id>
<formats>
<format>war</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>../../artemis-rest/target/classes/</directory>
<outputDirectory>/WEB-INF/classes</outputDirectory>
</fileSet>
<fileSet>
<directory>src/test/resources/rest/rest-test-bwlist/webapp/</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
</fileSets>
</assembly>

View File

@ -0,0 +1,37 @@
<!--
~ 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.
-->
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>rest-test</id>
<formats>
<format>war</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>../../artemis-rest/target/classes/</directory>
<outputDirectory>/WEB-INF/classes</outputDirectory>
</fileSet>
<fileSet>
<directory>src/test/resources/rest/rest-test/webapp/</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
</fileSets>
</assembly>

View File

@ -0,0 +1,56 @@
<?xml version="1.0"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<context-param>
<param-name>org.apache.activemq.artemis.jms.deserialization.whitelist</param-name>
<param-value>some.other.package</param-value>
</context-param>
<context-param>
<param-name>org.apache.activemq.artemis.jms.deserialization.blacklist</param-name>
<param-value>org.apache.activemq.artemis.example.rest.Order</param-value>
</context-param>
<filter>
<filter-name>Rest-Messaging</filter-name>
<filter-class>
org.jboss.resteasy.plugins.server.servlet.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>Rest-Messaging</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<listener>
<listener-class>org.apache.activemq.artemis.rest.integration.RestMessagingBootstrapListener</listener-class>
</listener>
</web-app>

View File

@ -0,0 +1,58 @@
<?xml version="1.0"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<!--
<context-param>
<param-name>org.apache.activemq.artemis.jms.deserialization.whitelist</param-name>
<param-value>some.other.package</param-value>
</context-param>
<context-param>
<param-name>org.apache.activemq.artemis.jms.deserialization.blacklist</param-name>
<param-value>org.apache.activemq.artemis.example.rest.Order</param-value>
</context-param>
-->
<filter>
<filter-name>Rest-Messaging</filter-name>
<filter-class>
org.jboss.resteasy.plugins.server.servlet.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>Rest-Messaging</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<listener>
<listener-class>org.apache.activemq.artemis.rest.integration.RestMessagingBootstrapListener</listener-class>
</listener>
</web-app>

View File

@ -739,7 +739,7 @@ public class MessageHeaderTest extends MessageHeaderTestBase {
ObjectMessage foreignObjectMessage = new SimpleJMSObjectMessage();
ActiveMQObjectMessage copy = new ActiveMQObjectMessage(foreignObjectMessage, session);
ActiveMQObjectMessage copy = new ActiveMQObjectMessage(foreignObjectMessage, session, null);
MessageHeaderTestBase.ensureEquivalent(foreignObjectMessage, copy);
}

View File

@ -390,6 +390,18 @@ public class ActiveMQResourceAdapterConfigTest extends ActiveMQTestBase {
" <config-property-name>ProtocolManagerFactoryStr</config-property-name>" +
" <config-property-type>java.lang.String</config-property-type>" +
" <config-property-value></config-property-value>" +
" </config-property>" +
" <config-property>" +
" <description>List of package/class names against which matching objects are permitted to be deserilized</description>" +
" <config-property-name>DeserializationWhiteList</config-property-name>" +
" <config-property-type>java.lang.String</config-property-type>" +
" <config-property-value></config-property-value>" +
" </config-property>" +
" <config-property>" +
" <description>List of package/classe names against which matching objects are forbidden to be deserialized</description>" +
" <config-property-name>DeserializationBlackList</config-property-name>" +
" <config-property-type>java.lang.String</config-property-type>" +
" <config-property-value></config-property-value>" +
" </config-property>";
private static String rootConfig = "<root>" + config + commentedOutConfigs + "</root>";

View File

@ -474,6 +474,43 @@ public class ResourceAdapterTest extends ActiveMQTestBase {
}
}
@Test
public void testActivationDeserializationParameters() throws Exception {
ActiveMQServer server = createServer(false);
try {
server.start();
ActiveMQResourceAdapter ra = new ActiveMQResourceAdapter();
ra.setConnectorClassName(INVM_CONNECTOR_FACTORY);
ra.setUserName("userGlobal");
ra.setPassword("passwordGlobal");
ra.setDeserializationWhiteList("a.b.c.d.e");
ra.setDeserializationBlackList("f.g.h.i.j");
ra.start(new BootstrapContext());
ActiveMQConnectionFactory factory = ra.getDefaultActiveMQConnectionFactory();
assertEquals("a.b.c.d.e", factory.getDeserializationWhiteList());
assertEquals("f.g.h.i.j", factory.getDeserializationBlackList());
ConnectionFactoryProperties overrides = new ConnectionFactoryProperties();
overrides.setDeserializationWhiteList("k.l.m.n");
overrides.setDeserializationBlackList("o.p.q.r");
factory = ra.newConnectionFactory(overrides);
assertEquals("k.l.m.n", factory.getDeserializationWhiteList());
assertEquals("o.p.q.r", factory.getDeserializationBlackList());
ra.stop();
}
finally {
server.stop();
}
}
@Test
public void testForConnectionLeakDuringActivationWhenSessionCreationFails() throws Exception {
ActiveMQServer server = createServer(false);

View File

@ -16,12 +16,16 @@
*/
package org.apache.activemq.artemis.tests.unit.util;
import org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.EnclosingClass;
import org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.TestClass1;
import org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.TestClass2;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
@ -33,12 +37,17 @@ import java.net.URLClassLoader;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.junit.Assert;
import org.apache.activemq.artemis.utils.ObjectInputStreamWithClassLoader;
import org.junit.Test;
public class ObjectInputStreamWithClassLoaderTest extends ActiveMQTestBase {
// Constants -----------------------------------------------------
@ -47,29 +56,33 @@ public class ObjectInputStreamWithClassLoaderTest extends ActiveMQTestBase {
// Static --------------------------------------------------------
public static ClassLoader newClassLoader(final Class anyUserClass) throws Exception {
ProtectionDomain protectionDomain = anyUserClass.getProtectionDomain();
CodeSource codeSource = protectionDomain.getCodeSource();
URL classLocation = codeSource.getLocation();
public static ClassLoader newClassLoader(final Class... userClasses) throws Exception {
Set<URL> userClassUrls = new HashSet<>();
for (Class anyUserClass : userClasses) {
ProtectionDomain protectionDomain = anyUserClass.getProtectionDomain();
CodeSource codeSource = protectionDomain.getCodeSource();
URL classLocation = codeSource.getLocation();
userClassUrls.add(classLocation);
}
StringTokenizer tokenString = new StringTokenizer(System.getProperty("java.class.path"), File.pathSeparator);
String pathIgnore = System.getProperty("java.home");
if (pathIgnore == null) {
pathIgnore = classLocation.toString();
pathIgnore = userClassUrls.iterator().next().toString();
}
List<URL> urls = new ArrayList<>();
while (tokenString.hasMoreElements()) {
String value = tokenString.nextToken();
URL itemLocation = new File(value).toURI().toURL();
if (!itemLocation.equals(classLocation) && itemLocation.toString().indexOf(pathIgnore) >= 0) {
if (!userClassUrls.contains(itemLocation) && itemLocation.toString().indexOf(pathIgnore) >= 0) {
urls.add(itemLocation);
}
}
URL[] urlArray = urls.toArray(new URL[urls.size()]);
ClassLoader masterClassLoader = URLClassLoader.newInstance(urlArray, null);
ClassLoader appClassLoader = URLClassLoader.newInstance(new URL[]{classLocation}, masterClassLoader);
ClassLoader appClassLoader = URLClassLoader.newInstance(userClassUrls.toArray(new URL[0]), masterClassLoader);
return appClassLoader;
}
@ -85,7 +98,11 @@ public class ObjectInputStreamWithClassLoaderTest extends ActiveMQTestBase {
AnObject obj = new AnObjectImpl();
byte[] bytes = ObjectInputStreamWithClassLoaderTest.toBytes(obj);
ClassLoader testClassLoader = ObjectInputStreamWithClassLoaderTest.newClassLoader(obj.getClass());
//Class.isAnonymousClass() call used in ObjectInputStreamWithClassLoader
//need to access the enclosing class and its parent class of the obj
//i.e. ActiveMQTestBase and Assert.
ClassLoader testClassLoader = ObjectInputStreamWithClassLoaderTest.newClassLoader(
obj.getClass(), ActiveMQTestBase.class, Assert.class);
Thread.currentThread().setContextClassLoader(testClassLoader);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
@ -113,7 +130,8 @@ public class ObjectInputStreamWithClassLoaderTest extends ActiveMQTestBase {
originalProxy.setMyInt(100);
byte[] bytes = ObjectInputStreamWithClassLoaderTest.toBytes(originalProxy);
ClassLoader testClassLoader = ObjectInputStreamWithClassLoaderTest.newClassLoader(this.getClass());
ClassLoader testClassLoader = ObjectInputStreamWithClassLoaderTest.newClassLoader(this.getClass(),
ActiveMQTestBase.class, Assert.class);
Thread.currentThread().setContextClassLoader(testClassLoader);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStreamWithClassLoader ois = new ObjectInputStreamWithClassLoader(bais);
@ -132,6 +150,349 @@ public class ObjectInputStreamWithClassLoaderTest extends ActiveMQTestBase {
}
@Test
public void testWhiteBlackList() throws Exception {
File serailizeFile = new File(temporaryFolder.getRoot(), "testclass.bin");
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(serailizeFile));
try {
outputStream.writeObject(new TestClass1());
outputStream.flush();
}
finally {
outputStream.close();
}
//default
assertNull(readSerializedObject(null, null, serailizeFile));
//white list
String whiteList = "org.apache.activemq.artemis.tests.unit.util.deserialization";
assertNull(readSerializedObject(whiteList, null, serailizeFile));
whiteList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1";
assertNull(readSerializedObject(whiteList, null, serailizeFile));
whiteList = "some.other.package";
Exception result = readSerializedObject(whiteList, null, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
//blacklist
String blackList = "org.apache.activemq.artemis.tests.unit.util";
result = readSerializedObject(null, blackList, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
blackList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1";
result = readSerializedObject(null, blackList, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
blackList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg2";
result = readSerializedObject(null, blackList, serailizeFile);
assertNull(result);
blackList = "some.other.package";
whiteList = "some.other.package1";
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
//blacklist priority
blackList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1, some.other.package";
whiteList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1";
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
blackList = "org.apache.activemq.artemis.tests.unit, some.other.package";
whiteList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1";
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
blackList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.pkg2, some.other.package";
whiteList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1";
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertNull(result);
blackList = "some.other.package, org.apache.activemq.artemis.tests.unit.util.deserialization.pkg2";
whiteList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1";
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertNull(result);
//wildcard
blackList = "*";
whiteList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1";
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
blackList = "*";
whiteList = "*";
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
result = readSerializedObject(whiteList, null, serailizeFile);
assertNull(result);
}
@Test
public void testWhiteBlackListAgainstArrayObject() throws Exception {
File serailizeFile = new File(temporaryFolder.getRoot(), "testclass.bin");
TestClass1[] sourceObject = new TestClass1[]{new TestClass1()};
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(serailizeFile));
try {
outputStream.writeObject(sourceObject);
outputStream.flush();
}
finally {
outputStream.close();
}
//default ok
String blackList = null;
String whiteList = null;
Object result = readSerializedObject(whiteList, blackList, serailizeFile);
assertNull(result);
//now blacklist TestClass1
blackList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.TestClass1";
whiteList = null;
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
//now whitelist TestClass1, it should pass.
blackList = null;
whiteList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.TestClass1";
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertNull(result);
}
@Test
public void testWhiteBlackListAgainstListObject() throws Exception {
File serailizeFile = new File(temporaryFolder.getRoot(), "testclass.bin");
List<TestClass1> sourceObject = new ArrayList<>();
sourceObject.add(new TestClass1());
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(serailizeFile));
try {
outputStream.writeObject(sourceObject);
outputStream.flush();
}
finally {
outputStream.close();
}
//default ok
String blackList = null;
String whiteList = null;
Object result = readSerializedObject(whiteList, blackList, serailizeFile);
assertNull(result);
//now blacklist TestClass1
blackList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.TestClass1";
whiteList = null;
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
//now whitelist TestClass1, should fail because the List type is not allowed
blackList = null;
whiteList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.TestClass1";
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
//now add List to white list, it should pass
blackList = null;
whiteList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.TestClass1," +
"java.util.ArrayList";
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertNull(result);
}
@Test
public void testWhiteBlackListAgainstListMapObject() throws Exception {
File serailizeFile = new File(temporaryFolder.getRoot(), "testclass.bin");
Map<TestClass1, TestClass2> sourceObject = new HashMap<>();
sourceObject.put(new TestClass1(), new TestClass2());
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(serailizeFile));
try {
outputStream.writeObject(sourceObject);
outputStream.flush();
}
finally {
outputStream.close();
}
String blackList = null;
String whiteList = null;
Object result = readSerializedObject(whiteList, blackList, serailizeFile);
assertNull(result);
//now blacklist the key
blackList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.TestClass1";
whiteList = null;
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
//now blacklist the value
blackList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.TestClass2";
whiteList = null;
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
//now white list the key, should fail too because value is forbidden
blackList = null;
whiteList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.TestClass1";
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
//now white list the value, should fail too because the key is forbidden
blackList = null;
whiteList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.TestClass2";
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
//both key and value are in the whitelist, it should fail because HashMap not permitted
blackList = null;
whiteList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.TestClass1," +
"org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.TestClass2";
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
//now add HashMap, test should pass.
blackList = null;
whiteList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.TestClass1," +
"org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.TestClass2," +
"java.util.HashMap";
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertNull(result);
}
@Test
public void testWhiteBlackListAnonymousObject() throws Exception {
File serailizeFile = new File(temporaryFolder.getRoot(), "testclass.bin");
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(serailizeFile));
try {
Serializable object = EnclosingClass.anonymousObject;
assertTrue(object.getClass().isAnonymousClass());
outputStream.writeObject(object);
outputStream.flush();
}
finally {
outputStream.close();
}
//default
String blackList = null;
String whiteList = null;
assertNull(readSerializedObject(whiteList, blackList, serailizeFile));
//forbidden by specifying the enclosing class
blackList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.EnclosingClass";
Object result = readSerializedObject(whiteList, blackList, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
//do it in whiteList
blackList = null;
whiteList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.EnclosingClass";
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertNull(result);
}
@Test
public void testWhiteBlackListLocalObject() throws Exception {
File serailizeFile = new File(temporaryFolder.getRoot(), "testclass.bin");
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(serailizeFile));
try {
Object object = EnclosingClass.getLocalObject();
assertTrue(object.getClass().isLocalClass());
outputStream.writeObject(object);
outputStream.flush();
}
finally {
outputStream.close();
}
//default
String blackList = null;
String whiteList = null;
assertNull(readSerializedObject(whiteList, blackList, serailizeFile));
//forbidden by specifying the enclosing class
blackList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.EnclosingClass";
Object result = readSerializedObject(whiteList, blackList, serailizeFile);
assertTrue(result instanceof ClassNotFoundException);
//do it in whiteList
blackList = null;
whiteList = "org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1.EnclosingClass";
result = readSerializedObject(whiteList, blackList, serailizeFile);
assertNull(result);
}
@Test
public void testWhiteBlackListSystemProperty() throws Exception {
File serailizeFile = new File(temporaryFolder.getRoot(), "testclass.bin");
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(serailizeFile));
try {
outputStream.writeObject(new TestClass1());
outputStream.flush();
}
finally {
outputStream.close();
}
System.setProperty(ObjectInputStreamWithClassLoader.BLACKLIST_PROPERTY, "system.defined.black.list");
System.setProperty(ObjectInputStreamWithClassLoader.WHITELIST_PROPERTY, "system.defined.white.list");
try {
ObjectInputStreamWithClassLoader ois = new ObjectInputStreamWithClassLoader(new FileInputStream(serailizeFile));
String bList = ois.getBlackList();
String wList = ois.getWhiteList();
assertEquals("wrong black list: " + bList, "system.defined.black.list", bList);
assertEquals("wrong white list: " + wList, "system.defined.white.list", wList);
ois.close();
}
finally {
System.clearProperty(ObjectInputStreamWithClassLoader.BLACKLIST_PROPERTY);
System.clearProperty(ObjectInputStreamWithClassLoader.WHITELIST_PROPERTY);
}
}
private Exception readSerializedObject(String whiteList, String blackList, File serailizeFile) {
Exception result = null;
ObjectInputStreamWithClassLoader ois = null;
try {
ois = new ObjectInputStreamWithClassLoader(new FileInputStream(serailizeFile));
ois.setWhiteList(whiteList);
ois.setBlackList(blackList);
ois.readObject();
}
catch (Exception e) {
result = e;
}
finally {
try {
ois.close();
}
catch (IOException e) {
result = e;
}
}
return result;
}
// Package protected ---------------------------------------------
// Protected -----------------------------------------------------

View File

@ -0,0 +1,31 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1;
import java.io.Serializable;
public class EnclosingClass implements Serializable {
public static Serializable anonymousObject = new Serializable() {
};
public static Object getLocalObject() {
class LocalClass implements Serializable {
}
return new LocalClass();
}
}

View File

@ -0,0 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1;
import java.io.Serializable;
public class TestClass1 implements Serializable {
}

View File

@ -0,0 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.tests.unit.util.deserialization.pkg1;
import java.io.Serializable;
public class TestClass2 implements Serializable {
}