This closes #707
This commit is contained in:
commit
2539e6f2ab
|
@ -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;
|
||||
|
|
|
@ -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 --------------------------------------------------------------------------------
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<>();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {
|
||||
}
|
|
@ -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 {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>";
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 -----------------------------------------------------
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
}
|
|
@ -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 {
|
||||
}
|
Loading…
Reference in New Issue