ARTEMIS-770 AMQP Message Transformer refactor
Refactor the AMQP Message transformers both for better performance and also to fix a number of issues with the transformers creating inbound and outbound messages with incorrectly mapped values or extra data appended where it should not be.
This commit is contained in:
parent
6f6d9845fe
commit
62627bf2ee
|
@ -21,7 +21,6 @@ import java.util.concurrent.Executor;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQException;
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQQueueExistsException;
|
||||
import org.apache.activemq.artemis.api.core.SimpleString;
|
||||
|
@ -37,6 +36,7 @@ import org.apache.activemq.artemis.core.server.ServerSession;
|
|||
import org.apache.activemq.artemis.core.server.impl.ServerConsumerImpl;
|
||||
import org.apache.activemq.artemis.core.transaction.Transaction;
|
||||
import org.apache.activemq.artemis.jms.client.ActiveMQConnection;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.ProtonMessageConverter;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.message.EncodedMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.exceptions.ActiveMQAMQPException;
|
||||
import org.apache.activemq.artemis.protocol.amqp.exceptions.ActiveMQAMQPInternalErrorException;
|
||||
|
@ -59,9 +59,10 @@ import org.apache.qpid.proton.amqp.messaging.Accepted;
|
|||
import org.apache.qpid.proton.amqp.messaging.Rejected;
|
||||
import org.apache.qpid.proton.amqp.transport.AmqpError;
|
||||
import org.apache.qpid.proton.amqp.transport.ErrorCondition;
|
||||
import org.apache.qpid.proton.codec.WritableBuffer;
|
||||
import org.apache.qpid.proton.engine.Delivery;
|
||||
import org.apache.qpid.proton.engine.Receiver;
|
||||
import org.apache.qpid.proton.message.ProtonJMessage;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
public class AMQPSessionCallback implements SessionCallback {
|
||||
|
@ -259,8 +260,11 @@ public class AMQPSessionCallback implements SessionCallback {
|
|||
}
|
||||
}
|
||||
|
||||
public ProtonJMessage encodeMessage(Object message, int deliveryCount) throws Exception {
|
||||
return (ProtonJMessage) manager.getConverter().outbound((ServerMessage) message, deliveryCount);
|
||||
public long encodeMessage(Object message, int deliveryCount, WritableBuffer buffer) throws Exception {
|
||||
ProtonMessageConverter converter = (ProtonMessageConverter) manager.getConverter();
|
||||
|
||||
// The Proton variant accepts a WritableBuffer to allow for a faster more direct encode.
|
||||
return (long) converter.outbound((ServerMessage) message, deliveryCount, buffer);
|
||||
}
|
||||
|
||||
public String tempQueueName() {
|
||||
|
|
|
@ -1,148 +0,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.
|
||||
*/
|
||||
package org.apache.activemq.artemis.protocol.amqp.converter;
|
||||
|
||||
import javax.jms.BytesMessage;
|
||||
import javax.jms.Destination;
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.MapMessage;
|
||||
import javax.jms.Message;
|
||||
import javax.jms.ObjectMessage;
|
||||
import javax.jms.StreamMessage;
|
||||
import javax.jms.TextMessage;
|
||||
|
||||
import org.apache.activemq.artemis.core.buffers.impl.ResetLimitWrappedActiveMQBuffer;
|
||||
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.protocol.amqp.converter.jms.ServerDestination;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSBytesMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMapMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSObjectMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSStreamMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSTextMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.message.JMSVendor;
|
||||
import org.apache.activemq.artemis.utils.IDGenerator;
|
||||
|
||||
public class ActiveMQJMSVendor implements JMSVendor {
|
||||
|
||||
private final IDGenerator serverGenerator;
|
||||
|
||||
ActiveMQJMSVendor(IDGenerator idGenerator) {
|
||||
this.serverGenerator = idGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BytesMessage createBytesMessage() {
|
||||
return new ServerJMSBytesMessage(newMessage(org.apache.activemq.artemis.api.core.Message.BYTES_TYPE), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamMessage createStreamMessage() {
|
||||
return new ServerJMSStreamMessage(newMessage(org.apache.activemq.artemis.api.core.Message.STREAM_TYPE), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message createMessage() {
|
||||
return new ServerJMSMessage(newMessage(org.apache.activemq.artemis.api.core.Message.DEFAULT_TYPE), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextMessage createTextMessage() {
|
||||
return new ServerJMSTextMessage(newMessage(org.apache.activemq.artemis.api.core.Message.TEXT_TYPE), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectMessage createObjectMessage() {
|
||||
return new ServerJMSObjectMessage(newMessage(org.apache.activemq.artemis.api.core.Message.OBJECT_TYPE), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapMessage createMapMessage() {
|
||||
return new ServerJMSMapMessage(newMessage(org.apache.activemq.artemis.api.core.Message.MAP_TYPE), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJMSXUserID(Message message, String s) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Destination createDestination(String name) {
|
||||
return new ServerDestination(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJMSXGroupID(Message message, String s) {
|
||||
try {
|
||||
message.setStringProperty("_AMQ_GROUP_ID", s);
|
||||
} catch (JMSException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJMSXGroupSequence(Message message, int i) {
|
||||
try {
|
||||
message.setIntProperty("JMSXGroupSeq", i);
|
||||
} catch (JMSException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJMSXDeliveryCount(Message message, long l) {
|
||||
try {
|
||||
message.setLongProperty("JMSXDeliveryCount", l);
|
||||
} catch (JMSException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public ServerJMSMessage wrapMessage(int messageType, ServerMessage wrapped, int deliveryCount) {
|
||||
switch (messageType) {
|
||||
case org.apache.activemq.artemis.api.core.Message.STREAM_TYPE:
|
||||
return new ServerJMSStreamMessage(wrapped, deliveryCount);
|
||||
case org.apache.activemq.artemis.api.core.Message.BYTES_TYPE:
|
||||
return new ServerJMSBytesMessage(wrapped, deliveryCount);
|
||||
case org.apache.activemq.artemis.api.core.Message.MAP_TYPE:
|
||||
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
|
||||
public String toAddress(Destination destination) {
|
||||
if (destination instanceof ActiveMQDestination) {
|
||||
return ((ActiveMQDestination) destination).getAddress();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ServerMessageImpl newMessage(byte messageType) {
|
||||
ServerMessageImpl message = new ServerMessageImpl(serverGenerator.generateID(), 512);
|
||||
message.setType(messageType);
|
||||
((ResetLimitWrappedActiveMQBuffer) message.getBodyBuffer()).setMessage(null);
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
|
@ -16,91 +16,86 @@
|
|||
*/
|
||||
package org.apache.activemq.artemis.protocol.amqp.converter;
|
||||
|
||||
import javax.jms.BytesMessage;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_NATIVE;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.jms.BytesMessage;
|
||||
|
||||
import org.apache.activemq.artemis.core.client.ActiveMQClientLogger;
|
||||
import org.apache.activemq.artemis.core.server.ServerMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSBytesMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPNativeOutboundTransformer;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.message.EncodedMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.message.InboundTransformer;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.message.JMSMappingInboundTransformer;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.message.JMSMappingOutboundTransformer;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.message.OutboundTransformer;
|
||||
import org.apache.activemq.artemis.protocol.amqp.util.NettyWritable;
|
||||
import org.apache.activemq.artemis.spi.core.protocol.MessageConverter;
|
||||
import org.apache.activemq.artemis.utils.IDGenerator;
|
||||
import org.apache.qpid.proton.codec.WritableBuffer;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
public class ProtonMessageConverter implements MessageConverter {
|
||||
|
||||
ActiveMQJMSVendor activeMQJMSVendor;
|
||||
|
||||
private final String prefixVendor;
|
||||
|
||||
public ProtonMessageConverter(IDGenerator idGenerator) {
|
||||
activeMQJMSVendor = new ActiveMQJMSVendor(idGenerator);
|
||||
inboundTransformer = new JMSMappingInboundTransformer(activeMQJMSVendor);
|
||||
outboundTransformer = new JMSMappingOutboundTransformer(activeMQJMSVendor);
|
||||
prefixVendor = outboundTransformer.getPrefixVendor();
|
||||
inboundTransformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
outboundTransformer = new JMSMappingOutboundTransformer(idGenerator);
|
||||
}
|
||||
|
||||
private final InboundTransformer inboundTransformer;
|
||||
private final JMSMappingOutboundTransformer outboundTransformer;
|
||||
private final OutboundTransformer outboundTransformer;
|
||||
|
||||
@Override
|
||||
public ServerMessage inbound(Object messageSource) throws Exception {
|
||||
ServerJMSMessage jmsMessage = inboundJMSType((EncodedMessage) messageSource);
|
||||
|
||||
return (ServerMessage) jmsMessage.getInnerMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Just create the JMS Part of the inbound (for testing)
|
||||
*
|
||||
* @param messageSource
|
||||
* @return
|
||||
* @throws Exception https://issues.jboss.org/browse/ENTMQ-1560
|
||||
*/
|
||||
public ServerJMSMessage inboundJMSType(EncodedMessage messageSource) throws Exception {
|
||||
EncodedMessage encodedMessageSource = messageSource;
|
||||
EncodedMessage encodedMessageSource = (EncodedMessage) messageSource;
|
||||
ServerJMSMessage transformedMessage = null;
|
||||
|
||||
InboundTransformer transformer = inboundTransformer;
|
||||
|
||||
while (transformer != null) {
|
||||
try {
|
||||
transformedMessage = (ServerJMSMessage) transformer.transform(encodedMessageSource);
|
||||
break;
|
||||
transformedMessage = inboundTransformer.transform(encodedMessageSource);
|
||||
} catch (Exception e) {
|
||||
ActiveMQClientLogger.LOGGER.debug("Transform of message using [{}] transformer, failed" + inboundTransformer.getTransformerName());
|
||||
ActiveMQClientLogger.LOGGER.trace("Transformation error:", e);
|
||||
|
||||
transformer = transformer.getFallbackTransformer();
|
||||
}
|
||||
}
|
||||
|
||||
if (transformedMessage == null) {
|
||||
throw new IOException("Failed to transform incoming delivery, skipping.");
|
||||
}
|
||||
|
||||
transformedMessage.encode();
|
||||
|
||||
return transformedMessage;
|
||||
return (ServerMessage) transformedMessage.getInnerMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object outbound(ServerMessage messageOutbound, int deliveryCount) throws Exception {
|
||||
ServerJMSMessage jmsMessage = activeMQJMSVendor.wrapMessage(messageOutbound.getType(), messageOutbound, deliveryCount);
|
||||
// Useful for testing but not recommended for real life use.
|
||||
ByteBuf nettyBuffer = Unpooled.buffer(1024);
|
||||
NettyWritable buffer = new NettyWritable(nettyBuffer);
|
||||
long messageFormat = (long) outbound(messageOutbound, deliveryCount, buffer);
|
||||
|
||||
EncodedMessage encoded = new EncodedMessage(messageFormat, nettyBuffer.array(), nettyBuffer.arrayOffset() + nettyBuffer.readerIndex(),
|
||||
nettyBuffer.readableBytes());
|
||||
|
||||
return encoded;
|
||||
}
|
||||
|
||||
public Object outbound(ServerMessage messageOutbound, int deliveryCount, WritableBuffer buffer) throws Exception {
|
||||
ServerJMSMessage jmsMessage = AMQPMessageSupport.wrapMessage(messageOutbound.getType(), messageOutbound, deliveryCount);
|
||||
|
||||
jmsMessage.decode();
|
||||
|
||||
if (jmsMessage.getBooleanProperty(prefixVendor + "NATIVE")) {
|
||||
if (jmsMessage.getBooleanProperty(JMS_AMQP_NATIVE)) {
|
||||
if (jmsMessage instanceof BytesMessage) {
|
||||
return AMQPNativeOutboundTransformer.transform(outboundTransformer, (BytesMessage) jmsMessage);
|
||||
return AMQPNativeOutboundTransformer.transform(outboundTransformer, (ServerJMSBytesMessage) jmsMessage, buffer);
|
||||
} else {
|
||||
return null;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return outboundTransformer.convert(jmsMessage);
|
||||
return outboundTransformer.transform(jmsMessage, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,13 @@
|
|||
*/
|
||||
package org.apache.activemq.artemis.protocol.amqp.converter.jms;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import javax.jms.DeliveryMode;
|
||||
import javax.jms.Destination;
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.Message;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQException;
|
||||
|
@ -47,6 +48,10 @@ public class ServerJMSMessage implements Message {
|
|||
this.deliveryCount = deliveryCount;
|
||||
}
|
||||
|
||||
public int getDeliveryCount() {
|
||||
return deliveryCount;
|
||||
}
|
||||
|
||||
private ActiveMQBuffer readBodyBuffer;
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,31 +16,20 @@
|
|||
*/
|
||||
package org.apache.activemq.artemis.protocol.amqp.converter.jms;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.ObjectMessage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.apache.activemq.artemis.api.core.Message;
|
||||
import org.apache.activemq.artemis.core.message.impl.MessageInternal;
|
||||
import org.apache.activemq.artemis.utils.ObjectInputStreamWithClassLoader;
|
||||
import org.apache.qpid.proton.amqp.Binary;
|
||||
|
||||
public class ServerJMSObjectMessage extends ServerJMSMessage implements ObjectMessage {
|
||||
|
||||
private static final String DEFAULT_WHITELIST;
|
||||
private static final String DEFAULT_BLACKLIST;
|
||||
public static final byte TYPE = Message.OBJECT_TYPE;
|
||||
|
||||
static {
|
||||
DEFAULT_WHITELIST = System.getProperty(ObjectInputStreamWithClassLoader.WHITELIST_PROPERTY, "java.lang,java.math,javax.security,java.util,org.apache.activemq,org.apache.qpid.proton.amqp");
|
||||
|
||||
DEFAULT_BLACKLIST = System.getProperty(ObjectInputStreamWithClassLoader.BLACKLIST_PROPERTY, null);
|
||||
}
|
||||
|
||||
public static final byte TYPE = Message.STREAM_TYPE;
|
||||
|
||||
private Serializable object;
|
||||
private Binary payload;
|
||||
|
||||
public ServerJMSObjectMessage(MessageInternal message, int deliveryCount) {
|
||||
super(message, deliveryCount);
|
||||
|
@ -48,23 +37,27 @@ public class ServerJMSObjectMessage extends ServerJMSMessage implements ObjectMe
|
|||
|
||||
@Override
|
||||
public void setObject(Serializable object) throws JMSException {
|
||||
this.object = object;
|
||||
throw new UnsupportedOperationException("Cannot set Object on this internal message");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable getObject() throws JMSException {
|
||||
return object;
|
||||
throw new UnsupportedOperationException("Cannot set Object on this internal message");
|
||||
}
|
||||
|
||||
public void setSerializedForm(Binary payload) {
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public Binary getSerializedForm() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode() throws Exception {
|
||||
super.encode();
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ObjectOutputStream ous = new ObjectOutputStream(out);
|
||||
ous.writeObject(object);
|
||||
byte[] src = out.toByteArray();
|
||||
getInnerMessage().getBodyBuffer().writeInt(src.length);
|
||||
getInnerMessage().getBodyBuffer().writeBytes(src);
|
||||
getInnerMessage().getBodyBuffer().writeInt(payload.getLength());
|
||||
getInnerMessage().getBodyBuffer().writeBytes(payload.getArray(), payload.getArrayOffset(), payload.getLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -73,10 +66,6 @@ public class ServerJMSObjectMessage extends ServerJMSMessage implements ObjectMe
|
|||
int size = getInnerMessage().getBodyBuffer().readInt();
|
||||
byte[] bytes = new byte[size];
|
||||
getInnerMessage().getBodyBuffer().readBytes(bytes);
|
||||
try (ObjectInputStreamWithClassLoader ois = new ObjectInputStreamWithClassLoader(new ByteArrayInputStream(bytes))) {
|
||||
ois.setWhiteList(DEFAULT_WHITELIST);
|
||||
ois.setBlackList(DEFAULT_BLACKLIST);
|
||||
object = (Serializable) ois.readObject();
|
||||
}
|
||||
payload = new Binary(bytes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* 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.protocol.amqp.converter.message;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.IllegalCharsetNameException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.UnsupportedCharsetException;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.apache.activemq.artemis.protocol.amqp.exceptions.ActiveMQAMQPInvalidContentTypeException;
|
||||
|
||||
public final class AMQPContentTypeSupport {
|
||||
|
||||
private static final String UTF_8 = "UTF-8";
|
||||
private static final String CHARSET = "charset";
|
||||
private static final String TEXT = "text";
|
||||
private static final String APPLICATION = "application";
|
||||
private static final String JAVASCRIPT = "javascript";
|
||||
private static final String XML = "xml";
|
||||
private static final String XML_VARIANT = "+xml";
|
||||
private static final String JSON = "json";
|
||||
private static final String JSON_VARIANT = "+json";
|
||||
private static final String XML_DTD = "xml-dtd";
|
||||
private static final String ECMASCRIPT = "ecmascript";
|
||||
|
||||
/**
|
||||
* @param contentType
|
||||
* the contentType of the received message
|
||||
* @return the character set to use, or null if not to treat the message as text
|
||||
* @throws ActiveMQAMQPInvalidContentTypeException
|
||||
* if the content-type is invalid in some way.
|
||||
*/
|
||||
public static Charset parseContentTypeForTextualCharset(final String contentType) throws ActiveMQAMQPInvalidContentTypeException {
|
||||
if (contentType == null || contentType.trim().isEmpty()) {
|
||||
throw new ActiveMQAMQPInvalidContentTypeException("Content type can't be null or empty");
|
||||
}
|
||||
|
||||
int subTypeSeparator = contentType.indexOf("/");
|
||||
if (subTypeSeparator == -1) {
|
||||
throw new ActiveMQAMQPInvalidContentTypeException("Content type has no '/' separator: " + contentType);
|
||||
}
|
||||
|
||||
final String type = contentType.substring(0, subTypeSeparator).toLowerCase().trim();
|
||||
|
||||
String subTypePart = contentType.substring(subTypeSeparator + 1).toLowerCase().trim();
|
||||
|
||||
String parameterPart = null;
|
||||
int parameterSeparator = subTypePart.indexOf(";");
|
||||
if (parameterSeparator != -1) {
|
||||
if (parameterSeparator < subTypePart.length() - 1) {
|
||||
parameterPart = contentType.substring(subTypeSeparator + 1).toLowerCase().trim();
|
||||
}
|
||||
subTypePart = subTypePart.substring(0, parameterSeparator).trim();
|
||||
}
|
||||
|
||||
if (subTypePart.isEmpty()) {
|
||||
throw new ActiveMQAMQPInvalidContentTypeException("Content type has no subtype after '/'" + contentType);
|
||||
}
|
||||
|
||||
final String subType = subTypePart;
|
||||
|
||||
if (isTextual(type, subType)) {
|
||||
String charset = findCharset(parameterPart);
|
||||
if (charset == null) {
|
||||
charset = UTF_8;
|
||||
}
|
||||
|
||||
if (UTF_8.equals(charset)) {
|
||||
return StandardCharsets.UTF_8;
|
||||
} else {
|
||||
try {
|
||||
return Charset.forName(charset);
|
||||
} catch (IllegalCharsetNameException icne) {
|
||||
throw new ActiveMQAMQPInvalidContentTypeException("Illegal charset: " + charset);
|
||||
} catch (UnsupportedCharsetException uce) {
|
||||
throw new ActiveMQAMQPInvalidContentTypeException("Unsupported charset: " + charset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// ----- Internal Content Type utilities ----------------------------------//
|
||||
|
||||
private static boolean isTextual(String type, String subType) {
|
||||
if (TEXT.equals(type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (APPLICATION.equals(type)) {
|
||||
if (XML.equals(subType) || JSON.equals(subType) || JAVASCRIPT.equals(subType) || subType.endsWith(XML_VARIANT) || subType.endsWith(JSON_VARIANT)
|
||||
|| XML_DTD.equals(subType) || ECMASCRIPT.equals(subType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String findCharset(String paramaterPart) {
|
||||
String charset = null;
|
||||
|
||||
if (paramaterPart != null) {
|
||||
StringTokenizer tokenizer = new StringTokenizer(paramaterPart, ";");
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
String parameter = tokenizer.nextToken().trim();
|
||||
int eqIndex = parameter.indexOf('=');
|
||||
if (eqIndex != -1) {
|
||||
String name = parameter.substring(0, eqIndex);
|
||||
if (CHARSET.equalsIgnoreCase(name.trim())) {
|
||||
String value = unquote(parameter.substring(eqIndex + 1));
|
||||
|
||||
charset = value.toUpperCase();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return charset;
|
||||
}
|
||||
|
||||
private static String unquote(String s) {
|
||||
if (s.length() > 1 && (s.startsWith("\"") && s.endsWith("\""))) {
|
||||
return s.substring(1, s.length() - 1);
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,24 +28,29 @@ import org.apache.qpid.proton.amqp.Binary;
|
|||
import org.apache.qpid.proton.amqp.UnsignedLong;
|
||||
|
||||
/**
|
||||
* Helper class for identifying and converting message-id and correlation-id values between
|
||||
* the AMQP types and the Strings values used by JMS.
|
||||
* Helper class for identifying and converting message-id and correlation-id values between the
|
||||
* AMQP types and the Strings values used by JMS.
|
||||
* <p>
|
||||
* <p>AMQP messages allow for 4 types of message-id/correlation-id: message-id-string, message-id-binary,
|
||||
* message-id-uuid, or message-id-ulong. In order to accept or return a string representation of these
|
||||
* for interoperability with other AMQP clients, the following encoding can be used after removing or
|
||||
* before adding the "ID:" prefix used for a JMSMessageID value:<br>
|
||||
* <p>
|
||||
* AMQP messages allow for 4 types of message-id/correlation-id: message-id-string,
|
||||
* message-id-binary, message-id-uuid, or message-id-ulong. In order to accept or return a
|
||||
* string representation of these for interoperability with other AMQP clients, the following
|
||||
* encoding can be used after removing or before adding the "ID:" prefix used for a JMSMessageID
|
||||
* value:<br>
|
||||
* <p>
|
||||
* {@literal "AMQP_BINARY:<hex representation of binary content>"}<br>
|
||||
* {@literal "AMQP_UUID:<string representation of uuid>"}<br>
|
||||
* {@literal "AMQP_ULONG:<string representation of ulong>"}<br>
|
||||
* {@literal "AMQP_STRING:<string>"}<br>
|
||||
* <p>
|
||||
* <p>The AMQP_STRING encoding exists only for escaping message-id-string values that happen to begin
|
||||
* with one of the encoding prefixes (including AMQP_STRING itself). It MUST NOT be used otherwise.
|
||||
* <p>
|
||||
* <p>When provided a string for conversion which attempts to identify itself as an encoded binary, uuid, or
|
||||
* ulong but can't be converted into the indicated format, an exception will be thrown.
|
||||
* The AMQP_STRING encoding exists only for escaping message-id-string values that happen to
|
||||
* begin with one of the encoding prefixes (including AMQP_STRING itself). It MUST NOT be used
|
||||
* otherwise.
|
||||
* <p>
|
||||
* <p>
|
||||
* When provided a string for conversion which attempts to identify itself as an encoded binary,
|
||||
* uuid, or ulong but can't be converted into the indicated format, an exception will be thrown.
|
||||
*/
|
||||
public class AMQPMessageIdHelper {
|
||||
|
||||
|
@ -63,11 +68,12 @@ public class AMQPMessageIdHelper {
|
|||
private static final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
|
||||
|
||||
/**
|
||||
* Takes the provided AMQP messageId style object, and convert it to a base string.
|
||||
* Encodes type information as a prefix where necessary to convey or escape the type
|
||||
* of the provided object.
|
||||
* Takes the provided AMQP messageId style object, and convert it to a base string. Encodes
|
||||
* type information as a prefix where necessary to convey or escape the type of the provided
|
||||
* object.
|
||||
*
|
||||
* @param messageId the raw messageId object to process
|
||||
* @param messageId
|
||||
* the raw messageId object to process
|
||||
* @return the base string to be used in creating the actual id.
|
||||
*/
|
||||
public String toBaseMessageIdString(Object messageId) {
|
||||
|
@ -106,9 +112,12 @@ public class AMQPMessageIdHelper {
|
|||
* Takes the provided base id string and return the appropriate amqp messageId style object.
|
||||
* Converts the type based on any relevant encoding information found as a prefix.
|
||||
*
|
||||
* @param baseId the object to be converted to an AMQP MessageId value.
|
||||
* @param baseId
|
||||
* the object to be converted to an AMQP MessageId value.
|
||||
* @return the AMQP messageId style object
|
||||
* @throws ActiveMQAMQPIllegalStateException if the provided baseId String indicates an encoded type but can't be converted to that type.
|
||||
* @throws ActiveMQAMQPIllegalStateException
|
||||
* if the provided baseId String indicates an encoded type but can't be converted to
|
||||
* that type.
|
||||
*/
|
||||
public Object toIdObject(String baseId) throws ActiveMQAMQPIllegalStateException {
|
||||
if (baseId == null) {
|
||||
|
@ -143,15 +152,17 @@ public class AMQPMessageIdHelper {
|
|||
* <p>
|
||||
* The hex characters may be upper or lower case.
|
||||
*
|
||||
* @param hexString string to convert to a binary value.
|
||||
* @param hexString
|
||||
* string to convert to a binary value.
|
||||
* @return a byte array containing the binary representation
|
||||
* @throws IllegalArgumentException if the provided String is a non-even length or contains
|
||||
* non-hex characters
|
||||
* @throws IllegalArgumentException
|
||||
* if the provided String is a non-even length or contains non-hex characters
|
||||
*/
|
||||
public byte[] convertHexStringToBinary(String hexString) throws IllegalArgumentException {
|
||||
int length = hexString.length();
|
||||
|
||||
// As each byte needs two characters in the hex encoding, the string must be an even length.
|
||||
// As each byte needs two characters in the hex encoding, the string must be an even
|
||||
// length.
|
||||
if (length % 2 != 0) {
|
||||
throw new IllegalArgumentException("The provided hex String must be an even length, but was of length " + length + ": " + hexString);
|
||||
}
|
||||
|
@ -177,7 +188,8 @@ public class AMQPMessageIdHelper {
|
|||
* <p>
|
||||
* The returned hex characters are upper-case.
|
||||
*
|
||||
* @param bytes the binary value to convert to a hex String instance.
|
||||
* @param bytes
|
||||
* the binary value to convert to a hex String instance.
|
||||
* @return a String containing a hex representation of the bytes
|
||||
*/
|
||||
public String convertBinaryToHexString(byte[] bytes) {
|
||||
|
@ -201,8 +213,7 @@ public class AMQPMessageIdHelper {
|
|||
// ----- Internal implementation ------------------------------------------//
|
||||
|
||||
private boolean hasTypeEncodingPrefix(String stringId) {
|
||||
return hasAmqpBinaryPrefix(stringId) || hasAmqpUuidPrefix(stringId) ||
|
||||
hasAmqpUlongPrefix(stringId) || hasAmqpStringPrefix(stringId);
|
||||
return hasAmqpBinaryPrefix(stringId) || hasAmqpUuidPrefix(stringId) || hasAmqpUlongPrefix(stringId) || hasAmqpStringPrefix(stringId);
|
||||
}
|
||||
|
||||
private boolean hasAmqpStringPrefix(String stringId) {
|
||||
|
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* 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.protocol.amqp.converter.message;
|
||||
|
||||
import static org.apache.activemq.artemis.api.core.Message.BYTES_TYPE;
|
||||
import static org.apache.activemq.artemis.api.core.Message.DEFAULT_TYPE;
|
||||
import static org.apache.activemq.artemis.api.core.Message.MAP_TYPE;
|
||||
import static org.apache.activemq.artemis.api.core.Message.OBJECT_TYPE;
|
||||
import static org.apache.activemq.artemis.api.core.Message.STREAM_TYPE;
|
||||
import static org.apache.activemq.artemis.api.core.Message.TEXT_TYPE;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.jms.Destination;
|
||||
import javax.jms.JMSException;
|
||||
|
||||
import org.apache.activemq.artemis.core.buffers.impl.ResetLimitWrappedActiveMQBuffer;
|
||||
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.protocol.amqp.converter.jms.ServerJMSBytesMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMapMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSObjectMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSStreamMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSTextMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.exceptions.ActiveMQAMQPInvalidContentTypeException;
|
||||
import org.apache.activemq.artemis.utils.IDGenerator;
|
||||
import org.apache.qpid.proton.amqp.Binary;
|
||||
import org.apache.qpid.proton.amqp.Symbol;
|
||||
import org.apache.qpid.proton.amqp.messaging.Data;
|
||||
import org.apache.qpid.proton.message.Message;
|
||||
|
||||
/**
|
||||
* Support class containing constant values and static methods that are used to map to / from
|
||||
* AMQP Message types being sent or received.
|
||||
*/
|
||||
public final class AMQPMessageSupport {
|
||||
|
||||
// Message Properties used to map AMQP to JMS and back
|
||||
|
||||
public static final String JMS_AMQP_PREFIX = "JMS_AMQP_";
|
||||
public static final int JMS_AMQP_PREFIX_LENGTH = JMS_AMQP_PREFIX.length();
|
||||
|
||||
public static final String MESSAGE_FORMAT = "MESSAGE_FORMAT";
|
||||
public static final String ORIGINAL_ENCODING = "ORIGINAL_ENCODING";
|
||||
public static final String NATIVE = "NATIVE";
|
||||
public static final String HEADER = "HEADER";
|
||||
public static final String PROPERTIES = "PROPERTIES";
|
||||
|
||||
public static final String FIRST_ACQUIRER = "FirstAcquirer";
|
||||
public static final String CONTENT_TYPE = "ContentType";
|
||||
public static final String CONTENT_ENCODING = "ContentEncoding";
|
||||
public static final String REPLYTO_GROUP_ID = "ReplyToGroupID";
|
||||
|
||||
public static final String DELIVERY_ANNOTATION_PREFIX = "DA_";
|
||||
public static final String MESSAGE_ANNOTATION_PREFIX = "MA_";
|
||||
public static final String FOOTER_PREFIX = "FT_";
|
||||
|
||||
public static final String JMS_AMQP_HEADER = JMS_AMQP_PREFIX + HEADER;
|
||||
public static final String JMS_AMQP_PROPERTIES = JMS_AMQP_PREFIX + PROPERTIES;
|
||||
public static final String JMS_AMQP_ORIGINAL_ENCODING = JMS_AMQP_PREFIX + ORIGINAL_ENCODING;
|
||||
public static final String JMS_AMQP_MESSAGE_FORMAT = JMS_AMQP_PREFIX + MESSAGE_FORMAT;
|
||||
public static final String JMS_AMQP_NATIVE = JMS_AMQP_PREFIX + NATIVE;
|
||||
public static final String JMS_AMQP_FIRST_ACQUIRER = JMS_AMQP_PREFIX + FIRST_ACQUIRER;
|
||||
public static final String JMS_AMQP_CONTENT_TYPE = JMS_AMQP_PREFIX + CONTENT_TYPE;
|
||||
public static final String JMS_AMQP_CONTENT_ENCODING = JMS_AMQP_PREFIX + CONTENT_ENCODING;
|
||||
public static final String JMS_AMQP_REPLYTO_GROUP_ID = JMS_AMQP_PREFIX + REPLYTO_GROUP_ID;
|
||||
public static final String JMS_AMQP_DELIVERY_ANNOTATION_PREFIX = JMS_AMQP_PREFIX + DELIVERY_ANNOTATION_PREFIX;
|
||||
public static final String JMS_AMQP_MESSAGE_ANNOTATION_PREFIX = JMS_AMQP_PREFIX + MESSAGE_ANNOTATION_PREFIX;
|
||||
public static final String JMS_AMQP_FOOTER_PREFIX = JMS_AMQP_PREFIX + FOOTER_PREFIX;
|
||||
|
||||
// Message body type definitions
|
||||
public static final Binary EMPTY_BINARY = new Binary(new byte[0]);
|
||||
public static final Data EMPTY_BODY = new Data(EMPTY_BINARY);
|
||||
|
||||
public static final short AMQP_UNKNOWN = 0;
|
||||
public static final short AMQP_NULL = 1;
|
||||
public static final short AMQP_DATA = 2;
|
||||
public static final short AMQP_SEQUENCE = 3;
|
||||
public static final short AMQP_VALUE_NULL = 4;
|
||||
public static final short AMQP_VALUE_STRING = 5;
|
||||
public static final short AMQP_VALUE_BINARY = 6;
|
||||
public static final short AMQP_VALUE_MAP = 7;
|
||||
public static final short AMQP_VALUE_LIST = 8;
|
||||
|
||||
/**
|
||||
* Content type used to mark Data sections as containing a serialized java object.
|
||||
*/
|
||||
public static final String SERIALIZED_JAVA_OBJECT_CONTENT_TYPE = "application/x-java-serialized-object";
|
||||
|
||||
/**
|
||||
* Content type used to mark Data sections as containing arbitrary bytes.
|
||||
*/
|
||||
public static final String OCTET_STREAM_CONTENT_TYPE = "application/octet-stream";
|
||||
|
||||
/**
|
||||
* Lookup and return the correct Proton Symbol instance based on the given key.
|
||||
*
|
||||
* @param key
|
||||
* the String value name of the Symbol to locate.
|
||||
*
|
||||
* @return the Symbol value that matches the given key.
|
||||
*/
|
||||
public static Symbol getSymbol(String key) {
|
||||
return Symbol.valueOf(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe way to access message annotations which will check internal structure and either
|
||||
* return the annotation if it exists or null if the annotation or any annotations are
|
||||
* present.
|
||||
*
|
||||
* @param key
|
||||
* the String key to use to lookup an annotation.
|
||||
* @param message
|
||||
* the AMQP message object that is being examined.
|
||||
*
|
||||
* @return the given annotation value or null if not present in the message.
|
||||
*/
|
||||
public static Object getMessageAnnotation(String key, Message message) {
|
||||
if (message != null && message.getMessageAnnotations() != null) {
|
||||
Map<Symbol, Object> annotations = message.getMessageAnnotations().getValue();
|
||||
return annotations.get(AMQPMessageSupport.getSymbol(key));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the content-type field of the properties section (if present) in the given
|
||||
* message matches the provided string (where null matches if there is no content type
|
||||
* present.
|
||||
*
|
||||
* @param contentType
|
||||
* content type string to compare against, or null if none
|
||||
* @param message
|
||||
* the AMQP message object that is being examined.
|
||||
*
|
||||
* @return true if content type matches
|
||||
*/
|
||||
public static boolean isContentType(String contentType, Message message) {
|
||||
if (contentType == null) {
|
||||
return message.getContentType() == null;
|
||||
} else {
|
||||
return contentType.equals(message.getContentType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contentType
|
||||
* the contentType of the received message
|
||||
* @return the character set to use, or null if not to treat the message as text
|
||||
*/
|
||||
public static Charset getCharsetForTextualContent(String contentType) {
|
||||
try {
|
||||
return AMQPContentTypeSupport.parseContentTypeForTextualCharset(contentType);
|
||||
} catch (ActiveMQAMQPInvalidContentTypeException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static ServerJMSMessage wrapMessage(int messageType, ServerMessage wrapped, int deliveryCount) {
|
||||
switch (messageType) {
|
||||
case STREAM_TYPE:
|
||||
return new ServerJMSStreamMessage(wrapped, deliveryCount);
|
||||
case BYTES_TYPE:
|
||||
return new ServerJMSBytesMessage(wrapped, deliveryCount);
|
||||
case MAP_TYPE:
|
||||
return new ServerJMSMapMessage(wrapped, deliveryCount);
|
||||
case TEXT_TYPE:
|
||||
return new ServerJMSTextMessage(wrapped, deliveryCount);
|
||||
case OBJECT_TYPE:
|
||||
return new ServerJMSObjectMessage(wrapped, deliveryCount);
|
||||
default:
|
||||
return new ServerJMSMessage(wrapped, deliveryCount);
|
||||
}
|
||||
}
|
||||
|
||||
public static String toAddress(Destination destination) {
|
||||
if (destination instanceof ActiveMQDestination) {
|
||||
return ((ActiveMQDestination) destination).getAddress();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ServerJMSBytesMessage createBytesMessage(IDGenerator idGenerator) {
|
||||
return new ServerJMSBytesMessage(newMessage(idGenerator, BYTES_TYPE), 0);
|
||||
}
|
||||
|
||||
public static ServerJMSMessage createBytesMessage(IDGenerator idGenerator, byte[] array, int arrayOffset, int length) throws JMSException {
|
||||
ServerJMSBytesMessage message = createBytesMessage(idGenerator);
|
||||
message.writeBytes(array, arrayOffset, length);
|
||||
return message;
|
||||
}
|
||||
|
||||
public static ServerJMSStreamMessage createStreamMessage(IDGenerator idGenerator) {
|
||||
return new ServerJMSStreamMessage(newMessage(idGenerator, STREAM_TYPE), 0);
|
||||
}
|
||||
|
||||
public static ServerJMSMessage createMessage(IDGenerator idGenerator) {
|
||||
return new ServerJMSMessage(newMessage(idGenerator, DEFAULT_TYPE), 0);
|
||||
}
|
||||
|
||||
public static ServerJMSTextMessage createTextMessage(IDGenerator idGenerator) {
|
||||
return new ServerJMSTextMessage(newMessage(idGenerator, TEXT_TYPE), 0);
|
||||
}
|
||||
|
||||
public static ServerJMSTextMessage createTextMessage(IDGenerator idGenerator, String text) throws JMSException {
|
||||
ServerJMSTextMessage message = createTextMessage(idGenerator);
|
||||
message.setText(text);
|
||||
return message;
|
||||
}
|
||||
|
||||
public static ServerJMSObjectMessage createObjectMessage(IDGenerator idGenerator) {
|
||||
return new ServerJMSObjectMessage(newMessage(idGenerator, OBJECT_TYPE), 0);
|
||||
}
|
||||
|
||||
public static ServerJMSMessage createObjectMessage(IDGenerator idGenerator, Binary serializedForm) throws JMSException {
|
||||
ServerJMSObjectMessage message = createObjectMessage(idGenerator);
|
||||
message.setSerializedForm(serializedForm);
|
||||
return message;
|
||||
}
|
||||
|
||||
public static ServerJMSMessage createObjectMessage(IDGenerator idGenerator, byte[] array, int offset, int length) throws JMSException {
|
||||
ServerJMSObjectMessage message = createObjectMessage(idGenerator);
|
||||
message.setSerializedForm(new Binary(array, offset, length));
|
||||
return message;
|
||||
}
|
||||
|
||||
public static ServerJMSMapMessage createMapMessage(IDGenerator idGenerator) {
|
||||
return new ServerJMSMapMessage(newMessage(idGenerator, MAP_TYPE), 0);
|
||||
}
|
||||
|
||||
public static ServerJMSMapMessage createMapMessage(IDGenerator idGenerator, Map<String, Object> content) throws JMSException {
|
||||
ServerJMSMapMessage message = createMapMessage(idGenerator);
|
||||
final Set<Map.Entry<String, Object>> set = content.entrySet();
|
||||
for (Map.Entry<String, Object> entry : set) {
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof Binary) {
|
||||
Binary binary = (Binary) value;
|
||||
value = Arrays.copyOfRange(binary.getArray(), binary.getArrayOffset(), binary.getLength());
|
||||
}
|
||||
message.setObject(entry.getKey(), value);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private static ServerMessageImpl newMessage(IDGenerator idGenerator, byte messageType) {
|
||||
ServerMessageImpl message = new ServerMessageImpl(idGenerator.generateID(), 512);
|
||||
message.setType(messageType);
|
||||
((ResetLimitWrappedActiveMQBuffer) message.getBodyBuffer()).setMessage(null);
|
||||
return message;
|
||||
}
|
||||
}
|
|
@ -16,8 +16,12 @@
|
|||
*/
|
||||
package org.apache.activemq.artemis.protocol.amqp.converter.message;
|
||||
|
||||
@Deprecated
|
||||
public class AMQPMessageTypes {
|
||||
|
||||
// TODO - Remove in future release as these are no longer used by the
|
||||
// inbound JMS Transformer.
|
||||
|
||||
public static final String AMQP_TYPE_KEY = "amqp:type";
|
||||
|
||||
public static final String AMQP_SEQUENCE = "amqp:sequence";
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/**
|
||||
/*
|
||||
* 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
|
||||
* <p>
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
*
|
||||
* 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.
|
||||
|
@ -16,12 +16,13 @@
|
|||
*/
|
||||
package org.apache.activemq.artemis.protocol.amqp.converter.message;
|
||||
|
||||
import javax.jms.Message;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMessage;
|
||||
import org.apache.activemq.artemis.utils.IDGenerator;
|
||||
|
||||
public class AMQPNativeInboundTransformer extends AMQPRawInboundTransformer {
|
||||
|
||||
public AMQPNativeInboundTransformer(JMSVendor vendor) {
|
||||
super(vendor);
|
||||
public AMQPNativeInboundTransformer(IDGenerator idGenerator) {
|
||||
super(idGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -31,16 +32,13 @@ public class AMQPNativeInboundTransformer extends AMQPRawInboundTransformer {
|
|||
|
||||
@Override
|
||||
public InboundTransformer getFallbackTransformer() {
|
||||
return new AMQPRawInboundTransformer(getVendor());
|
||||
return new AMQPRawInboundTransformer(idGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message transform(EncodedMessage amqpMessage) throws Exception {
|
||||
public ServerJMSMessage transform(EncodedMessage amqpMessage) throws Exception {
|
||||
org.apache.qpid.proton.message.Message amqp = amqpMessage.decode();
|
||||
|
||||
Message rc = super.transform(amqpMessage);
|
||||
|
||||
populateMessage(rc, amqp);
|
||||
return rc;
|
||||
return populateMessage(super.transform(amqpMessage), amqp);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/**
|
||||
/*
|
||||
* 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
|
||||
* <p>
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
*
|
||||
* 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.
|
||||
|
@ -16,24 +16,41 @@
|
|||
*/
|
||||
package org.apache.activemq.artemis.protocol.amqp.converter.message;
|
||||
|
||||
import javax.jms.BytesMessage;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import javax.jms.JMSException;
|
||||
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSBytesMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMessage;
|
||||
import org.apache.activemq.artemis.utils.IDGenerator;
|
||||
import org.apache.qpid.proton.amqp.UnsignedInteger;
|
||||
import org.apache.qpid.proton.amqp.messaging.Header;
|
||||
import org.apache.qpid.proton.codec.WritableBuffer;
|
||||
import org.apache.qpid.proton.message.ProtonJMessage;
|
||||
|
||||
public class AMQPNativeOutboundTransformer extends OutboundTransformer {
|
||||
|
||||
public AMQPNativeOutboundTransformer(JMSVendor vendor) {
|
||||
super(vendor);
|
||||
public AMQPNativeOutboundTransformer(IDGenerator idGenerator) {
|
||||
super(idGenerator);
|
||||
}
|
||||
|
||||
public static ProtonJMessage transform(OutboundTransformer options, BytesMessage msg) throws JMSException {
|
||||
byte[] data = new byte[(int) msg.getBodyLength()];
|
||||
msg.readBytes(data);
|
||||
msg.reset();
|
||||
int count = msg.getIntProperty("JMSXDeliveryCount");
|
||||
@Override
|
||||
public long transform(ServerJMSMessage message, WritableBuffer buffer) throws JMSException, UnsupportedEncodingException {
|
||||
if (message == null || !(message instanceof ServerJMSBytesMessage)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return transform(this, (ServerJMSBytesMessage) message, buffer);
|
||||
}
|
||||
|
||||
public static long transform(OutboundTransformer options, ServerJMSBytesMessage message, WritableBuffer buffer) throws JMSException {
|
||||
byte[] data = new byte[(int) message.getBodyLength()];
|
||||
message.readBytes(data);
|
||||
message.reset();
|
||||
|
||||
// The AMQP delivery-count field only includes prior failed delivery attempts,
|
||||
int amqpDeliveryCount = message.getDeliveryCount() - 1;
|
||||
if (amqpDeliveryCount >= 1) {
|
||||
|
||||
// decode...
|
||||
ProtonJMessage amqp = (ProtonJMessage) org.apache.qpid.proton.message.Message.Factory.create();
|
||||
|
@ -46,15 +63,18 @@ public class AMQPNativeOutboundTransformer extends OutboundTransformer {
|
|||
len -= decoded;
|
||||
}
|
||||
|
||||
// Update the DeliveryCount header...
|
||||
// The AMQP delivery-count field only includes prior failed delivery attempts,
|
||||
// whereas JMSXDeliveryCount includes the first/current delivery attempt. Subtract 1.
|
||||
if (amqp.getHeader() == null) {
|
||||
// Update the DeliveryCount header which might require adding a Header
|
||||
if (amqp.getHeader() == null && amqpDeliveryCount > 0) {
|
||||
amqp.setHeader(new Header());
|
||||
}
|
||||
|
||||
amqp.getHeader().setDeliveryCount(new UnsignedInteger(count - 1));
|
||||
amqp.getHeader().setDeliveryCount(new UnsignedInteger(amqpDeliveryCount));
|
||||
|
||||
return amqp;
|
||||
amqp.encode(buffer);
|
||||
} else {
|
||||
buffer.put(data, 0, data.length);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/**
|
||||
/*
|
||||
* 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
|
||||
* <p>
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
*
|
||||
* 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.
|
||||
|
@ -16,14 +16,21 @@
|
|||
*/
|
||||
package org.apache.activemq.artemis.protocol.amqp.converter.message;
|
||||
|
||||
import javax.jms.BytesMessage;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_MESSAGE_FORMAT;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_NATIVE;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.createBytesMessage;
|
||||
|
||||
import javax.jms.DeliveryMode;
|
||||
import javax.jms.Message;
|
||||
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSBytesMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMessage;
|
||||
import org.apache.activemq.artemis.utils.IDGenerator;
|
||||
|
||||
public class AMQPRawInboundTransformer extends InboundTransformer {
|
||||
|
||||
public AMQPRawInboundTransformer(JMSVendor vendor) {
|
||||
super(vendor);
|
||||
public AMQPRawInboundTransformer(IDGenerator idGenerator) {
|
||||
super(idGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -37,24 +44,19 @@ public class AMQPRawInboundTransformer extends InboundTransformer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Message transform(EncodedMessage amqpMessage) throws Exception {
|
||||
BytesMessage rc = vendor.createBytesMessage();
|
||||
rc.writeBytes(amqpMessage.getArray(), amqpMessage.getArrayOffset(), amqpMessage.getLength());
|
||||
public ServerJMSMessage transform(EncodedMessage amqpMessage) throws Exception {
|
||||
ServerJMSBytesMessage message = createBytesMessage(idGenerator);
|
||||
message.writeBytes(amqpMessage.getArray(), amqpMessage.getArrayOffset(), amqpMessage.getLength());
|
||||
|
||||
// We cannot decode the message headers to check so err on the side of caution
|
||||
// and mark all messages as persistent.
|
||||
rc.setJMSDeliveryMode(DeliveryMode.PERSISTENT);
|
||||
rc.setJMSPriority(defaultPriority);
|
||||
message.setJMSDeliveryMode(DeliveryMode.PERSISTENT);
|
||||
message.setJMSPriority(Message.DEFAULT_PRIORITY);
|
||||
message.setJMSTimestamp(System.currentTimeMillis());
|
||||
|
||||
final long now = System.currentTimeMillis();
|
||||
rc.setJMSTimestamp(now);
|
||||
if (defaultTtl > 0) {
|
||||
rc.setJMSExpiration(now + defaultTtl);
|
||||
}
|
||||
message.setLongProperty(JMS_AMQP_MESSAGE_FORMAT, amqpMessage.getMessageFormat());
|
||||
message.setBooleanProperty(JMS_AMQP_NATIVE, true);
|
||||
|
||||
rc.setLongProperty(prefixVendor + "MESSAGE_FORMAT", amqpMessage.getMessageFormat());
|
||||
rc.setBooleanProperty(prefixVendor + "NATIVE", true);
|
||||
|
||||
return rc;
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/**
|
||||
/*
|
||||
* 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
|
||||
* <p>
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
*
|
||||
* 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.
|
||||
|
|
|
@ -16,13 +16,26 @@
|
|||
*/
|
||||
package org.apache.activemq.artemis.protocol.amqp.converter.message;
|
||||
|
||||
import javax.jms.DeliveryMode;
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.Message;
|
||||
import static org.apache.activemq.artemis.api.core.Message.HDR_SCHEDULED_DELIVERY_TIME;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_CONTENT_ENCODING;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_CONTENT_TYPE;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_FIRST_ACQUIRER;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_FOOTER_PREFIX;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_HEADER;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_MESSAGE_ANNOTATION_PREFIX;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_REPLYTO_GROUP_ID;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.jms.DeliveryMode;
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.Message;
|
||||
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerDestination;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMessage;
|
||||
import org.apache.activemq.artemis.utils.IDGenerator;
|
||||
import org.apache.qpid.proton.amqp.Binary;
|
||||
import org.apache.qpid.proton.amqp.Decimal128;
|
||||
import org.apache.qpid.proton.amqp.Decimal32;
|
||||
|
@ -38,109 +51,61 @@ import org.apache.qpid.proton.amqp.messaging.Header;
|
|||
import org.apache.qpid.proton.amqp.messaging.MessageAnnotations;
|
||||
import org.apache.qpid.proton.amqp.messaging.Properties;
|
||||
|
||||
import static org.apache.activemq.artemis.api.core.Message.HDR_SCHEDULED_DELIVERY_TIME;
|
||||
|
||||
public abstract class InboundTransformer {
|
||||
|
||||
JMSVendor vendor;
|
||||
protected IDGenerator idGenerator;
|
||||
|
||||
public static final String TRANSFORMER_NATIVE = "native";
|
||||
public static final String TRANSFORMER_RAW = "raw";
|
||||
public static final String TRANSFORMER_JMS = "jms";
|
||||
|
||||
String prefixVendor = "JMS_AMQP_";
|
||||
String prefixDeliveryAnnotations = "DA_";
|
||||
String prefixMessageAnnotations = "MA_";
|
||||
String prefixFooter = "FT_";
|
||||
|
||||
int defaultDeliveryMode = DeliveryMode.NON_PERSISTENT;
|
||||
int defaultPriority = Message.DEFAULT_PRIORITY;
|
||||
long defaultTtl = Message.DEFAULT_TIME_TO_LIVE;
|
||||
|
||||
public InboundTransformer(JMSVendor vendor) {
|
||||
this.vendor = vendor;
|
||||
public InboundTransformer(IDGenerator idGenerator) {
|
||||
this.idGenerator = idGenerator;
|
||||
}
|
||||
|
||||
public abstract Message transform(EncodedMessage amqpMessage) throws Exception;
|
||||
public abstract ServerJMSMessage transform(EncodedMessage amqpMessage) throws Exception;
|
||||
|
||||
public abstract String getTransformerName();
|
||||
|
||||
public abstract InboundTransformer getFallbackTransformer();
|
||||
|
||||
public int getDefaultDeliveryMode() {
|
||||
return defaultDeliveryMode;
|
||||
}
|
||||
|
||||
public void setDefaultDeliveryMode(int defaultDeliveryMode) {
|
||||
this.defaultDeliveryMode = defaultDeliveryMode;
|
||||
}
|
||||
|
||||
public int getDefaultPriority() {
|
||||
return defaultPriority;
|
||||
}
|
||||
|
||||
public void setDefaultPriority(int defaultPriority) {
|
||||
this.defaultPriority = defaultPriority;
|
||||
}
|
||||
|
||||
public long getDefaultTtl() {
|
||||
return defaultTtl;
|
||||
}
|
||||
|
||||
public void setDefaultTtl(long defaultTtl) {
|
||||
this.defaultTtl = defaultTtl;
|
||||
}
|
||||
|
||||
public String getPrefixVendor() {
|
||||
return prefixVendor;
|
||||
}
|
||||
|
||||
public void setPrefixVendor(String prefixVendor) {
|
||||
this.prefixVendor = prefixVendor;
|
||||
}
|
||||
|
||||
public JMSVendor getVendor() {
|
||||
return vendor;
|
||||
}
|
||||
|
||||
public void setVendor(JMSVendor vendor) {
|
||||
this.vendor = vendor;
|
||||
}
|
||||
|
||||
protected void populateMessage(Message jms, org.apache.qpid.proton.message.Message amqp) throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ServerJMSMessage populateMessage(ServerJMSMessage jms, org.apache.qpid.proton.message.Message amqp) throws Exception {
|
||||
Header header = amqp.getHeader();
|
||||
if (header == null) {
|
||||
header = new Header();
|
||||
}
|
||||
if (header != null) {
|
||||
jms.setBooleanProperty(JMS_AMQP_HEADER, true);
|
||||
|
||||
if (header.getDurable() != null) {
|
||||
jms.setJMSDeliveryMode(header.getDurable().booleanValue() ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT);
|
||||
} else {
|
||||
jms.setJMSDeliveryMode(defaultDeliveryMode);
|
||||
jms.setJMSDeliveryMode(Message.DEFAULT_DELIVERY_MODE);
|
||||
}
|
||||
|
||||
if (header.getPriority() != null) {
|
||||
jms.setJMSPriority(header.getPriority().intValue());
|
||||
} else {
|
||||
jms.setJMSPriority(defaultPriority);
|
||||
jms.setJMSPriority(Message.DEFAULT_PRIORITY);
|
||||
}
|
||||
|
||||
if (header.getFirstAcquirer() != null) {
|
||||
jms.setBooleanProperty(prefixVendor + "FirstAcquirer", header.getFirstAcquirer());
|
||||
jms.setBooleanProperty(JMS_AMQP_FIRST_ACQUIRER, header.getFirstAcquirer());
|
||||
}
|
||||
|
||||
if (header.getDeliveryCount() != null) {
|
||||
vendor.setJMSXDeliveryCount(jms, header.getDeliveryCount().longValue());
|
||||
// AMQP Delivery Count counts only failed delivers where JMS
|
||||
// Delivery Count should include the original delivery in the count.
|
||||
jms.setLongProperty("JMSXDeliveryCount", header.getDeliveryCount().longValue() + 1);
|
||||
}
|
||||
} else {
|
||||
jms.setJMSPriority((byte) Message.DEFAULT_PRIORITY);
|
||||
jms.setJMSDeliveryMode(DeliveryMode.NON_PERSISTENT);
|
||||
}
|
||||
|
||||
final MessageAnnotations ma = amqp.getMessageAnnotations();
|
||||
if (ma != null) {
|
||||
for (Map.Entry<?, ?> entry : ma.getValue().entrySet()) {
|
||||
String key = entry.getKey().toString();
|
||||
if ("x-opt-jms-type".equals(key) && entry.getValue() != null) {
|
||||
// Legacy annotation, JMSType value will be replaced by Subject further down if also present.
|
||||
jms.setJMSType(entry.getValue().toString());
|
||||
} else if ("x-opt-delivery-time".equals(key) && entry.getValue() != null) {
|
||||
if ("x-opt-delivery-time".equals(key) && entry.getValue() != null) {
|
||||
long deliveryTime = ((Number) entry.getValue()).longValue();
|
||||
jms.setLongProperty(HDR_SCHEDULED_DELIVERY_TIME.toString(), deliveryTime);
|
||||
} else if ("x-opt-delivery-delay".equals(key) && entry.getValue() != null) {
|
||||
|
@ -149,41 +114,15 @@ public abstract class InboundTransformer {
|
|||
jms.setLongProperty(HDR_SCHEDULED_DELIVERY_TIME.toString(), System.currentTimeMillis() + delay);
|
||||
}
|
||||
}
|
||||
//todo
|
||||
/*else if ("x-opt-delivery-repeat".equals(key) && entry.getValue() != null) {
|
||||
int repeat = ((Number) entry.getValue()).intValue();
|
||||
if (repeat > 0) {
|
||||
jms.setIntProperty(ScheduledMessage.AMQ_SCHEDULED_REPEAT, repeat);
|
||||
}
|
||||
} else if ("x-opt-delivery-period".equals(key) && entry.getValue() != null) {
|
||||
long period = ((Number) entry.getValue()).longValue();
|
||||
if (period > 0) {
|
||||
jms.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_PERIOD, period);
|
||||
}
|
||||
} else if ("x-opt-delivery-cron".equals(key) && entry.getValue() != null) {
|
||||
String cronEntry = (String) entry.getValue();
|
||||
if (cronEntry != null) {
|
||||
jms.setStringProperty(ScheduledMessage.AMQ_SCHEDULED_CRON, cronEntry);
|
||||
}
|
||||
}*/
|
||||
|
||||
setProperty(jms, prefixVendor + prefixMessageAnnotations + key, entry.getValue());
|
||||
setProperty(jms, JMS_AMQP_MESSAGE_ANNOTATION_PREFIX + key, entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
final ApplicationProperties ap = amqp.getApplicationProperties();
|
||||
if (ap != null) {
|
||||
for (Map.Entry<Object, Object> entry : (Set<Map.Entry<Object, Object>>) ap.getValue().entrySet()) {
|
||||
String key = entry.getKey().toString();
|
||||
if ("JMSXGroupID".equals(key)) {
|
||||
vendor.setJMSXGroupID(jms, entry.getValue().toString());
|
||||
} else if ("JMSXGroupSequence".equals(key)) {
|
||||
vendor.setJMSXGroupSequence(jms, ((Number) entry.getValue()).intValue());
|
||||
} else if ("JMSXUserID".equals(key)) {
|
||||
vendor.setJMSXUserID(jms, entry.getValue().toString());
|
||||
} else {
|
||||
setProperty(jms, key, entry.getValue());
|
||||
}
|
||||
setProperty(jms, entry.getKey().toString(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,37 +133,38 @@ public abstract class InboundTransformer {
|
|||
}
|
||||
Binary userId = properties.getUserId();
|
||||
if (userId != null) {
|
||||
vendor.setJMSXUserID(jms, new String(userId.getArray(), userId.getArrayOffset(), userId.getLength(), StandardCharsets.UTF_8));
|
||||
// TODO - Better Way to set this?
|
||||
jms.setStringProperty("JMSXUserID", new String(userId.getArray(), userId.getArrayOffset(), userId.getLength(), StandardCharsets.UTF_8));
|
||||
}
|
||||
if (properties.getTo() != null) {
|
||||
jms.setJMSDestination(vendor.createDestination(properties.getTo()));
|
||||
jms.setJMSDestination(new ServerDestination(properties.getTo()));
|
||||
}
|
||||
if (properties.getSubject() != null) {
|
||||
jms.setJMSType(properties.getSubject());
|
||||
}
|
||||
if (properties.getReplyTo() != null) {
|
||||
jms.setJMSReplyTo(vendor.createDestination(properties.getReplyTo()));
|
||||
jms.setJMSReplyTo(new ServerDestination(properties.getReplyTo()));
|
||||
}
|
||||
if (properties.getCorrelationId() != null) {
|
||||
jms.setJMSCorrelationID(AMQPMessageIdHelper.INSTANCE.toBaseMessageIdString(properties.getCorrelationId()));
|
||||
}
|
||||
if (properties.getContentType() != null) {
|
||||
jms.setStringProperty(prefixVendor + "ContentType", properties.getContentType().toString());
|
||||
jms.setStringProperty(JMS_AMQP_CONTENT_TYPE, properties.getContentType().toString());
|
||||
}
|
||||
if (properties.getContentEncoding() != null) {
|
||||
jms.setStringProperty(prefixVendor + "ContentEncoding", properties.getContentEncoding().toString());
|
||||
jms.setStringProperty(JMS_AMQP_CONTENT_ENCODING, properties.getContentEncoding().toString());
|
||||
}
|
||||
if (properties.getCreationTime() != null) {
|
||||
jms.setJMSTimestamp(properties.getCreationTime().getTime());
|
||||
}
|
||||
if (properties.getGroupId() != null) {
|
||||
vendor.setJMSXGroupID(jms, properties.getGroupId());
|
||||
jms.setStringProperty("_AMQ_GROUP_ID", properties.getGroupId());
|
||||
}
|
||||
if (properties.getGroupSequence() != null) {
|
||||
vendor.setJMSXGroupSequence(jms, properties.getGroupSequence().intValue());
|
||||
jms.setIntProperty("JMSXGroupSeq", properties.getGroupSequence().intValue());
|
||||
}
|
||||
if (properties.getReplyToGroupId() != null) {
|
||||
jms.setStringProperty(prefixVendor + "ReplyToGroupID", properties.getReplyToGroupId());
|
||||
jms.setStringProperty(JMS_AMQP_REPLYTO_GROUP_ID, properties.getReplyToGroupId());
|
||||
}
|
||||
if (properties.getAbsoluteExpiryTime() != null) {
|
||||
jms.setJMSExpiration(properties.getAbsoluteExpiryTime().getTime());
|
||||
|
@ -232,9 +172,9 @@ public abstract class InboundTransformer {
|
|||
}
|
||||
|
||||
// If the jms expiration has not yet been set...
|
||||
if (jms.getJMSExpiration() == 0) {
|
||||
if (header != null && jms.getJMSExpiration() == 0) {
|
||||
// Then lets try to set it based on the message ttl.
|
||||
long ttl = defaultTtl;
|
||||
long ttl = Message.DEFAULT_TIME_TO_LIVE;
|
||||
if (header.getTtl() != null) {
|
||||
ttl = header.getTtl().longValue();
|
||||
}
|
||||
|
@ -250,9 +190,11 @@ public abstract class InboundTransformer {
|
|||
if (fp != null) {
|
||||
for (Map.Entry<Object, Object> entry : (Set<Map.Entry<Object, Object>>) fp.getValue().entrySet()) {
|
||||
String key = entry.getKey().toString();
|
||||
setProperty(jms, prefixVendor + prefixFooter + key, entry.getValue());
|
||||
setProperty(jms, JMS_AMQP_FOOTER_PREFIX + key, entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return jms;
|
||||
}
|
||||
|
||||
private void setProperty(Message msg, String key, Object value) throws JMSException {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/**
|
||||
/*
|
||||
* 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
|
||||
* <p>
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
*
|
||||
* 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.
|
||||
|
@ -16,27 +16,50 @@
|
|||
*/
|
||||
package org.apache.activemq.artemis.protocol.amqp.converter.message;
|
||||
|
||||
import javax.jms.BytesMessage;
|
||||
import javax.jms.MapMessage;
|
||||
import javax.jms.Message;
|
||||
import javax.jms.ObjectMessage;
|
||||
import javax.jms.StreamMessage;
|
||||
import javax.jms.TextMessage;
|
||||
import java.io.Serializable;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_DATA;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_NULL;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_SEQUENCE;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_VALUE_BINARY;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_VALUE_LIST;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_VALUE_MAP;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_VALUE_NULL;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_VALUE_STRING;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_MESSAGE_FORMAT;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_ORIGINAL_ENCODING;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.OCTET_STREAM_CONTENT_TYPE;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.SERIALIZED_JAVA_OBJECT_CONTENT_TYPE;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.createBytesMessage;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.createMapMessage;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.createMessage;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.createObjectMessage;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.createStreamMessage;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.createTextMessage;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.getCharsetForTextualContent;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.isContentType;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSStreamMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.exceptions.ActiveMQAMQPInternalErrorException;
|
||||
import org.apache.activemq.artemis.utils.IDGenerator;
|
||||
import org.apache.qpid.proton.amqp.Binary;
|
||||
import org.apache.qpid.proton.amqp.messaging.AmqpSequence;
|
||||
import org.apache.qpid.proton.amqp.messaging.AmqpValue;
|
||||
import org.apache.qpid.proton.amqp.messaging.Data;
|
||||
import org.apache.qpid.proton.amqp.messaging.Section;
|
||||
import org.apache.qpid.proton.message.Message;
|
||||
|
||||
public class JMSMappingInboundTransformer extends InboundTransformer {
|
||||
|
||||
public JMSMappingInboundTransformer(JMSVendor vendor) {
|
||||
super(vendor);
|
||||
public JMSMappingInboundTransformer(IDGenerator idGenerator) {
|
||||
super(idGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -46,75 +69,128 @@ public class JMSMappingInboundTransformer extends InboundTransformer {
|
|||
|
||||
@Override
|
||||
public InboundTransformer getFallbackTransformer() {
|
||||
return new AMQPNativeInboundTransformer(getVendor());
|
||||
return new AMQPNativeInboundTransformer(idGenerator);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@Override
|
||||
public Message transform(EncodedMessage amqpMessage) throws Exception {
|
||||
org.apache.qpid.proton.message.Message amqp = amqpMessage.decode();
|
||||
public ServerJMSMessage transform(EncodedMessage encodedMessage) throws Exception {
|
||||
ServerJMSMessage transformedMessage = null;
|
||||
|
||||
try {
|
||||
Message amqpMessage = encodedMessage.decode();
|
||||
transformedMessage = createServerMessage(amqpMessage);
|
||||
populateMessage(transformedMessage, amqpMessage);
|
||||
} catch (Exception ex) {
|
||||
InboundTransformer transformer = this.getFallbackTransformer();
|
||||
|
||||
while (transformer != null) {
|
||||
try {
|
||||
transformedMessage = transformer.transform(encodedMessage);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
transformer = transformer.getFallbackTransformer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Regardless of the transformer that finally decoded the message we need to ensure that
|
||||
// the AMQP Message Format value is preserved for application on retransmit.
|
||||
if (transformedMessage != null && encodedMessage.getMessageFormat() != 0) {
|
||||
transformedMessage.setLongProperty(JMS_AMQP_MESSAGE_FORMAT, encodedMessage.getMessageFormat());
|
||||
}
|
||||
|
||||
return transformedMessage;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ServerJMSMessage createServerMessage(Message message) throws Exception {
|
||||
|
||||
Section body = message.getBody();
|
||||
ServerJMSMessage result;
|
||||
|
||||
Message rc;
|
||||
final Section body = amqp.getBody();
|
||||
if (body == null) {
|
||||
rc = vendor.createMessage();
|
||||
if (isContentType(SERIALIZED_JAVA_OBJECT_CONTENT_TYPE, message)) {
|
||||
result = createObjectMessage(idGenerator);
|
||||
} else if (isContentType(OCTET_STREAM_CONTENT_TYPE, message) || isContentType(null, message)) {
|
||||
result = createBytesMessage(idGenerator);
|
||||
} else {
|
||||
Charset charset = getCharsetForTextualContent(message.getContentType());
|
||||
if (charset != null) {
|
||||
result = createTextMessage(idGenerator);
|
||||
} else {
|
||||
result = createMessage(idGenerator);
|
||||
}
|
||||
}
|
||||
|
||||
result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_NULL);
|
||||
} else if (body instanceof Data) {
|
||||
Binary d = ((Data) body).getValue();
|
||||
BytesMessage m = vendor.createBytesMessage();
|
||||
m.writeBytes(d.getArray(), d.getArrayOffset(), d.getLength());
|
||||
rc = m;
|
||||
Binary payload = ((Data) body).getValue();
|
||||
|
||||
if (isContentType(SERIALIZED_JAVA_OBJECT_CONTENT_TYPE, message)) {
|
||||
result = createObjectMessage(idGenerator, payload.getArray(), payload.getArrayOffset(), payload.getLength());
|
||||
} else if (isContentType(OCTET_STREAM_CONTENT_TYPE, message)) {
|
||||
result = createBytesMessage(idGenerator, payload.getArray(), payload.getArrayOffset(), payload.getLength());
|
||||
} else {
|
||||
Charset charset = getCharsetForTextualContent(message.getContentType());
|
||||
if (StandardCharsets.UTF_8.equals(charset)) {
|
||||
ByteBuffer buf = ByteBuffer.wrap(payload.getArray(), payload.getArrayOffset(), payload.getLength());
|
||||
|
||||
try {
|
||||
CharBuffer chars = charset.newDecoder().decode(buf);
|
||||
result = createTextMessage(idGenerator, String.valueOf(chars));
|
||||
} catch (CharacterCodingException e) {
|
||||
result = createBytesMessage(idGenerator, payload.getArray(), payload.getArrayOffset(), payload.getLength());
|
||||
}
|
||||
} else {
|
||||
result = createBytesMessage(idGenerator, payload.getArray(), payload.getArrayOffset(), payload.getLength());
|
||||
}
|
||||
}
|
||||
|
||||
result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_DATA);
|
||||
} else if (body instanceof AmqpSequence) {
|
||||
AmqpSequence sequence = (AmqpSequence) body;
|
||||
StreamMessage m = vendor.createStreamMessage();
|
||||
ServerJMSStreamMessage m = createStreamMessage(idGenerator);
|
||||
for (Object item : sequence.getValue()) {
|
||||
m.writeObject(item);
|
||||
}
|
||||
rc = m;
|
||||
m.setStringProperty(AMQPMessageTypes.AMQP_TYPE_KEY, AMQPMessageTypes.AMQP_SEQUENCE);
|
||||
|
||||
result = m;
|
||||
result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_SEQUENCE);
|
||||
} else if (body instanceof AmqpValue) {
|
||||
Object value = ((AmqpValue) body).getValue();
|
||||
if (value == null) {
|
||||
rc = vendor.createObjectMessage();
|
||||
}
|
||||
if (value instanceof String) {
|
||||
TextMessage m = vendor.createTextMessage();
|
||||
m.setText((String) value);
|
||||
rc = m;
|
||||
if (value == null || value instanceof String) {
|
||||
result = createTextMessage(idGenerator, (String) value);
|
||||
|
||||
result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, value == null ? AMQP_VALUE_NULL : AMQP_VALUE_STRING);
|
||||
} else if (value instanceof Binary) {
|
||||
Binary d = (Binary) value;
|
||||
BytesMessage m = vendor.createBytesMessage();
|
||||
m.writeBytes(d.getArray(), d.getArrayOffset(), d.getLength());
|
||||
rc = m;
|
||||
Binary payload = (Binary) value;
|
||||
|
||||
if (isContentType(SERIALIZED_JAVA_OBJECT_CONTENT_TYPE, message)) {
|
||||
result = createObjectMessage(idGenerator, payload);
|
||||
} else {
|
||||
result = createBytesMessage(idGenerator, payload.getArray(), payload.getArrayOffset(), payload.getLength());
|
||||
}
|
||||
|
||||
result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_BINARY);
|
||||
} else if (value instanceof List) {
|
||||
StreamMessage m = vendor.createStreamMessage();
|
||||
ServerJMSStreamMessage m = createStreamMessage(idGenerator);
|
||||
for (Object item : (List<Object>) value) {
|
||||
m.writeObject(item);
|
||||
}
|
||||
rc = m;
|
||||
m.setStringProperty(AMQPMessageTypes.AMQP_TYPE_KEY, AMQPMessageTypes.AMQP_LIST);
|
||||
result = m;
|
||||
result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_LIST);
|
||||
} else if (value instanceof Map) {
|
||||
MapMessage m = vendor.createMapMessage();
|
||||
final Set<Map.Entry<String, Object>> set = ((Map<String, Object>) value).entrySet();
|
||||
for (Map.Entry<String, Object> entry : set) {
|
||||
m.setObject(entry.getKey(), entry.getValue());
|
||||
}
|
||||
rc = m;
|
||||
result = createMapMessage(idGenerator, (Map<String, Object>) value);
|
||||
result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_MAP);
|
||||
} else {
|
||||
ObjectMessage m = vendor.createObjectMessage();
|
||||
m.setObject((Serializable) value);
|
||||
rc = m;
|
||||
// Trigger fall-back to native encoder which generates BytesMessage with the
|
||||
// original message stored in the message body.
|
||||
throw new ActiveMQAMQPInternalErrorException("Unable to encode to ActiveMQ JMS Message");
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected body type: " + body.getClass());
|
||||
}
|
||||
rc.setJMSDeliveryMode(defaultDeliveryMode);
|
||||
rc.setJMSPriority(defaultPriority);
|
||||
rc.setJMSExpiration(defaultTtl);
|
||||
|
||||
populateMessage(rc, amqp);
|
||||
|
||||
rc.setLongProperty(prefixVendor + "MESSAGE_FORMAT", amqpMessage.getMessageFormat());
|
||||
rc.setBooleanProperty(prefixVendor + "NATIVE", false);
|
||||
return rc;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/**
|
||||
/*
|
||||
* 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
|
||||
* <p>
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
*
|
||||
* 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.
|
||||
|
@ -16,30 +16,61 @@
|
|||
*/
|
||||
package org.apache.activemq.artemis.protocol.amqp.converter.message;
|
||||
|
||||
import javax.jms.BytesMessage;
|
||||
import javax.jms.DeliveryMode;
|
||||
import javax.jms.Destination;
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.MapMessage;
|
||||
import javax.jms.Message;
|
||||
import javax.jms.MessageEOFException;
|
||||
import javax.jms.ObjectMessage;
|
||||
import javax.jms.Queue;
|
||||
import javax.jms.StreamMessage;
|
||||
import javax.jms.TemporaryQueue;
|
||||
import javax.jms.TemporaryTopic;
|
||||
import javax.jms.TextMessage;
|
||||
import javax.jms.Topic;
|
||||
import static org.apache.activemq.artemis.api.core.Message.HDR_SCHEDULED_DELIVERY_TIME;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_DATA;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_NULL;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_SEQUENCE;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_UNKNOWN;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_VALUE_BINARY;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_VALUE_LIST;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_VALUE_STRING;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.EMPTY_BINARY;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_CONTENT_ENCODING;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_CONTENT_TYPE;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_DELIVERY_ANNOTATION_PREFIX;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_FIRST_ACQUIRER;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_FOOTER_PREFIX;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_HEADER;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_MESSAGE_ANNOTATION_PREFIX;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_MESSAGE_FORMAT;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_NATIVE;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_ORIGINAL_ENCODING;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_PREFIX;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_PROPERTIES;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_REPLYTO_GROUP_ID;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.SERIALIZED_JAVA_OBJECT_CONTENT_TYPE;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.toAddress;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.jms.Destination;
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.Message;
|
||||
import javax.jms.MessageEOFException;
|
||||
import javax.jms.Queue;
|
||||
import javax.jms.TemporaryQueue;
|
||||
import javax.jms.TemporaryTopic;
|
||||
import javax.jms.TextMessage;
|
||||
import javax.jms.Topic;
|
||||
|
||||
import org.apache.activemq.artemis.core.message.impl.MessageInternal;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSBytesMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMapMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSObjectMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSStreamMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSTextMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.exceptions.ActiveMQAMQPIllegalStateException;
|
||||
import org.apache.activemq.artemis.reader.MessageUtil;
|
||||
import org.apache.activemq.artemis.utils.IDGenerator;
|
||||
import org.apache.qpid.proton.amqp.Binary;
|
||||
import org.apache.qpid.proton.amqp.Symbol;
|
||||
import org.apache.qpid.proton.amqp.UnsignedByte;
|
||||
|
@ -54,12 +85,16 @@ import org.apache.qpid.proton.amqp.messaging.Header;
|
|||
import org.apache.qpid.proton.amqp.messaging.MessageAnnotations;
|
||||
import org.apache.qpid.proton.amqp.messaging.Properties;
|
||||
import org.apache.qpid.proton.amqp.messaging.Section;
|
||||
import org.apache.qpid.proton.message.ProtonJMessage;
|
||||
import org.apache.qpid.proton.codec.AMQPDefinedTypes;
|
||||
import org.apache.qpid.proton.codec.DecoderImpl;
|
||||
import org.apache.qpid.proton.codec.EncoderImpl;
|
||||
import org.apache.qpid.proton.codec.WritableBuffer;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
public class JMSMappingOutboundTransformer extends OutboundTransformer {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(JMSMappingOutboundTransformer.class);
|
||||
|
||||
public static final Symbol JMS_DEST_TYPE_MSG_ANNOTATION = Symbol.valueOf("x-opt-jms-dest");
|
||||
public static final Symbol JMS_REPLY_TO_TYPE_MSG_ANNOTATION = Symbol.valueOf("x-opt-jms-reply-to");
|
||||
|
||||
|
@ -68,51 +103,340 @@ public class JMSMappingOutboundTransformer extends OutboundTransformer {
|
|||
public static final byte TEMP_QUEUE_TYPE = 0x02;
|
||||
public static final byte TEMP_TOPIC_TYPE = 0x03;
|
||||
|
||||
public JMSMappingOutboundTransformer(JMSVendor vendor) {
|
||||
super(vendor);
|
||||
// For now Proton requires that we create a decoder to create an encoder
|
||||
private static class EncoderDecoderPair {
|
||||
DecoderImpl decoder = new DecoderImpl();
|
||||
EncoderImpl encoder = new EncoderImpl(decoder);
|
||||
{
|
||||
AMQPDefinedTypes.registerAllTypes(decoder, encoder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the conversion between JMS Message and Proton Message without
|
||||
* re-encoding it to array. This is needed because some frameworks may elect
|
||||
* to do this on their own way (Netty for instance using Nettybuffers)
|
||||
*
|
||||
* @param msg
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public ProtonJMessage convert(Message msg) throws JMSException, UnsupportedEncodingException {
|
||||
Header header = new Header();
|
||||
Properties props = new Properties();
|
||||
HashMap<Symbol, Object> daMap = null;
|
||||
HashMap<Symbol, Object> maMap = null;
|
||||
HashMap apMap = null;
|
||||
private static final ThreadLocal<EncoderDecoderPair> tlsCodec = new ThreadLocal<EncoderDecoderPair>() {
|
||||
@Override
|
||||
protected EncoderDecoderPair initialValue() {
|
||||
return new EncoderDecoderPair();
|
||||
}
|
||||
};
|
||||
|
||||
public JMSMappingOutboundTransformer(IDGenerator idGenerator) {
|
||||
super(idGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long transform(ServerJMSMessage message, WritableBuffer buffer) throws JMSException, UnsupportedEncodingException {
|
||||
if (message == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long messageFormat = 0;
|
||||
Header header = null;
|
||||
Properties properties = null;
|
||||
Map<Symbol, Object> daMap = null;
|
||||
Map<Symbol, Object> maMap = null;
|
||||
Map<String, Object> apMap = null;
|
||||
Map<Object, Object> footerMap = null;
|
||||
|
||||
Section body = convertBody(message);
|
||||
|
||||
if (message.getInnerMessage().isDurable()) {
|
||||
if (header == null) {
|
||||
header = new Header();
|
||||
}
|
||||
header.setDurable(true);
|
||||
}
|
||||
byte priority = (byte) message.getJMSPriority();
|
||||
if (priority != Message.DEFAULT_PRIORITY) {
|
||||
if (header == null) {
|
||||
header = new Header();
|
||||
}
|
||||
header.setPriority(UnsignedByte.valueOf(priority));
|
||||
}
|
||||
String type = message.getJMSType();
|
||||
if (type != null) {
|
||||
if (properties == null) {
|
||||
properties = new Properties();
|
||||
}
|
||||
properties.setSubject(type);
|
||||
}
|
||||
String messageId = message.getJMSMessageID();
|
||||
if (messageId != null) {
|
||||
if (properties == null) {
|
||||
properties = new Properties();
|
||||
}
|
||||
try {
|
||||
properties.setMessageId(AMQPMessageIdHelper.INSTANCE.toIdObject(messageId));
|
||||
} catch (ActiveMQAMQPIllegalStateException e) {
|
||||
properties.setMessageId(messageId);
|
||||
}
|
||||
}
|
||||
Destination destination = message.getJMSDestination();
|
||||
if (destination != null) {
|
||||
if (properties == null) {
|
||||
properties = new Properties();
|
||||
}
|
||||
properties.setTo(toAddress(destination));
|
||||
if (maMap == null) {
|
||||
maMap = new HashMap<Symbol, Object>();
|
||||
}
|
||||
maMap.put(JMS_DEST_TYPE_MSG_ANNOTATION, destinationType(destination));
|
||||
}
|
||||
Destination replyTo = message.getJMSReplyTo();
|
||||
if (replyTo != null) {
|
||||
if (properties == null) {
|
||||
properties = new Properties();
|
||||
}
|
||||
properties.setReplyTo(toAddress(replyTo));
|
||||
if (maMap == null) {
|
||||
maMap = new HashMap<Symbol, Object>();
|
||||
}
|
||||
maMap.put(JMS_REPLY_TO_TYPE_MSG_ANNOTATION, destinationType(replyTo));
|
||||
}
|
||||
String correlationId = message.getJMSCorrelationID();
|
||||
if (correlationId != null) {
|
||||
if (properties == null) {
|
||||
properties = new Properties();
|
||||
}
|
||||
try {
|
||||
properties.setCorrelationId(AMQPMessageIdHelper.INSTANCE.toIdObject(correlationId));
|
||||
} catch (ActiveMQAMQPIllegalStateException e) {
|
||||
properties.setCorrelationId(correlationId);
|
||||
}
|
||||
}
|
||||
long expiration = message.getJMSExpiration();
|
||||
if (expiration != 0) {
|
||||
long ttl = expiration - System.currentTimeMillis();
|
||||
if (ttl < 0) {
|
||||
ttl = 1;
|
||||
}
|
||||
|
||||
if (header == null) {
|
||||
header = new Header();
|
||||
}
|
||||
header.setTtl(new UnsignedInteger((int) ttl));
|
||||
|
||||
if (properties == null) {
|
||||
properties = new Properties();
|
||||
}
|
||||
properties.setAbsoluteExpiryTime(new Date(expiration));
|
||||
}
|
||||
long timeStamp = message.getJMSTimestamp();
|
||||
if (timeStamp != 0) {
|
||||
if (properties == null) {
|
||||
properties = new Properties();
|
||||
}
|
||||
properties.setCreationTime(new Date(timeStamp));
|
||||
}
|
||||
|
||||
final Set<String> keySet = MessageUtil.getPropertyNames(message.getInnerMessage());
|
||||
for (String key : keySet) {
|
||||
if (key.startsWith("JMSX")) {
|
||||
if (key.equals("JMSXDeliveryCount")) {
|
||||
// The AMQP delivery-count field only includes prior failed delivery attempts,
|
||||
// whereas JMSXDeliveryCount includes the first/current delivery attempt.
|
||||
int amqpDeliveryCount = message.getDeliveryCount() - 1;
|
||||
if (amqpDeliveryCount > 0) {
|
||||
if (header == null) {
|
||||
header = new Header();
|
||||
}
|
||||
header.setDeliveryCount(new UnsignedInteger(amqpDeliveryCount));
|
||||
}
|
||||
continue;
|
||||
} else if (key.equals("JMSXUserID")) {
|
||||
String value = message.getStringProperty(key);
|
||||
if (properties == null) {
|
||||
properties = new Properties();
|
||||
}
|
||||
properties.setUserId(new Binary(value.getBytes(StandardCharsets.UTF_8)));
|
||||
continue;
|
||||
} else if (key.equals("JMSXGroupID")) {
|
||||
String value = message.getStringProperty(key);
|
||||
if (properties == null) {
|
||||
properties = new Properties();
|
||||
}
|
||||
properties.setGroupId(value);
|
||||
continue;
|
||||
} else if (key.equals("JMSXGroupSeq")) {
|
||||
UnsignedInteger value = new UnsignedInteger(message.getIntProperty(key));
|
||||
if (properties == null) {
|
||||
properties = new Properties();
|
||||
}
|
||||
properties.setGroupSequence(value);
|
||||
continue;
|
||||
}
|
||||
} else if (key.startsWith(JMS_AMQP_PREFIX)) {
|
||||
// AMQP Message Information stored from a conversion to the Core Message
|
||||
if (key.equals(JMS_AMQP_MESSAGE_FORMAT)) {
|
||||
messageFormat = message.getLongProperty(JMS_AMQP_MESSAGE_FORMAT);
|
||||
continue;
|
||||
} else if (key.equals(JMS_AMQP_NATIVE)) {
|
||||
// skip..internal use only
|
||||
continue;
|
||||
} else if (key.equals(JMS_AMQP_ORIGINAL_ENCODING)) {
|
||||
// skip..internal use only
|
||||
continue;
|
||||
} else if (key.equals(JMS_AMQP_FIRST_ACQUIRER)) {
|
||||
if (header == null) {
|
||||
header = new Header();
|
||||
}
|
||||
header.setFirstAcquirer(message.getBooleanProperty(key));
|
||||
continue;
|
||||
} else if (key.equals(JMS_AMQP_HEADER)) {
|
||||
if (header == null) {
|
||||
header = new Header();
|
||||
}
|
||||
continue;
|
||||
} else if (key.startsWith(JMS_AMQP_PROPERTIES)) {
|
||||
if (properties == null) {
|
||||
properties = new Properties();
|
||||
}
|
||||
continue;
|
||||
} else if (key.startsWith(JMS_AMQP_DELIVERY_ANNOTATION_PREFIX)) {
|
||||
if (daMap == null) {
|
||||
daMap = new HashMap<Symbol, Object>();
|
||||
}
|
||||
String name = key.substring(JMS_AMQP_DELIVERY_ANNOTATION_PREFIX.length());
|
||||
daMap.put(Symbol.valueOf(name), message.getObjectProperty(key));
|
||||
continue;
|
||||
} else if (key.startsWith(JMS_AMQP_MESSAGE_ANNOTATION_PREFIX)) {
|
||||
if (maMap == null) {
|
||||
maMap = new HashMap<Symbol, Object>();
|
||||
}
|
||||
String name = key.substring(JMS_AMQP_MESSAGE_ANNOTATION_PREFIX.length());
|
||||
maMap.put(Symbol.valueOf(name), message.getObjectProperty(key));
|
||||
continue;
|
||||
} else if (key.equals(JMS_AMQP_CONTENT_TYPE)) {
|
||||
if (properties == null) {
|
||||
properties = new Properties();
|
||||
}
|
||||
properties.setContentType(Symbol.getSymbol(message.getStringProperty(key)));
|
||||
continue;
|
||||
} else if (key.equals(JMS_AMQP_CONTENT_ENCODING)) {
|
||||
if (properties == null) {
|
||||
properties = new Properties();
|
||||
}
|
||||
properties.setContentEncoding(Symbol.getSymbol(message.getStringProperty(key)));
|
||||
continue;
|
||||
} else if (key.equals(JMS_AMQP_REPLYTO_GROUP_ID)) {
|
||||
if (properties == null) {
|
||||
properties = new Properties();
|
||||
}
|
||||
properties.setReplyToGroupId(message.getStringProperty(key));
|
||||
continue;
|
||||
} else if (key.startsWith(JMS_AMQP_FOOTER_PREFIX)) {
|
||||
if (footerMap == null) {
|
||||
footerMap = new HashMap<Object, Object>();
|
||||
}
|
||||
String name = key.substring(JMS_AMQP_FOOTER_PREFIX.length());
|
||||
footerMap.put(name, message.getObjectProperty(key));
|
||||
continue;
|
||||
}
|
||||
} else if (key.equals("_AMQ_GROUP_ID")) {
|
||||
String value = message.getStringProperty(key);
|
||||
if (properties == null) {
|
||||
properties = new Properties();
|
||||
}
|
||||
properties.setGroupId(value);
|
||||
continue;
|
||||
} else if (key.equals(ServerJMSMessage.NATIVE_MESSAGE_ID)) {
|
||||
// skip..internal use only
|
||||
continue;
|
||||
} else if (key.endsWith(HDR_SCHEDULED_DELIVERY_TIME.toString())) {
|
||||
// skip..remove annotation from previous inbound transformation
|
||||
continue;
|
||||
} else if (key.equals(AMQPMessageTypes.AMQP_TYPE_KEY)) {
|
||||
// skip..internal use only - TODO - Remove this deprecated value in future release.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (apMap == null) {
|
||||
apMap = new HashMap<String, Object>();
|
||||
}
|
||||
|
||||
Object objectProperty = message.getObjectProperty(key);
|
||||
if (objectProperty instanceof byte[]) {
|
||||
objectProperty = new Binary((byte[]) objectProperty);
|
||||
}
|
||||
|
||||
apMap.put(key, objectProperty);
|
||||
}
|
||||
|
||||
EncoderImpl encoder = tlsCodec.get().encoder;
|
||||
encoder.setByteBuffer(buffer);
|
||||
|
||||
if (header != null) {
|
||||
encoder.writeObject(header);
|
||||
}
|
||||
if (daMap != null) {
|
||||
encoder.writeObject(new DeliveryAnnotations(daMap));
|
||||
}
|
||||
if (maMap != null) {
|
||||
encoder.writeObject(new MessageAnnotations(maMap));
|
||||
}
|
||||
if (properties != null) {
|
||||
encoder.writeObject(properties);
|
||||
}
|
||||
if (apMap != null) {
|
||||
encoder.writeObject(new ApplicationProperties(apMap));
|
||||
}
|
||||
if (body != null) {
|
||||
encoder.writeObject(body);
|
||||
}
|
||||
if (footerMap != null) {
|
||||
encoder.writeObject(new Footer(footerMap));
|
||||
}
|
||||
|
||||
return messageFormat;
|
||||
}
|
||||
|
||||
private Section convertBody(ServerJMSMessage message) throws JMSException {
|
||||
|
||||
Section body = null;
|
||||
HashMap footerMap = null;
|
||||
if (msg instanceof BytesMessage) {
|
||||
BytesMessage m = (BytesMessage) msg;
|
||||
byte[] data = new byte[(int) m.getBodyLength()];
|
||||
m.readBytes(data);
|
||||
m.reset(); // Need to reset after readBytes or future readBytes
|
||||
// calls (ex: redeliveries) will fail and return -1
|
||||
body = new Data(new Binary(data));
|
||||
short orignalEncoding = AMQP_UNKNOWN;
|
||||
|
||||
try {
|
||||
orignalEncoding = message.getShortProperty(JMS_AMQP_ORIGINAL_ENCODING);
|
||||
} catch (Exception ex) {
|
||||
// Ignore and stick with UNKNOWN
|
||||
}
|
||||
if (msg instanceof TextMessage) {
|
||||
body = new AmqpValue(((TextMessage) msg).getText());
|
||||
|
||||
if (message instanceof ServerJMSBytesMessage) {
|
||||
Binary payload = getBinaryFromMessageBody((ServerJMSBytesMessage) message);
|
||||
|
||||
if (payload == null) {
|
||||
payload = EMPTY_BINARY;
|
||||
}
|
||||
if (msg instanceof MapMessage) {
|
||||
final HashMap<String, Object> map = new HashMap<>();
|
||||
final MapMessage m = (MapMessage) msg;
|
||||
final Enumeration<String> names = m.getMapNames();
|
||||
while (names.hasMoreElements()) {
|
||||
String key = names.nextElement();
|
||||
map.put(key, m.getObject(key));
|
||||
|
||||
switch (orignalEncoding) {
|
||||
case AMQP_NULL:
|
||||
break;
|
||||
case AMQP_VALUE_BINARY:
|
||||
body = new AmqpValue(payload);
|
||||
break;
|
||||
case AMQP_DATA:
|
||||
case AMQP_UNKNOWN:
|
||||
default:
|
||||
body = new Data(payload);
|
||||
break;
|
||||
}
|
||||
body = new AmqpValue(map);
|
||||
} else if (message instanceof ServerJMSTextMessage) {
|
||||
switch (orignalEncoding) {
|
||||
case AMQP_NULL:
|
||||
break;
|
||||
case AMQP_DATA:
|
||||
body = new Data(getBinaryFromMessageBody((ServerJMSTextMessage) message));
|
||||
break;
|
||||
case AMQP_VALUE_STRING:
|
||||
case AMQP_UNKNOWN:
|
||||
default:
|
||||
body = new AmqpValue(((TextMessage) message).getText());
|
||||
break;
|
||||
}
|
||||
if (msg instanceof StreamMessage) {
|
||||
} else if (message instanceof ServerJMSMapMessage) {
|
||||
body = new AmqpValue(getMapFromMessageBody((ServerJMSMapMessage) message));
|
||||
} else if (message instanceof ServerJMSStreamMessage) {
|
||||
ArrayList<Object> list = new ArrayList<>();
|
||||
final StreamMessage m = (StreamMessage) msg;
|
||||
final ServerJMSStreamMessage m = (ServerJMSStreamMessage) message;
|
||||
try {
|
||||
while (true) {
|
||||
list.add(m.readObject());
|
||||
|
@ -120,21 +444,58 @@ public class JMSMappingOutboundTransformer extends OutboundTransformer {
|
|||
} catch (MessageEOFException e) {
|
||||
}
|
||||
|
||||
String amqpType = msg.getStringProperty(AMQPMessageTypes.AMQP_TYPE_KEY);
|
||||
// Deprecated encoding markers - TODO - Remove on future release
|
||||
if (orignalEncoding == AMQP_UNKNOWN) {
|
||||
String amqpType = message.getStringProperty(AMQPMessageTypes.AMQP_TYPE_KEY);
|
||||
if (amqpType != null) {
|
||||
if (amqpType.equals(AMQPMessageTypes.AMQP_LIST)) {
|
||||
body = new AmqpValue(list);
|
||||
orignalEncoding = AMQP_VALUE_LIST;
|
||||
} else {
|
||||
orignalEncoding = AMQP_SEQUENCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (orignalEncoding) {
|
||||
case AMQP_SEQUENCE:
|
||||
body = new AmqpSequence(list);
|
||||
break;
|
||||
case AMQP_VALUE_LIST:
|
||||
case AMQP_UNKNOWN:
|
||||
default:
|
||||
body = new AmqpValue(list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (msg instanceof ObjectMessage) {
|
||||
body = new AmqpValue(((ObjectMessage) msg).getObject());
|
||||
} else if (message instanceof ServerJMSObjectMessage) {
|
||||
Binary payload = getBinaryFromMessageBody((ServerJMSObjectMessage) message);
|
||||
|
||||
if (payload == null) {
|
||||
payload = EMPTY_BINARY;
|
||||
}
|
||||
|
||||
if (body == null && msg instanceof ServerJMSMessage) {
|
||||
switch (orignalEncoding) {
|
||||
case AMQP_VALUE_BINARY:
|
||||
body = new AmqpValue(payload);
|
||||
break;
|
||||
case AMQP_DATA:
|
||||
case AMQP_UNKNOWN:
|
||||
default:
|
||||
body = new Data(payload);
|
||||
break;
|
||||
}
|
||||
|
||||
MessageInternal internalMessage = ((ServerJMSMessage) msg).getInnerMessage();
|
||||
if (!internalMessage.containsProperty("AMQP_MESSAGE_FORMAT")) {
|
||||
// For a non-AMQP message we tag the outbound content type as containing
|
||||
// a serialized Java object so that an AMQP client has a hint as to what
|
||||
// we are sending it.
|
||||
if (!message.propertyExists(JMS_AMQP_CONTENT_TYPE)) {
|
||||
message.setStringProperty(JMS_AMQP_CONTENT_TYPE, SERIALIZED_JAVA_OBJECT_CONTENT_TYPE);
|
||||
}
|
||||
} else if (message instanceof ServerJMSMessage) {
|
||||
// If this is not an AMQP message that was converted then the original encoding
|
||||
// will be unknown so we check for special cases of messages with special data
|
||||
// encoded into the server message body.
|
||||
if (orignalEncoding == AMQP_UNKNOWN) {
|
||||
MessageInternal internalMessage = message.getInnerMessage();
|
||||
int readerIndex = internalMessage.getBodyBuffer().readerIndex();
|
||||
try {
|
||||
Object s = internalMessage.getBodyBuffer().readNullableSimpleString();
|
||||
|
@ -149,146 +510,51 @@ public class JMSMappingOutboundTransformer extends OutboundTransformer {
|
|||
}
|
||||
}
|
||||
|
||||
header.setDurable(msg.getJMSDeliveryMode() == DeliveryMode.PERSISTENT ? true : false);
|
||||
header.setPriority(new UnsignedByte((byte) msg.getJMSPriority()));
|
||||
if (msg.getJMSType() != null) {
|
||||
props.setSubject(msg.getJMSType());
|
||||
}
|
||||
if (msg.getJMSMessageID() != null) {
|
||||
|
||||
String msgId = msg.getJMSMessageID();
|
||||
|
||||
try {
|
||||
props.setMessageId(AMQPMessageIdHelper.INSTANCE.toIdObject(msgId));
|
||||
} catch (ActiveMQAMQPIllegalStateException e) {
|
||||
props.setMessageId(msgId);
|
||||
}
|
||||
}
|
||||
if (msg.getJMSDestination() != null) {
|
||||
props.setTo(vendor.toAddress(msg.getJMSDestination()));
|
||||
if (maMap == null) {
|
||||
maMap = new HashMap<>();
|
||||
}
|
||||
maMap.put(JMS_DEST_TYPE_MSG_ANNOTATION, destinationType(msg.getJMSDestination()));
|
||||
}
|
||||
if (msg.getJMSReplyTo() != null) {
|
||||
props.setReplyTo(vendor.toAddress(msg.getJMSReplyTo()));
|
||||
if (maMap == null) {
|
||||
maMap = new HashMap<>();
|
||||
}
|
||||
maMap.put(JMS_REPLY_TO_TYPE_MSG_ANNOTATION, destinationType(msg.getJMSReplyTo()));
|
||||
}
|
||||
if (msg.getJMSCorrelationID() != null) {
|
||||
String correlationId = msg.getJMSCorrelationID();
|
||||
|
||||
try {
|
||||
props.setCorrelationId(AMQPMessageIdHelper.INSTANCE.toIdObject(correlationId));
|
||||
} catch (ActiveMQAMQPIllegalStateException e) {
|
||||
props.setCorrelationId(correlationId);
|
||||
}
|
||||
}
|
||||
if (msg.getJMSExpiration() != 0) {
|
||||
long ttl = msg.getJMSExpiration() - System.currentTimeMillis();
|
||||
if (ttl < 0) {
|
||||
ttl = 1;
|
||||
}
|
||||
header.setTtl(new UnsignedInteger((int) ttl));
|
||||
|
||||
props.setAbsoluteExpiryTime(new Date(msg.getJMSExpiration()));
|
||||
}
|
||||
if (msg.getJMSTimestamp() != 0) {
|
||||
props.setCreationTime(new Date(msg.getJMSTimestamp()));
|
||||
return body;
|
||||
}
|
||||
|
||||
final Enumeration<String> keys = msg.getPropertyNames();
|
||||
while (keys.hasMoreElements()) {
|
||||
String key = keys.nextElement();
|
||||
if (key.equals(messageFormatKey) || key.equals(nativeKey) || key.equals(ServerJMSMessage.NATIVE_MESSAGE_ID)) {
|
||||
// skip..
|
||||
} else if (key.equals(firstAcquirerKey)) {
|
||||
header.setFirstAcquirer(msg.getBooleanProperty(key));
|
||||
} else if (key.startsWith("JMSXDeliveryCount")) {
|
||||
// The AMQP delivery-count field only includes prior failed delivery attempts,
|
||||
// whereas JMSXDeliveryCount includes the first/current delivery attempt.
|
||||
int amqpDeliveryCount = msg.getIntProperty(key) - 1;
|
||||
if (amqpDeliveryCount > 0) {
|
||||
header.setDeliveryCount(new UnsignedInteger(amqpDeliveryCount));
|
||||
}
|
||||
} else if (key.startsWith("JMSXUserID")) {
|
||||
String value = msg.getStringProperty(key);
|
||||
props.setUserId(new Binary(value.getBytes(StandardCharsets.UTF_8)));
|
||||
} else if (key.startsWith("JMSXGroupID") || key.startsWith("_AMQ_GROUP_ID")) {
|
||||
String value = msg.getStringProperty(key);
|
||||
props.setGroupId(value);
|
||||
if (apMap == null) {
|
||||
apMap = new HashMap();
|
||||
}
|
||||
apMap.put(key, value);
|
||||
} else if (key.startsWith("JMSXGroupSeq")) {
|
||||
UnsignedInteger value = new UnsignedInteger(msg.getIntProperty(key));
|
||||
props.setGroupSequence(value);
|
||||
if (apMap == null) {
|
||||
apMap = new HashMap();
|
||||
}
|
||||
apMap.put(key, value);
|
||||
} else if (key.startsWith(prefixDeliveryAnnotationsKey)) {
|
||||
if (daMap == null) {
|
||||
daMap = new HashMap<>();
|
||||
}
|
||||
String name = key.substring(prefixDeliveryAnnotationsKey.length());
|
||||
daMap.put(Symbol.valueOf(name), msg.getObjectProperty(key));
|
||||
} else if (key.startsWith(prefixMessageAnnotationsKey)) {
|
||||
if (maMap == null) {
|
||||
maMap = new HashMap<>();
|
||||
}
|
||||
String name = key.substring(prefixMessageAnnotationsKey.length());
|
||||
maMap.put(Symbol.valueOf(name), msg.getObjectProperty(key));
|
||||
} else if (key.equals(contentTypeKey)) {
|
||||
props.setContentType(Symbol.getSymbol(msg.getStringProperty(key)));
|
||||
} else if (key.equals(contentEncodingKey)) {
|
||||
props.setContentEncoding(Symbol.getSymbol(msg.getStringProperty(key)));
|
||||
} else if (key.equals(replyToGroupIDKey)) {
|
||||
props.setReplyToGroupId(msg.getStringProperty(key));
|
||||
} else if (key.startsWith(prefixFooterKey)) {
|
||||
if (footerMap == null) {
|
||||
footerMap = new HashMap();
|
||||
}
|
||||
String name = key.substring(prefixFooterKey.length());
|
||||
footerMap.put(name, msg.getObjectProperty(key));
|
||||
} else if (key.equals(AMQPMessageTypes.AMQP_TYPE_KEY)) {
|
||||
// skip
|
||||
} else {
|
||||
if (apMap == null) {
|
||||
apMap = new HashMap();
|
||||
}
|
||||
Object objectProperty = msg.getObjectProperty(key);
|
||||
if (objectProperty instanceof byte[]) {
|
||||
Binary binary = new Binary((byte[]) objectProperty);
|
||||
apMap.put(key, binary);
|
||||
} else {
|
||||
apMap.put(key, objectProperty);
|
||||
}
|
||||
}
|
||||
private Binary getBinaryFromMessageBody(ServerJMSBytesMessage message) throws JMSException {
|
||||
byte[] data = new byte[(int) message.getBodyLength()];
|
||||
message.readBytes(data);
|
||||
message.reset(); // Need to reset after readBytes or future readBytes
|
||||
|
||||
return new Binary(data);
|
||||
}
|
||||
|
||||
MessageAnnotations ma = null;
|
||||
if (maMap != null) {
|
||||
ma = new MessageAnnotations(maMap);
|
||||
}
|
||||
DeliveryAnnotations da = null;
|
||||
if (daMap != null) {
|
||||
da = new DeliveryAnnotations(daMap);
|
||||
}
|
||||
ApplicationProperties ap = null;
|
||||
if (apMap != null) {
|
||||
ap = new ApplicationProperties(apMap);
|
||||
}
|
||||
Footer footer = null;
|
||||
if (footerMap != null) {
|
||||
footer = new Footer(footerMap);
|
||||
private Binary getBinaryFromMessageBody(ServerJMSTextMessage message) throws JMSException {
|
||||
Binary result = null;
|
||||
String text = message.getText();
|
||||
if (text != null) {
|
||||
result = new Binary(text.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
return (ProtonJMessage) org.apache.qpid.proton.message.Message.Factory.create(header, da, ma, props, ap, body, footer);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Binary getBinaryFromMessageBody(ServerJMSObjectMessage message) throws JMSException {
|
||||
message.getInnerMessage().getBodyBuffer().resetReaderIndex();
|
||||
int size = message.getInnerMessage().getBodyBuffer().readInt();
|
||||
byte[] bytes = new byte[size];
|
||||
message.getInnerMessage().getBodyBuffer().readBytes(bytes);
|
||||
|
||||
return new Binary(bytes);
|
||||
}
|
||||
|
||||
private Map<String, Object> getMapFromMessageBody(ServerJMSMapMessage message) throws JMSException {
|
||||
final HashMap<String, Object> map = new LinkedHashMap<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final Enumeration<String> names = message.getMapNames();
|
||||
while (names.hasMoreElements()) {
|
||||
String key = names.nextElement();
|
||||
Object value = message.getObject(key);
|
||||
if (value instanceof byte[]) {
|
||||
value = new Binary((byte[]) value);
|
||||
}
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private static byte destinationType(Destination destination) {
|
||||
|
|
|
@ -1,53 +0,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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.protocol.amqp.converter.message;
|
||||
|
||||
import javax.jms.BytesMessage;
|
||||
import javax.jms.Destination;
|
||||
import javax.jms.MapMessage;
|
||||
import javax.jms.Message;
|
||||
import javax.jms.ObjectMessage;
|
||||
import javax.jms.StreamMessage;
|
||||
import javax.jms.TextMessage;
|
||||
|
||||
public interface JMSVendor {
|
||||
|
||||
BytesMessage createBytesMessage();
|
||||
|
||||
StreamMessage createStreamMessage();
|
||||
|
||||
Message createMessage();
|
||||
|
||||
TextMessage createTextMessage();
|
||||
|
||||
ObjectMessage createObjectMessage();
|
||||
|
||||
MapMessage createMapMessage();
|
||||
|
||||
void setJMSXUserID(Message message, String value);
|
||||
|
||||
Destination createDestination(String name);
|
||||
|
||||
void setJMSXGroupID(Message message, String groupId);
|
||||
|
||||
void setJMSXGroupSequence(Message message, int value);
|
||||
|
||||
void setJMSXDeliveryCount(Message message, long value);
|
||||
|
||||
String toAddress(Destination destination);
|
||||
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
/**
|
||||
/*
|
||||
* 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
|
||||
* <p>
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
*
|
||||
* 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.
|
||||
|
@ -16,54 +16,36 @@
|
|||
*/
|
||||
package org.apache.activemq.artemis.protocol.amqp.converter.message;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import javax.jms.JMSException;
|
||||
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMessage;
|
||||
import org.apache.activemq.artemis.utils.IDGenerator;
|
||||
import org.apache.qpid.proton.codec.WritableBuffer;
|
||||
|
||||
public abstract class OutboundTransformer {
|
||||
|
||||
JMSVendor vendor;
|
||||
String prefixVendor;
|
||||
protected IDGenerator idGenerator;
|
||||
|
||||
String prefixDeliveryAnnotations = "DA_";
|
||||
String prefixMessageAnnotations = "MA_";
|
||||
String prefixFooter = "FT_";
|
||||
|
||||
String messageFormatKey;
|
||||
String nativeKey;
|
||||
String firstAcquirerKey;
|
||||
String prefixDeliveryAnnotationsKey;
|
||||
String prefixMessageAnnotationsKey;
|
||||
String contentTypeKey;
|
||||
String contentEncodingKey;
|
||||
String replyToGroupIDKey;
|
||||
String prefixFooterKey;
|
||||
|
||||
public OutboundTransformer(JMSVendor vendor) {
|
||||
this.vendor = vendor;
|
||||
this.setPrefixVendor("JMS_AMQP_");
|
||||
public OutboundTransformer(IDGenerator idGenerator) {
|
||||
this.idGenerator = idGenerator;
|
||||
}
|
||||
|
||||
public String getPrefixVendor() {
|
||||
return prefixVendor;
|
||||
}
|
||||
|
||||
public void setPrefixVendor(String prefixVendor) {
|
||||
this.prefixVendor = prefixVendor;
|
||||
|
||||
messageFormatKey = prefixVendor + "MESSAGE_FORMAT";
|
||||
nativeKey = prefixVendor + "NATIVE";
|
||||
firstAcquirerKey = prefixVendor + "FirstAcquirer";
|
||||
prefixDeliveryAnnotationsKey = prefixVendor + prefixDeliveryAnnotations;
|
||||
prefixMessageAnnotationsKey = prefixVendor + prefixMessageAnnotations;
|
||||
contentTypeKey = prefixVendor + "ContentType";
|
||||
contentEncodingKey = prefixVendor + "ContentEncoding";
|
||||
replyToGroupIDKey = prefixVendor + "ReplyToGroupID";
|
||||
prefixFooterKey = prefixVendor + prefixFooter;
|
||||
/**
|
||||
* Given an JMS Message perform a conversion to an AMQP Message and encode into a form that
|
||||
* is ready for transmission.
|
||||
*
|
||||
* @param message
|
||||
* the message to transform
|
||||
* @param buffer
|
||||
* the buffer where encoding should write to
|
||||
*
|
||||
* @return the message format key of the encoded message.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during message transformation
|
||||
*/
|
||||
public abstract long transform(ServerJMSMessage message, WritableBuffer buffer) throws JMSException, UnsupportedEncodingException;
|
||||
|
||||
}
|
||||
|
||||
public JMSVendor getVendor() {
|
||||
return vendor;
|
||||
}
|
||||
|
||||
public void setVendor(JMSVendor vendor) {
|
||||
this.vendor = vendor;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.protocol.amqp.exceptions;
|
||||
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQExceptionType;
|
||||
import org.apache.qpid.proton.amqp.transport.AmqpError;
|
||||
|
||||
public class ActiveMQAMQPInvalidContentTypeException extends ActiveMQAMQPException {
|
||||
|
||||
public ActiveMQAMQPInvalidContentTypeException(String message) {
|
||||
super(AmqpError.INTERNAL_ERROR, message, ActiveMQExceptionType.INTERNAL_ERROR);
|
||||
}
|
||||
}
|
|
@ -19,8 +19,6 @@ package org.apache.activemq.artemis.protocol.amqp.proton;
|
|||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import org.apache.activemq.artemis.api.core.SimpleString;
|
||||
import org.apache.activemq.artemis.core.server.QueueQueryResult;
|
||||
import org.apache.activemq.artemis.core.transaction.Transaction;
|
||||
|
@ -52,9 +50,11 @@ import org.apache.qpid.proton.amqp.transport.ErrorCondition;
|
|||
import org.apache.qpid.proton.amqp.transport.SenderSettleMode;
|
||||
import org.apache.qpid.proton.engine.Delivery;
|
||||
import org.apache.qpid.proton.engine.Sender;
|
||||
import org.apache.qpid.proton.message.ProtonJMessage;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
|
||||
public class ProtonServerSenderContext extends ProtonInitializable implements ProtonDeliveryHandler {
|
||||
|
||||
private static final Logger log = Logger.getLogger(ProtonServerSenderContext.class);
|
||||
|
@ -407,33 +407,6 @@ public class ProtonServerSenderContext extends ProtonInitializable implements Pr
|
|||
return 0;
|
||||
}
|
||||
|
||||
//encode the message
|
||||
ProtonJMessage serverMessage;
|
||||
try {
|
||||
// This can be done a lot better here
|
||||
serverMessage = sessionSPI.encodeMessage(message, deliveryCount);
|
||||
} catch (Throwable e) {
|
||||
log.warn(e.getMessage(), e);
|
||||
throw new ActiveMQAMQPInternalErrorException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
return performSend(serverMessage, message);
|
||||
}
|
||||
|
||||
private static boolean hasCapabilities(Symbol symbol, Source source) {
|
||||
if (source != null) {
|
||||
if (source.getCapabilities() != null) {
|
||||
for (Symbol cap : source.getCapabilities()) {
|
||||
if (symbol.equals(cap)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected int performSend(ProtonJMessage serverMessage, Object context) {
|
||||
if (!creditsSemaphore.tryAcquire()) {
|
||||
try {
|
||||
creditsSemaphore.acquire();
|
||||
|
@ -444,22 +417,32 @@ public class ProtonServerSenderContext extends ProtonInitializable implements Pr
|
|||
}
|
||||
}
|
||||
|
||||
//presettle means we can ack the message on the dealer side before we send it, i.e. for browsers
|
||||
// presettle means we can settle the message on the dealer side before we send it, i.e. for browsers
|
||||
boolean preSettle = sender.getRemoteSenderSettleMode() == SenderSettleMode.SETTLED;
|
||||
|
||||
//we only need a tag if we are going to ack later
|
||||
// we only need a tag if we are going to settle later
|
||||
byte[] tag = preSettle ? new byte[0] : protonSession.getTag();
|
||||
|
||||
ByteBuf nettyBuffer = PooledByteBufAllocator.DEFAULT.heapBuffer(1024);
|
||||
try {
|
||||
serverMessage.encode(new NettyWritable(nettyBuffer));
|
||||
long messageFormat = 0;
|
||||
|
||||
// Encode the Server Message into the given Netty Buffer as an AMQP
|
||||
// Message transformed from the internal message model.
|
||||
try {
|
||||
messageFormat = sessionSPI.encodeMessage(message, deliveryCount, new NettyWritable(nettyBuffer));
|
||||
} catch (Throwable e) {
|
||||
log.warn(e.getMessage(), e);
|
||||
throw new ActiveMQAMQPInternalErrorException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
int size = nettyBuffer.writerIndex();
|
||||
|
||||
synchronized (connection.getLock()) {
|
||||
final Delivery delivery;
|
||||
delivery = sender.delivery(tag, 0, tag.length);
|
||||
delivery.setContext(context);
|
||||
delivery.setMessageFormat((int) messageFormat);
|
||||
delivery.setContext(message);
|
||||
|
||||
// this will avoid a copy.. patch provided by Norman using buffer.array()
|
||||
sender.send(nettyBuffer.array(), nettyBuffer.arrayOffset() + nettyBuffer.readerIndex(), nettyBuffer.readableBytes());
|
||||
|
@ -479,6 +462,19 @@ public class ProtonServerSenderContext extends ProtonInitializable implements Pr
|
|||
}
|
||||
}
|
||||
|
||||
private static boolean hasCapabilities(Symbol symbol, Source source) {
|
||||
if (source != null) {
|
||||
if (source.getCapabilities() != null) {
|
||||
for (Symbol cap : source.getCapabilities()) {
|
||||
if (symbol.equals(cap)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String createQueueName(String clientId, String pubId) {
|
||||
return clientId + "." + pubId;
|
||||
}
|
||||
|
|
|
@ -16,32 +16,31 @@
|
|||
*/
|
||||
package org.apache.activemq.artemis.protocol.amqp.converter;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import static org.apache.activemq.artemis.api.core.Message.BYTES_TYPE;
|
||||
import static org.apache.activemq.artemis.api.core.Message.MAP_TYPE;
|
||||
import static org.apache.activemq.artemis.api.core.Message.STREAM_TYPE;
|
||||
import static org.apache.activemq.artemis.api.core.Message.TEXT_TYPE;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.wrapMessage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
|
||||
import org.apache.activemq.artemis.api.core.SimpleString;
|
||||
import org.apache.activemq.artemis.core.journal.EncodingSupport;
|
||||
import org.apache.activemq.artemis.core.server.ServerMessage;
|
||||
import org.apache.activemq.artemis.core.server.impl.ServerMessageImpl;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSBytesMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMapMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSObjectMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSStreamMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSTextMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.message.EncodedMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.util.NettyWritable;
|
||||
import org.apache.activemq.artemis.utils.SimpleIDGenerator;
|
||||
import org.apache.blacklist.ABadClass;
|
||||
import org.apache.qpid.proton.amqp.Binary;
|
||||
import org.apache.qpid.proton.amqp.messaging.AmqpSequence;
|
||||
import org.apache.qpid.proton.amqp.messaging.AmqpValue;
|
||||
|
@ -53,10 +52,13 @@ import org.apache.qpid.proton.message.impl.MessageImpl;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
|
||||
public class TestConversions extends Assert {
|
||||
|
||||
@Test
|
||||
public void testObjectMessageWhiteList() throws Exception {
|
||||
public void testAmqpValueOfBooleanIsPassedThrough() throws Exception {
|
||||
Map<String, Object> mapprop = createPropertiesMap();
|
||||
ApplicationProperties properties = new ApplicationProperties(mapprop);
|
||||
MessageImpl message = (MessageImpl) Message.Factory.create();
|
||||
|
@ -73,39 +75,15 @@ public class TestConversions extends Assert {
|
|||
EncodedMessage encodedMessage = encodeMessage(message);
|
||||
|
||||
ProtonMessageConverter converter = new ProtonMessageConverter(new SimpleIDGenerator(0));
|
||||
ServerJMSObjectMessage serverMessage = (ServerJMSObjectMessage) converter.inboundJMSType(encodedMessage);
|
||||
ServerMessage serverMessage = converter.inbound(encodedMessage);
|
||||
|
||||
verifyProperties(serverMessage);
|
||||
verifyProperties(new ServerJMSMessage(serverMessage, 0));
|
||||
|
||||
assertEquals(true, serverMessage.getObject());
|
||||
EncodedMessage encoded = (EncodedMessage) converter.outbound(serverMessage, 0);
|
||||
Message amqpMessage = encoded.decode();
|
||||
|
||||
Object obj = converter.outbound((ServerMessage) serverMessage.getInnerMessage(), 0);
|
||||
|
||||
AmqpValue value = (AmqpValue) ((Message) obj).getBody();
|
||||
AmqpValue value = (AmqpValue) amqpMessage.getBody();
|
||||
assertEquals(value.getValue(), true);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObjectMessageNotOnWhiteList() throws Exception {
|
||||
|
||||
ProtonMessageConverter converter = new ProtonMessageConverter(new SimpleIDGenerator(0));
|
||||
ServerMessageImpl message = new ServerMessageImpl(1, 1024);
|
||||
message.setType((byte) 2);
|
||||
ServerJMSObjectMessage serverMessage = new ServerJMSObjectMessage(message, 1024);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ObjectOutputStream ois = new ObjectOutputStream(out);
|
||||
ois.writeObject(new ABadClass());
|
||||
byte[] src = out.toByteArray();
|
||||
serverMessage.getInnerMessage().getBodyBuffer().writeInt(src.length);
|
||||
serverMessage.getInnerMessage().getBodyBuffer().writeBytes(src);
|
||||
|
||||
try {
|
||||
converter.outbound((ServerMessage) serverMessage.getInnerMessage(), 0);
|
||||
fail("should throw ClassNotFoundException");
|
||||
} catch (ClassNotFoundException e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -126,22 +104,23 @@ public class TestConversions extends Assert {
|
|||
EncodedMessage encodedMessage = encodeMessage(message);
|
||||
|
||||
ProtonMessageConverter converter = new ProtonMessageConverter(new SimpleIDGenerator(0));
|
||||
ServerJMSBytesMessage serverMessage = (ServerJMSBytesMessage) converter.inboundJMSType(encodedMessage);
|
||||
ServerMessage serverMessage = converter.inbound(encodedMessage);
|
||||
|
||||
verifyProperties(serverMessage);
|
||||
ServerJMSBytesMessage bytesMessage = (ServerJMSBytesMessage) wrapMessage(BYTES_TYPE, serverMessage, 0);
|
||||
|
||||
assertEquals(bodyBytes.length, serverMessage.getBodyLength());
|
||||
verifyProperties(bytesMessage);
|
||||
|
||||
assertEquals(bodyBytes.length, bytesMessage.getBodyLength());
|
||||
|
||||
byte[] newBodyBytes = new byte[4];
|
||||
|
||||
serverMessage.readBytes(newBodyBytes);
|
||||
bytesMessage.readBytes(newBodyBytes);
|
||||
|
||||
Assert.assertArrayEquals(bodyBytes, newBodyBytes);
|
||||
|
||||
Object obj = converter.outbound((ServerMessage) serverMessage.getInnerMessage(), 0);
|
||||
Object obj = converter.outbound(serverMessage, 0);
|
||||
|
||||
System.out.println("output = " + obj);
|
||||
|
||||
}
|
||||
|
||||
private void verifyProperties(javax.jms.Message message) throws Exception {
|
||||
|
@ -175,25 +154,25 @@ public class TestConversions extends Assert {
|
|||
EncodedMessage encodedMessage = encodeMessage(message);
|
||||
|
||||
ProtonMessageConverter converter = new ProtonMessageConverter(new SimpleIDGenerator(0));
|
||||
ServerJMSMapMessage serverMessage = (ServerJMSMapMessage) converter.inboundJMSType(encodedMessage);
|
||||
ServerMessage serverMessage = converter.inbound(encodedMessage);
|
||||
|
||||
verifyProperties(serverMessage);
|
||||
ServerJMSMapMessage mapMessage = (ServerJMSMapMessage) wrapMessage(MAP_TYPE, serverMessage, 0);
|
||||
mapMessage.decode();
|
||||
|
||||
Assert.assertEquals(1, serverMessage.getInt("someint"));
|
||||
Assert.assertEquals("value", serverMessage.getString("somestr"));
|
||||
verifyProperties(mapMessage);
|
||||
|
||||
Object obj = converter.outbound((ServerMessage) serverMessage.getInnerMessage(), 0);
|
||||
Assert.assertEquals(1, mapMessage.getInt("someint"));
|
||||
Assert.assertEquals("value", mapMessage.getString("somestr"));
|
||||
|
||||
reEncodeMsg(obj);
|
||||
EncodedMessage encoded = (EncodedMessage) converter.outbound(serverMessage, 0);
|
||||
Message amqpMessage = encoded.decode();
|
||||
|
||||
MessageImpl outMessage = (MessageImpl) obj;
|
||||
AmqpValue value = (AmqpValue) outMessage.getBody();
|
||||
Map mapoutput = (Map) value.getValue();
|
||||
AmqpValue value = (AmqpValue) amqpMessage.getBody();
|
||||
Map<?, ?> mapoutput = (Map<?, ?>) value.getValue();
|
||||
|
||||
assertEquals(Integer.valueOf(1), mapoutput.get("someint"));
|
||||
|
||||
System.out.println("output = " + obj);
|
||||
|
||||
System.out.println("output = " + amqpMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -212,26 +191,25 @@ public class TestConversions extends Assert {
|
|||
EncodedMessage encodedMessage = encodeMessage(message);
|
||||
|
||||
ProtonMessageConverter converter = new ProtonMessageConverter(new SimpleIDGenerator(0));
|
||||
ServerJMSStreamMessage serverMessage = (ServerJMSStreamMessage) converter.inboundJMSType(encodedMessage);
|
||||
ServerMessage serverMessage = converter.inbound(encodedMessage);
|
||||
|
||||
simulatePersistence(serverMessage);
|
||||
|
||||
verifyProperties(serverMessage);
|
||||
ServerJMSStreamMessage streamMessage = (ServerJMSStreamMessage) wrapMessage(STREAM_TYPE, serverMessage, 0);
|
||||
|
||||
serverMessage.reset();
|
||||
verifyProperties(streamMessage);
|
||||
|
||||
assertEquals(10, serverMessage.readInt());
|
||||
assertEquals("10", serverMessage.readString());
|
||||
streamMessage.reset();
|
||||
|
||||
Object obj = converter.outbound((ServerMessage) serverMessage.getInnerMessage(), 0);
|
||||
assertEquals(10, streamMessage.readInt());
|
||||
assertEquals("10", streamMessage.readString());
|
||||
|
||||
reEncodeMsg(obj);
|
||||
EncodedMessage encoded = (EncodedMessage) converter.outbound(serverMessage, 0);
|
||||
Message amqpMessage = encoded.decode();
|
||||
|
||||
MessageImpl outMessage = (MessageImpl) obj;
|
||||
List list = ((AmqpSequence) outMessage.getBody()).getValue();
|
||||
List<?> list = ((AmqpSequence) amqpMessage.getBody()).getValue();
|
||||
Assert.assertEquals(Integer.valueOf(10), list.get(0));
|
||||
Assert.assertEquals("10", list.get(1));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -247,33 +225,33 @@ public class TestConversions extends Assert {
|
|||
EncodedMessage encodedMessage = encodeMessage(message);
|
||||
|
||||
ProtonMessageConverter converter = new ProtonMessageConverter(new SimpleIDGenerator(0));
|
||||
ServerJMSTextMessage serverMessage = (ServerJMSTextMessage) converter.inboundJMSType(encodedMessage);
|
||||
ServerMessage serverMessage = converter.inbound(encodedMessage);
|
||||
|
||||
simulatePersistence(serverMessage);
|
||||
|
||||
verifyProperties(serverMessage);
|
||||
ServerJMSTextMessage textMessage = (ServerJMSTextMessage) wrapMessage(TEXT_TYPE, serverMessage, 0);
|
||||
textMessage.decode();
|
||||
|
||||
Assert.assertEquals(text, serverMessage.getText());
|
||||
verifyProperties(textMessage);
|
||||
|
||||
Object obj = converter.outbound((ServerMessage) serverMessage.getInnerMessage(), 0);
|
||||
Assert.assertEquals(text, textMessage.getText());
|
||||
|
||||
reEncodeMsg(obj);
|
||||
EncodedMessage encoded = (EncodedMessage) converter.outbound(serverMessage, 0);
|
||||
Message amqpMessage = encoded.decode();
|
||||
|
||||
MessageImpl outMessage = (MessageImpl) obj;
|
||||
AmqpValue value = (AmqpValue) outMessage.getBody();
|
||||
AmqpValue value = (AmqpValue) amqpMessage.getBody();
|
||||
String textValue = (String) value.getValue();
|
||||
|
||||
Assert.assertEquals(text, textValue);
|
||||
|
||||
System.out.println("output = " + obj);
|
||||
|
||||
System.out.println("output = " + amqpMessage);
|
||||
}
|
||||
|
||||
private void simulatePersistence(ServerJMSMessage serverMessage) {
|
||||
serverMessage.getInnerMessage().setAddress(new SimpleString("jms.queue.SomeAddress"));
|
||||
private void simulatePersistence(ServerMessage serverMessage) {
|
||||
serverMessage.setAddress(new SimpleString("jms.queue.SomeAddress"));
|
||||
// This is just to simulate what would happen during the persistence of the message
|
||||
// We need to still be able to recover the message when we read it back
|
||||
((EncodingSupport) serverMessage.getInnerMessage()).encode(new EmptyBuffer());
|
||||
((EncodingSupport) serverMessage).encode(new EmptyBuffer());
|
||||
}
|
||||
|
||||
private ProtonJMessage reEncodeMsg(Object obj) {
|
||||
|
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* 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.protocol.amqp.converter.message;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.apache.activemq.artemis.protocol.amqp.exceptions.ActiveMQAMQPInvalidContentTypeException;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AMQPContentTypeSupportTest {
|
||||
|
||||
@Test(expected = ActiveMQAMQPInvalidContentTypeException.class)
|
||||
public void testParseContentTypeWithOnlyType() throws Exception {
|
||||
doParseContentTypeTestImpl("type", null);
|
||||
}
|
||||
|
||||
@Test(expected = ActiveMQAMQPInvalidContentTypeException.class)
|
||||
public void testParseContentTypeEndsWithSlash() throws Exception {
|
||||
doParseContentTypeTestImpl("type/", null);
|
||||
}
|
||||
|
||||
@Test(expected = ActiveMQAMQPInvalidContentTypeException.class)
|
||||
public void testParseContentTypeMissingSubtype() throws Exception {
|
||||
doParseContentTypeTestImpl("type/;", null);
|
||||
}
|
||||
|
||||
@Test(expected = ActiveMQAMQPInvalidContentTypeException.class)
|
||||
public void testParseContentTypeEmptyString() throws Exception {
|
||||
doParseContentTypeTestImpl("", null);
|
||||
}
|
||||
|
||||
@Test(expected = ActiveMQAMQPInvalidContentTypeException.class)
|
||||
public void testParseContentTypeNullString() throws Exception {
|
||||
doParseContentTypeTestImpl(null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeNoParamsAfterSeparatorNonTextual() throws Exception {
|
||||
// Expect null as this is not a textual type
|
||||
doParseContentTypeTestImpl("type/subtype;", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeNoParamsAfterSeparatorTextualType() throws Exception {
|
||||
doParseContentTypeTestImpl("text/plain;", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeEmptyParamsAfterSeparator() throws Exception {
|
||||
doParseContentTypeTestImpl("text/plain;;", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeNoParams() throws Exception {
|
||||
doParseContentTypeTestImpl("text/plain", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithCharsetUtf8() throws Exception {
|
||||
doParseContentTypeTestImpl("text/plain;charset=utf-8", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithCharsetAscii() throws Exception {
|
||||
doParseContentTypeTestImpl("text/plain;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithMultipleParams() throws Exception {
|
||||
doParseContentTypeTestImpl("text/plain; param=value; charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithCharsetQuoted() throws Exception {
|
||||
doParseContentTypeTestImpl("text/plain;charset=\"us-ascii\"", StandardCharsets.US_ASCII);
|
||||
}
|
||||
|
||||
@Test(expected = ActiveMQAMQPInvalidContentTypeException.class)
|
||||
public void testParseContentTypeWithCharsetQuotedEmpty() throws Exception {
|
||||
doParseContentTypeTestImpl("text/plain;charset=\"\"", null);
|
||||
}
|
||||
|
||||
@Test(expected = ActiveMQAMQPInvalidContentTypeException.class)
|
||||
public void testParseContentTypeWithCharsetQuoteNotClosed() throws Exception {
|
||||
doParseContentTypeTestImpl("text/plain;charset=\"unclosed", null);
|
||||
}
|
||||
|
||||
@Test(expected = ActiveMQAMQPInvalidContentTypeException.class)
|
||||
public void testParseContentTypeWithCharsetQuoteNotClosedEmpty() throws Exception {
|
||||
doParseContentTypeTestImpl("text/plain;charset=\"", null);
|
||||
}
|
||||
|
||||
@Test(expected = ActiveMQAMQPInvalidContentTypeException.class)
|
||||
public void testParseContentTypeWithNoCharsetValue() throws Exception {
|
||||
doParseContentTypeTestImpl("text/plain;charset=", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithTextPlain() throws Exception {
|
||||
doParseContentTypeTestImpl("text/plain;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doParseContentTypeTestImpl("text/plain;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doParseContentTypeTestImpl("text/plain;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doParseContentTypeTestImpl("text/plain", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithTextJson() throws Exception {
|
||||
doParseContentTypeTestImpl("text/json;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doParseContentTypeTestImpl("text/json;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doParseContentTypeTestImpl("text/json;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doParseContentTypeTestImpl("text/json", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithTextHtml() throws Exception {
|
||||
doParseContentTypeTestImpl("text/html;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doParseContentTypeTestImpl("text/html;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doParseContentTypeTestImpl("text/html;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doParseContentTypeTestImpl("text/html", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithTextFoo() throws Exception {
|
||||
doParseContentTypeTestImpl("text/foo;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doParseContentTypeTestImpl("text/foo;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doParseContentTypeTestImpl("text/foo;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doParseContentTypeTestImpl("text/foo", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithApplicationJson() throws Exception {
|
||||
doParseContentTypeTestImpl("application/json;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doParseContentTypeTestImpl("application/json;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doParseContentTypeTestImpl("application/json;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doParseContentTypeTestImpl("application/json", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithApplicationJsonVariant() throws Exception {
|
||||
doParseContentTypeTestImpl("application/something+json;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doParseContentTypeTestImpl("application/something+json;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doParseContentTypeTestImpl("application/something+json;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doParseContentTypeTestImpl("application/something+json", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithApplicationJavascript() throws Exception {
|
||||
doParseContentTypeTestImpl("application/javascript;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doParseContentTypeTestImpl("application/javascript;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doParseContentTypeTestImpl("application/javascript;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doParseContentTypeTestImpl("application/javascript", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithApplicationEcmascript() throws Exception {
|
||||
doParseContentTypeTestImpl("application/ecmascript;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doParseContentTypeTestImpl("application/ecmascript;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doParseContentTypeTestImpl("application/ecmascript;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doParseContentTypeTestImpl("application/ecmascript", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithApplicationXml() throws Exception {
|
||||
doParseContentTypeTestImpl("application/xml;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doParseContentTypeTestImpl("application/xml;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doParseContentTypeTestImpl("application/xml;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doParseContentTypeTestImpl("application/xml", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithApplicationXmlVariant() throws Exception {
|
||||
doParseContentTypeTestImpl("application/something+xml;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doParseContentTypeTestImpl("application/something+xml;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doParseContentTypeTestImpl("application/something+xml;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doParseContentTypeTestImpl("application/something+xml", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithApplicationXmlDtd() throws Exception {
|
||||
doParseContentTypeTestImpl("application/xml-dtd;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doParseContentTypeTestImpl("application/xml-dtd;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doParseContentTypeTestImpl("application/xml-dtd;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doParseContentTypeTestImpl("application/xml-dtd", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithApplicationOtherNotTextual() throws Exception {
|
||||
// Expect null as this is not a textual type
|
||||
doParseContentTypeTestImpl("application/other", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithApplicationOctetStream() throws Exception {
|
||||
// Expect null as this is not a textual type
|
||||
doParseContentTypeTestImpl(AMQPMessageSupport.OCTET_STREAM_CONTENT_TYPE, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseContentTypeWithApplicationJavaSerialized() throws Exception {
|
||||
// Expect null as this is not a textual type
|
||||
doParseContentTypeTestImpl(AMQPMessageSupport.SERIALIZED_JAVA_OBJECT_CONTENT_TYPE, null);
|
||||
}
|
||||
|
||||
private void doParseContentTypeTestImpl(String contentType, Charset expected) throws ActiveMQAMQPInvalidContentTypeException {
|
||||
Charset charset = AMQPContentTypeSupport.parseContentTypeForTextualCharset(contentType);
|
||||
if (expected == null) {
|
||||
assertNull("Expected no charset, but got:" + charset, charset);
|
||||
} else {
|
||||
assertEquals("Charset not as expected", expected, charset);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,391 @@
|
|||
/*
|
||||
*
|
||||
* 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.protocol.amqp.converter.message;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.activemq.artemis.protocol.amqp.exceptions.ActiveMQAMQPException;
|
||||
import org.apache.qpid.proton.amqp.Binary;
|
||||
import org.apache.qpid.proton.amqp.UnsignedLong;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AMQPMessageIdHelperTest {
|
||||
|
||||
private AMQPMessageIdHelper messageIdHelper;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
messageIdHelper = new AMQPMessageIdHelper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toBaseMessageIdString(Object)} returns null if given
|
||||
* null
|
||||
*/
|
||||
@Test
|
||||
public void testToBaseMessageIdStringWithNull() {
|
||||
String nullString = null;
|
||||
assertNull("null string should have been returned", messageIdHelper.toBaseMessageIdString(nullString));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toBaseMessageIdString(Object)} throws an IAE if given
|
||||
* an unexpected object type.
|
||||
*/
|
||||
@Test
|
||||
public void testToBaseMessageIdStringThrowsIAEWithUnexpectedType() {
|
||||
try {
|
||||
messageIdHelper.toBaseMessageIdString(new Object());
|
||||
fail("expected exception not thrown");
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toBaseMessageIdString(Object)} returns the given
|
||||
* basic string unchanged
|
||||
*/
|
||||
@Test
|
||||
public void testToBaseMessageIdStringWithString() {
|
||||
String stringMessageId = "myIdString";
|
||||
|
||||
String baseMessageIdString = messageIdHelper.toBaseMessageIdString(stringMessageId);
|
||||
assertNotNull("null string should not have been returned", baseMessageIdString);
|
||||
assertEquals("expected base id string was not returned", stringMessageId, baseMessageIdString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toBaseMessageIdString(Object)} returns a string
|
||||
* indicating an AMQP encoded string, when the given string happens to already begin with the
|
||||
* {@link AMQPMessageIdHelper#AMQP_UUID_PREFIX}.
|
||||
*/
|
||||
@Test
|
||||
public void testToBaseMessageIdStringWithStringBeginningWithEncodingPrefixForUUID() {
|
||||
String uuidStringMessageId = AMQPMessageIdHelper.AMQP_UUID_PREFIX + UUID.randomUUID();
|
||||
String expected = AMQPMessageIdHelper.AMQP_STRING_PREFIX + uuidStringMessageId;
|
||||
|
||||
String baseMessageIdString = messageIdHelper.toBaseMessageIdString(uuidStringMessageId);
|
||||
assertNotNull("null string should not have been returned", baseMessageIdString);
|
||||
assertEquals("expected base id string was not returned", expected, baseMessageIdString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toBaseMessageIdString(Object)} returns a string
|
||||
* indicating an AMQP encoded string, when the given string happens to already begin with the
|
||||
* {@link AMQPMessageIdHelper#AMQP_ULONG_PREFIX}.
|
||||
*/
|
||||
@Test
|
||||
public void testToBaseMessageIdStringWithStringBeginningWithEncodingPrefixForLong() {
|
||||
String longStringMessageId = AMQPMessageIdHelper.AMQP_ULONG_PREFIX + Long.valueOf(123456789L);
|
||||
String expected = AMQPMessageIdHelper.AMQP_STRING_PREFIX + longStringMessageId;
|
||||
|
||||
String baseMessageIdString = messageIdHelper.toBaseMessageIdString(longStringMessageId);
|
||||
assertNotNull("null string should not have been returned", baseMessageIdString);
|
||||
assertEquals("expected base id string was not returned", expected, baseMessageIdString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toBaseMessageIdString(Object)} returns a string
|
||||
* indicating an AMQP encoded string, when the given string happens to already begin with the
|
||||
* {@link AMQPMessageIdHelper#AMQP_BINARY_PREFIX}.
|
||||
*/
|
||||
@Test
|
||||
public void testToBaseMessageIdStringWithStringBeginningWithEncodingPrefixForBinary() {
|
||||
String binaryStringMessageId = AMQPMessageIdHelper.AMQP_BINARY_PREFIX + "0123456789ABCDEF";
|
||||
String expected = AMQPMessageIdHelper.AMQP_STRING_PREFIX + binaryStringMessageId;
|
||||
|
||||
String baseMessageIdString = messageIdHelper.toBaseMessageIdString(binaryStringMessageId);
|
||||
assertNotNull("null string should not have been returned", baseMessageIdString);
|
||||
assertEquals("expected base id string was not returned", expected, baseMessageIdString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toBaseMessageIdString(Object)} returns a string
|
||||
* indicating an AMQP encoded string (effectively twice), when the given string happens to
|
||||
* already begin with the {@link AMQPMessageIdHelper#AMQP_STRING_PREFIX}.
|
||||
*/
|
||||
@Test
|
||||
public void testToBaseMessageIdStringWithStringBeginningWithEncodingPrefixForString() {
|
||||
String stringMessageId = AMQPMessageIdHelper.AMQP_STRING_PREFIX + "myStringId";
|
||||
String expected = AMQPMessageIdHelper.AMQP_STRING_PREFIX + stringMessageId;
|
||||
|
||||
String baseMessageIdString = messageIdHelper.toBaseMessageIdString(stringMessageId);
|
||||
assertNotNull("null string should not have been returned", baseMessageIdString);
|
||||
assertEquals("expected base id string was not returned", expected, baseMessageIdString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toBaseMessageIdString(Object)} returns a string
|
||||
* indicating an AMQP encoded UUID when given a UUID object.
|
||||
*/
|
||||
@Test
|
||||
public void testToBaseMessageIdStringWithUUID() {
|
||||
UUID uuidMessageId = UUID.randomUUID();
|
||||
String expected = AMQPMessageIdHelper.AMQP_UUID_PREFIX + uuidMessageId.toString();
|
||||
|
||||
String baseMessageIdString = messageIdHelper.toBaseMessageIdString(uuidMessageId);
|
||||
assertNotNull("null string should not have been returned", baseMessageIdString);
|
||||
assertEquals("expected base id string was not returned", expected, baseMessageIdString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toBaseMessageIdString(Object)} returns a string
|
||||
* indicating an AMQP encoded ulong when given a UnsignedLong object.
|
||||
*/
|
||||
@Test
|
||||
public void testToBaseMessageIdStringWithUnsignedLong() {
|
||||
UnsignedLong uLongMessageId = UnsignedLong.valueOf(123456789L);
|
||||
String expected = AMQPMessageIdHelper.AMQP_ULONG_PREFIX + uLongMessageId.toString();
|
||||
|
||||
String baseMessageIdString = messageIdHelper.toBaseMessageIdString(uLongMessageId);
|
||||
assertNotNull("null string should not have been returned", baseMessageIdString);
|
||||
assertEquals("expected base id string was not returned", expected, baseMessageIdString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toBaseMessageIdString(Object)} returns a string
|
||||
* indicating an AMQP encoded binary when given a Binary object.
|
||||
*/
|
||||
@Test
|
||||
public void testToBaseMessageIdStringWithBinary() {
|
||||
byte[] bytes = new byte[] {(byte) 0x00, (byte) 0xAB, (byte) 0x09, (byte) 0xFF};
|
||||
Binary binary = new Binary(bytes);
|
||||
|
||||
String expected = AMQPMessageIdHelper.AMQP_BINARY_PREFIX + "00AB09FF";
|
||||
|
||||
String baseMessageIdString = messageIdHelper.toBaseMessageIdString(binary);
|
||||
assertNotNull("null string should not have been returned", baseMessageIdString);
|
||||
assertEquals("expected base id string was not returned", expected, baseMessageIdString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toIdObject(String)} returns an UnsignedLong when
|
||||
* given a string indicating an encoded AMQP ulong id.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testToIdObjectWithEncodedUlong() throws Exception {
|
||||
UnsignedLong longId = UnsignedLong.valueOf(123456789L);
|
||||
String provided = AMQPMessageIdHelper.AMQP_ULONG_PREFIX + "123456789";
|
||||
|
||||
Object idObject = messageIdHelper.toIdObject(provided);
|
||||
assertNotNull("null object should not have been returned", idObject);
|
||||
assertEquals("expected id object was not returned", longId, idObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toIdObject(String)} returns a Binary when given a
|
||||
* string indicating an encoded AMQP binary id, using upper case hex characters
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testToIdObjectWithEncodedBinaryUppercaseHexString() throws Exception {
|
||||
byte[] bytes = new byte[] {(byte) 0x00, (byte) 0xAB, (byte) 0x09, (byte) 0xFF};
|
||||
Binary binaryId = new Binary(bytes);
|
||||
|
||||
String provided = AMQPMessageIdHelper.AMQP_BINARY_PREFIX + "00AB09FF";
|
||||
|
||||
Object idObject = messageIdHelper.toIdObject(provided);
|
||||
assertNotNull("null object should not have been returned", idObject);
|
||||
assertEquals("expected id object was not returned", binaryId, idObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toIdObject(String)} returns null when given null.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testToIdObjectWithNull() throws Exception {
|
||||
assertNull("null object should have been returned", messageIdHelper.toIdObject(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toIdObject(String)} returns a Binary when given a
|
||||
* string indicating an encoded AMQP binary id, using lower case hex characters.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testToIdObjectWithEncodedBinaryLowercaseHexString() throws Exception {
|
||||
byte[] bytes = new byte[] {(byte) 0x00, (byte) 0xAB, (byte) 0x09, (byte) 0xFF};
|
||||
Binary binaryId = new Binary(bytes);
|
||||
|
||||
String provided = AMQPMessageIdHelper.AMQP_BINARY_PREFIX + "00ab09ff";
|
||||
|
||||
Object idObject = messageIdHelper.toIdObject(provided);
|
||||
assertNotNull("null object should not have been returned", idObject);
|
||||
assertEquals("expected id object was not returned", binaryId, idObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toIdObject(String)} returns a UUID when given a
|
||||
* string indicating an encoded AMQP uuid id.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testToIdObjectWithEncodedUuid() throws Exception {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
String provided = AMQPMessageIdHelper.AMQP_UUID_PREFIX + uuid.toString();
|
||||
|
||||
Object idObject = messageIdHelper.toIdObject(provided);
|
||||
assertNotNull("null object should not have been returned", idObject);
|
||||
assertEquals("expected id object was not returned", uuid, idObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toIdObject(String)} returns a string when given a
|
||||
* string without any type encoding prefix.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testToIdObjectWithStringContainingNoEncodingPrefix() throws Exception {
|
||||
String stringId = "myStringId";
|
||||
|
||||
Object idObject = messageIdHelper.toIdObject(stringId);
|
||||
assertNotNull("null object should not have been returned", idObject);
|
||||
assertEquals("expected id object was not returned", stringId, idObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toIdObject(String)} returns the remainder of the
|
||||
* provided string after removing the {@link AMQPMessageIdHelper#AMQP_STRING_PREFIX} prefix.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testToIdObjectWithStringContainingStringEncodingPrefix() throws Exception {
|
||||
String suffix = "myStringSuffix";
|
||||
String stringId = AMQPMessageIdHelper.AMQP_STRING_PREFIX + suffix;
|
||||
|
||||
Object idObject = messageIdHelper.toIdObject(stringId);
|
||||
assertNotNull("null object should not have been returned", idObject);
|
||||
assertEquals("expected id object was not returned", suffix, idObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that when given a string with with the {@link AMQPMessageIdHelper#AMQP_STRING_PREFIX}
|
||||
* prefix and then additionally the {@link AMQPMessageIdHelper#AMQP_UUID_PREFIX}, the
|
||||
* {@link AMQPMessageIdHelper#toIdObject(String)} method returns the remainder of the
|
||||
* provided string after removing the {@link AMQPMessageIdHelper#AMQP_STRING_PREFIX} prefix.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testToIdObjectWithStringContainingStringEncodingPrefixAndThenUuidPrefix() throws Exception {
|
||||
String encodedUuidString = AMQPMessageIdHelper.AMQP_UUID_PREFIX + UUID.randomUUID().toString();
|
||||
String stringId = AMQPMessageIdHelper.AMQP_STRING_PREFIX + encodedUuidString;
|
||||
|
||||
Object idObject = messageIdHelper.toIdObject(stringId);
|
||||
assertNotNull("null object should not have been returned", idObject);
|
||||
assertEquals("expected id object was not returned", encodedUuidString, idObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toIdObject(String)} throws an
|
||||
* {@link IdConversionException} when presented with an encoded binary hex string of uneven
|
||||
* length (after the prefix) that thus can't be converted due to each byte using 2 characters
|
||||
*/
|
||||
@Test
|
||||
public void testToIdObjectWithStringContainingBinaryHexThrowsWithUnevenLengthString() {
|
||||
String unevenHead = AMQPMessageIdHelper.AMQP_BINARY_PREFIX + "123";
|
||||
|
||||
try {
|
||||
messageIdHelper.toIdObject(unevenHead);
|
||||
fail("expected exception was not thrown");
|
||||
} catch (ActiveMQAMQPException ex) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link AMQPMessageIdHelper#toIdObject(String)} throws an
|
||||
* {@link IdConversionException} when presented with an encoded binary hex string (after the
|
||||
* prefix) that contains characters other than 0-9 and A-F and a-f, and thus can't be
|
||||
* converted
|
||||
*/
|
||||
@Test
|
||||
public void testToIdObjectWithStringContainingBinaryHexThrowsWithNonHexCharacters() {
|
||||
|
||||
// char before '0'
|
||||
char nonHexChar = '/';
|
||||
String nonHexString = AMQPMessageIdHelper.AMQP_BINARY_PREFIX + nonHexChar + nonHexChar;
|
||||
|
||||
try {
|
||||
messageIdHelper.toIdObject(nonHexString);
|
||||
fail("expected exception was not thrown");
|
||||
} catch (ActiveMQAMQPException ex) {
|
||||
// expected
|
||||
}
|
||||
|
||||
// char after '9', before 'A'
|
||||
nonHexChar = ':';
|
||||
nonHexString = AMQPMessageIdHelper.AMQP_BINARY_PREFIX + nonHexChar + nonHexChar;
|
||||
|
||||
try {
|
||||
messageIdHelper.toIdObject(nonHexString);
|
||||
fail("expected exception was not thrown");
|
||||
} catch (ActiveMQAMQPException ex) {
|
||||
// expected
|
||||
}
|
||||
|
||||
// char after 'F', before 'a'
|
||||
nonHexChar = 'G';
|
||||
nonHexString = AMQPMessageIdHelper.AMQP_BINARY_PREFIX + nonHexChar + nonHexChar;
|
||||
|
||||
try {
|
||||
messageIdHelper.toIdObject(nonHexString);
|
||||
fail("expected exception was not thrown");
|
||||
} catch (ActiveMQAMQPException ex) {
|
||||
// expected
|
||||
}
|
||||
|
||||
// char after 'f'
|
||||
nonHexChar = 'g';
|
||||
nonHexString = AMQPMessageIdHelper.AMQP_BINARY_PREFIX + nonHexChar + nonHexChar;
|
||||
|
||||
try {
|
||||
messageIdHelper.toIdObject(nonHexString);
|
||||
fail("expected exception was not thrown");
|
||||
} catch (ActiveMQAMQPException ex) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* 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.protocol.amqp.converter.message;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.qpid.proton.Proton;
|
||||
import org.apache.qpid.proton.amqp.Symbol;
|
||||
import org.apache.qpid.proton.amqp.messaging.MessageAnnotations;
|
||||
import org.apache.qpid.proton.message.Message;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AMQPMessageSupportTest {
|
||||
|
||||
// ---------- getSymbol ---------------------------------------------------//
|
||||
|
||||
@Test
|
||||
public void testGetSymbol() {
|
||||
assertNotNull(AMQPMessageSupport.getSymbol("x-opt-something-or-other"));
|
||||
}
|
||||
|
||||
// ---------- getMessageAnnotation ----------------------------------------//
|
||||
|
||||
@Test
|
||||
public void testGetMessageAnnotationWhenMessageHasAnnotationsMap() {
|
||||
Map<Symbol, Object> messageAnnotationsMap = new HashMap<>();
|
||||
messageAnnotationsMap.put(Symbol.valueOf("x-opt-test"), Boolean.TRUE);
|
||||
Message message = Proton.message();
|
||||
message.setMessageAnnotations(new MessageAnnotations(messageAnnotationsMap));
|
||||
|
||||
assertNotNull(AMQPMessageSupport.getMessageAnnotation("x-opt-test", message));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMessageAnnotationWhenMessageHasEmptyAnnotationsMap() {
|
||||
Map<Symbol, Object> messageAnnotationsMap = new HashMap<>();
|
||||
Message message = Proton.message();
|
||||
message.setMessageAnnotations(new MessageAnnotations(messageAnnotationsMap));
|
||||
|
||||
assertNull(AMQPMessageSupport.getMessageAnnotation("x-opt-test", message));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMessageAnnotationWhenMessageHasNoAnnotationsMap() {
|
||||
Message message = Proton.message();
|
||||
assertNull(AMQPMessageSupport.getMessageAnnotation("x-opt-test", message));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMessageAnnotationWhenMessageIsNull() {
|
||||
assertNull(AMQPMessageSupport.getMessageAnnotation("x-opt-test", null));
|
||||
}
|
||||
|
||||
// ---------- isContentType -----------------------------------------------//
|
||||
|
||||
@Test
|
||||
public void testIsContentTypeWithNullStringValueAndNullMessageContentType() {
|
||||
Message message = Proton.message();
|
||||
assertTrue(AMQPMessageSupport.isContentType(null, message));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsContentTypeWithNonNullStringValueAndNullMessageContentType() {
|
||||
Message message = Proton.message();
|
||||
assertFalse(AMQPMessageSupport.isContentType("test", message));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsContentTypeWithNonNullStringValueAndNonNullMessageContentTypeNotEqual() {
|
||||
Message message = Proton.message();
|
||||
message.setContentType("fails");
|
||||
assertFalse(AMQPMessageSupport.isContentType("test", message));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsContentTypeWithNonNullStringValueAndNonNullMessageContentTypeEqual() {
|
||||
Message message = Proton.message();
|
||||
message.setContentType("test");
|
||||
assertTrue(AMQPMessageSupport.isContentType("test", message));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsContentTypeWithNullStringValueAndNonNullMessageContentType() {
|
||||
Message message = Proton.message();
|
||||
message.setContentType("test");
|
||||
assertFalse(AMQPMessageSupport.isContentType(null, message));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,718 @@
|
|||
/*
|
||||
* 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.protocol.amqp.converter.message;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.jms.Destination;
|
||||
import javax.jms.MapMessage;
|
||||
import javax.jms.Queue;
|
||||
import javax.jms.TemporaryQueue;
|
||||
import javax.jms.TemporaryTopic;
|
||||
import javax.jms.TextMessage;
|
||||
import javax.jms.Topic;
|
||||
|
||||
import org.apache.activemq.artemis.jms.client.ActiveMQMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSBytesMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMapMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSObjectMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSStreamMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSTextMessage;
|
||||
import org.apache.activemq.artemis.utils.IDGenerator;
|
||||
import org.apache.activemq.artemis.utils.SimpleIDGenerator;
|
||||
import org.apache.qpid.proton.Proton;
|
||||
import org.apache.qpid.proton.amqp.Binary;
|
||||
import org.apache.qpid.proton.amqp.Symbol;
|
||||
import org.apache.qpid.proton.amqp.messaging.AmqpSequence;
|
||||
import org.apache.qpid.proton.amqp.messaging.AmqpValue;
|
||||
import org.apache.qpid.proton.amqp.messaging.Data;
|
||||
import org.apache.qpid.proton.amqp.messaging.MessageAnnotations;
|
||||
import org.apache.qpid.proton.message.Message;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class JMSMappingInboundTransformerTest {
|
||||
|
||||
private IDGenerator idGenerator;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.idGenerator = new SimpleIDGenerator(0);
|
||||
}
|
||||
|
||||
// ----- Null Body Section ------------------------------------------------//
|
||||
|
||||
/**
|
||||
* Test that a message with no body section, but with the content type set to
|
||||
* {@value AMQPMessageSupport#OCTET_STREAM_CONTENT_TYPE} results in a BytesMessage
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateBytesMessageFromNoBodySectionAndContentType() throws Exception {
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
|
||||
Message message = Message.Factory.create();
|
||||
message.setContentType(AMQPMessageSupport.OCTET_STREAM_CONTENT_TYPE);
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSBytesMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a message with no body section, and no content-type results in a BytesMessage
|
||||
* when not otherwise annotated to indicate the type of JMS message it is.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateBytesMessageFromNoBodySectionAndNoContentType() throws Exception {
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
|
||||
Message message = Message.Factory.create();
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSBytesMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a message with no body section, but with the content type set to
|
||||
* {@value AMQPMessageSupport#SERIALIZED_JAVA_OBJECT_CONTENT_TYPE} results in an
|
||||
* ObjectMessage when not otherwise annotated to indicate the type of JMS message it is.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateObjectMessageFromNoBodySectionAndContentType() throws Exception {
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
|
||||
Message message = Message.Factory.create();
|
||||
message.setContentType(AMQPMessageSupport.SERIALIZED_JAVA_OBJECT_CONTENT_TYPE);
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSObjectMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTextMessageFromNoBodySectionAndContentType() throws Exception {
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
|
||||
Message message = Message.Factory.create();
|
||||
message.setContentType("text/plain");
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSTextMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a message with no body section, and with the content type set to an unknown
|
||||
* value results in a plain Message when not otherwise annotated to indicate the type of JMS
|
||||
* message it is.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
public void testCreateGenericMessageFromNoBodySectionAndUnknownContentType() throws Exception {
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
|
||||
Message message = Message.Factory.create();
|
||||
message.setContentType("unknown-content-type");
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ActiveMQMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
|
||||
// ----- Data Body Section ------------------------------------------------//
|
||||
|
||||
/**
|
||||
* Test that a data body containing nothing, but with the content type set to
|
||||
* {@value AMQPMessageSupport#OCTET_STREAM_CONTENT_TYPE} results in a BytesMessage when not
|
||||
* otherwise annotated to indicate the type of JMS message it is.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateBytesMessageFromDataWithEmptyBinaryAndContentType() throws Exception {
|
||||
Message message = Proton.message();
|
||||
Binary binary = new Binary(new byte[0]);
|
||||
message.setBody(new Data(binary));
|
||||
message.setContentType(AMQPMessageSupport.OCTET_STREAM_CONTENT_TYPE);
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSBytesMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a message with an empty data body section, and with the content type set to an
|
||||
* unknown value results in a BytesMessage when not otherwise annotated to indicate the type
|
||||
* of JMS message it is.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
public void testCreateBytesMessageFromDataWithUnknownContentType() throws Exception {
|
||||
Message message = Proton.message();
|
||||
Binary binary = new Binary(new byte[0]);
|
||||
message.setBody(new Data(binary));
|
||||
message.setContentType("unknown-content-type");
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSBytesMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a receiving a data body containing nothing and no content type being set results
|
||||
* in a BytesMessage when not otherwise annotated to indicate the type of JMS message it is.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateBytesMessageFromDataWithEmptyBinaryAndNoContentType() throws Exception {
|
||||
Message message = Proton.message();
|
||||
Binary binary = new Binary(new byte[0]);
|
||||
message.setBody(new Data(binary));
|
||||
|
||||
assertNull(message.getContentType());
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSBytesMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that receiving a data body containing nothing, but with the content type set to
|
||||
* {@value AMQPMessageSupport#SERIALIZED_JAVA_OBJECT_CONTENT_TYPE} results in an
|
||||
* ObjectMessage when not otherwise annotated to indicate the type of JMS message it is.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateObjectMessageFromDataWithContentTypeAndEmptyBinary() throws Exception {
|
||||
Message message = Proton.message();
|
||||
Binary binary = new Binary(new byte[0]);
|
||||
message.setBody(new Data(binary));
|
||||
message.setContentType(AMQPMessageSupport.SERIALIZED_JAVA_OBJECT_CONTENT_TYPE);
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSObjectMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTextMessageFromDataWithContentTypeTextPlain() throws Exception {
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("text/plain;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("text/plain;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("text/plain;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("text/plain", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTextMessageFromDataWithContentTypeTextJson() throws Exception {
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("text/json;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("text/json;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("text/json;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("text/json", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTextMessageFromDataWithContentTypeTextHtml() throws Exception {
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("text/html;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("text/html;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("text/html;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("text/html", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTextMessageFromDataWithContentTypeTextFoo() throws Exception {
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("text/foo;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("text/foo;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("text/foo;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("text/foo", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTextMessageFromDataWithContentTypeApplicationJson() throws Exception {
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/json;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/json;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/json;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/json", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTextMessageFromDataWithContentTypeApplicationJsonVariant() throws Exception {
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/something+json;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/something+json;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/something+json;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/something+json", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTextMessageFromDataWithContentTypeApplicationJavascript() throws Exception {
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/javascript;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/javascript;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/javascript;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/javascript", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTextMessageFromDataWithContentTypeApplicationEcmascript() throws Exception {
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/ecmascript;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/ecmascript;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/ecmascript;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/ecmascript", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTextMessageFromDataWithContentTypeApplicationXml() throws Exception {
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/xml;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/xml;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/xml;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/xml", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTextMessageFromDataWithContentTypeApplicationXmlVariant() throws Exception {
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/something+xml;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/something+xml;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/something+xml;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/something+xml", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTextMessageFromDataWithContentTypeApplicationXmlDtd() throws Exception {
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/xml-dtd;charset=iso-8859-1", StandardCharsets.ISO_8859_1);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/xml-dtd;charset=us-ascii", StandardCharsets.US_ASCII);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/xml-dtd;charset=utf-8", StandardCharsets.UTF_8);
|
||||
doCreateTextMessageFromDataWithContentTypeTestImpl("application/xml-dtd", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private void doCreateTextMessageFromDataWithContentTypeTestImpl(String contentType, Charset expectedCharset) throws Exception {
|
||||
Message message = Proton.message();
|
||||
Binary binary = new Binary(new byte[0]);
|
||||
message.setBody(new Data(binary));
|
||||
message.setContentType(contentType);
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
if (StandardCharsets.UTF_8.equals(expectedCharset)) {
|
||||
assertEquals("Unexpected message class type", ServerJMSTextMessage.class, jmsMessage.getClass());
|
||||
} else {
|
||||
assertEquals("Unexpected message class type", ServerJMSBytesMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
// ----- AmqpValue transformations ----------------------------------------//
|
||||
|
||||
/**
|
||||
* Test that an amqp-value body containing a string results in a TextMessage when not
|
||||
* otherwise annotated to indicate the type of JMS message it is.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateTextMessageFromAmqpValueWithString() throws Exception {
|
||||
Message message = Proton.message();
|
||||
message.setBody(new AmqpValue("content"));
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSTextMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an amqp-value body containing a null results in an TextMessage when not
|
||||
* otherwise annotated to indicate the type of JMS message it is.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateTextMessageFromAmqpValueWithNull() throws Exception {
|
||||
Message message = Proton.message();
|
||||
message.setBody(new AmqpValue(null));
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSTextMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a message with an AmqpValue section containing a Binary, but with the content
|
||||
* type set to {@value AMQPMessageSupport#SERIALIZED_JAVA_OBJECT_CONTENT_TYPE} results in an
|
||||
* ObjectMessage when not otherwise annotated to indicate the type of JMS message it is.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateObjectMessageFromAmqpValueWithBinaryAndContentType() throws Exception {
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
|
||||
Message message = Message.Factory.create();
|
||||
message.setBody(new AmqpValue(new Binary(new byte[0])));
|
||||
message.setContentType(AMQPMessageSupport.SERIALIZED_JAVA_OBJECT_CONTENT_TYPE);
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSObjectMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an amqp-value body containing a map results in an MapMessage when not otherwise
|
||||
* annotated to indicate the type of JMS message it is.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateAmqpMapMessageFromAmqpValueWithMap() throws Exception {
|
||||
Message message = Proton.message();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
message.setBody(new AmqpValue(map));
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSMapMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an amqp-value body containing a map that has an AMQP Binary as one of the
|
||||
* entries encoded into the Map results in an MapMessage where a byte array can be read from
|
||||
* the entry.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateAmqpMapMessageFromAmqpValueWithMapContainingBinaryEntry() throws Exception {
|
||||
final String ENTRY_NAME = "bytesEntry";
|
||||
|
||||
Message message = Proton.message();
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
|
||||
byte[] inputBytes = new byte[] {1, 2, 3, 4, 5};
|
||||
map.put(ENTRY_NAME, new Binary(inputBytes));
|
||||
|
||||
message.setBody(new AmqpValue(map));
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSMapMessage.class, jmsMessage.getClass());
|
||||
|
||||
MapMessage mapMessage = (MapMessage) jmsMessage;
|
||||
byte[] outputBytes = mapMessage.getBytes(ENTRY_NAME);
|
||||
assertNotNull(outputBytes);
|
||||
assertTrue(Arrays.equals(inputBytes, outputBytes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an amqp-value body containing a list results in an StreamMessage when not
|
||||
* otherwise annotated to indicate the type of JMS message it is.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateAmqpStreamMessageFromAmqpValueWithList() throws Exception {
|
||||
Message message = Proton.message();
|
||||
List<String> list = new ArrayList<>();
|
||||
message.setBody(new AmqpValue(list));
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSStreamMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an amqp-sequence body containing a list results in an StreamMessage when not
|
||||
* otherwise annotated to indicate the type of JMS message it is.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateAmqpStreamMessageFromAmqpSequence() throws Exception {
|
||||
Message message = Proton.message();
|
||||
List<String> list = new ArrayList<>();
|
||||
message.setBody(new AmqpSequence(list));
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSStreamMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an amqp-value body containing a binary value results in BytesMessage when not
|
||||
* otherwise annotated to indicate the type of JMS message it is.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateAmqpBytesMessageFromAmqpValueWithBinary() throws Exception {
|
||||
Message message = Proton.message();
|
||||
Binary binary = new Binary(new byte[0]);
|
||||
message.setBody(new AmqpValue(binary));
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSBytesMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an amqp-value body containing a value which can't be categorized results in an
|
||||
* exception from the transformer and then try the transformer's own fallback transformer to
|
||||
* result in an BytesMessage.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurs during the test.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateBytesMessageFromAmqpValueWithUncategorisedContent() throws Exception {
|
||||
Message message = Proton.message();
|
||||
message.setBody(new AmqpValue(UUID.randomUUID()));
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertNotNull("Message should not be null", jmsMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSBytesMessage.class, jmsMessage.getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransformMessageWithAmqpValueStringCreatesTextMessage() throws Exception {
|
||||
String contentString = "myTextMessageContent";
|
||||
Message message = Message.Factory.create();
|
||||
message.setBody(new AmqpValue(contentString));
|
||||
|
||||
EncodedMessage em = encodeMessage(message);
|
||||
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
|
||||
assertTrue("Expected TextMessage", jmsMessage instanceof TextMessage);
|
||||
assertEquals("Unexpected message class type", ServerJMSTextMessage.class, jmsMessage.getClass());
|
||||
|
||||
TextMessage textMessage = (TextMessage) jmsMessage;
|
||||
|
||||
assertNotNull(textMessage.getText());
|
||||
assertEquals(contentString, textMessage.getText());
|
||||
}
|
||||
|
||||
// ----- Destination Conversions ------------------------------------------//
|
||||
|
||||
@Test
|
||||
public void testTransformWithNoToTypeDestinationTypeAnnotation() throws Exception {
|
||||
doTransformWithToTypeDestinationTypeAnnotationTestImpl(null, Destination.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransformWithQueueStringToTypeDestinationTypeAnnotation() throws Exception {
|
||||
doTransformWithToTypeDestinationTypeAnnotationTestImpl("queue", Queue.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransformWithTemporaryQueueStringToTypeDestinationTypeAnnotation() throws Exception {
|
||||
doTransformWithToTypeDestinationTypeAnnotationTestImpl("queue,temporary", TemporaryQueue.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransformWithTopicStringToTypeDestinationTypeAnnotation() throws Exception {
|
||||
doTransformWithToTypeDestinationTypeAnnotationTestImpl("topic", Topic.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransformWithTemporaryTopicStringToTypeDestinationTypeAnnotation() throws Exception {
|
||||
doTransformWithToTypeDestinationTypeAnnotationTestImpl("topic,temporary", TemporaryTopic.class);
|
||||
}
|
||||
|
||||
private void doTransformWithToTypeDestinationTypeAnnotationTestImpl(Object toTypeAnnotationValue, Class<? extends Destination> expectedClass)
|
||||
throws Exception {
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
|
||||
String toAddress = "toAddress";
|
||||
Message amqp = Message.Factory.create();
|
||||
amqp.setBody(new AmqpValue("myTextMessageContent"));
|
||||
amqp.setAddress(toAddress);
|
||||
if (toTypeAnnotationValue != null) {
|
||||
Map<Symbol, Object> map = new HashMap<>();
|
||||
map.put(Symbol.valueOf("x-opt-to-type"), toTypeAnnotationValue);
|
||||
MessageAnnotations ma = new MessageAnnotations(map);
|
||||
amqp.setMessageAnnotations(ma);
|
||||
}
|
||||
|
||||
EncodedMessage em = encodeMessage(amqp);
|
||||
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
assertTrue("Expected TextMessage", jmsMessage instanceof TextMessage);
|
||||
}
|
||||
|
||||
// ----- ReplyTo Conversions ----------------------------------------------//
|
||||
|
||||
@Test
|
||||
public void testTransformWithNoReplyToTypeDestinationTypeAnnotation() throws Exception {
|
||||
doTransformWithReplyToTypeDestinationTypeAnnotationTestImpl(null, Destination.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransformWithQueueStringReplyToTypeDestinationTypeAnnotation() throws Exception {
|
||||
doTransformWithReplyToTypeDestinationTypeAnnotationTestImpl("queue", Queue.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransformWithTemporaryQueueStringReplyToTypeDestinationTypeAnnotation() throws Exception {
|
||||
doTransformWithReplyToTypeDestinationTypeAnnotationTestImpl("queue,temporary", TemporaryQueue.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransformWithTopicStringReplyToTypeDestinationTypeAnnotation() throws Exception {
|
||||
doTransformWithReplyToTypeDestinationTypeAnnotationTestImpl("topic", Topic.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransformWithTemporaryTopicStringReplyToTypeDestinationTypeAnnotation() throws Exception {
|
||||
doTransformWithReplyToTypeDestinationTypeAnnotationTestImpl("topic,temporary", TemporaryTopic.class);
|
||||
}
|
||||
|
||||
private void doTransformWithReplyToTypeDestinationTypeAnnotationTestImpl(Object replyToTypeAnnotationValue, Class<? extends Destination> expectedClass)
|
||||
throws Exception {
|
||||
JMSMappingInboundTransformer transformer = new JMSMappingInboundTransformer(idGenerator);
|
||||
|
||||
String replyToAddress = "replyToAddress";
|
||||
Message amqp = Message.Factory.create();
|
||||
amqp.setBody(new AmqpValue("myTextMessageContent"));
|
||||
amqp.setReplyTo(replyToAddress);
|
||||
if (replyToTypeAnnotationValue != null) {
|
||||
Map<Symbol, Object> map = new HashMap<>();
|
||||
map.put(Symbol.valueOf("x-opt-reply-type"), replyToTypeAnnotationValue);
|
||||
MessageAnnotations ma = new MessageAnnotations(map);
|
||||
amqp.setMessageAnnotations(ma);
|
||||
}
|
||||
|
||||
EncodedMessage em = encodeMessage(amqp);
|
||||
|
||||
javax.jms.Message jmsMessage = transformer.transform(em);
|
||||
assertTrue("Expected TextMessage", jmsMessage instanceof TextMessage);
|
||||
}
|
||||
|
||||
// ----- Utility Methods --------------------------------------------------//
|
||||
|
||||
private EncodedMessage encodeMessage(Message message) {
|
||||
byte[] encodeBuffer = new byte[1024 * 8];
|
||||
int encodedSize;
|
||||
while (true) {
|
||||
try {
|
||||
encodedSize = message.encode(encodeBuffer, 0, encodeBuffer.length);
|
||||
break;
|
||||
} catch (java.nio.BufferOverflowException e) {
|
||||
encodeBuffer = new byte[encodeBuffer.length * 2];
|
||||
}
|
||||
}
|
||||
|
||||
long messageFormat = 0;
|
||||
return new EncodedMessage(messageFormat, encodeBuffer, 0, encodedSize);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,952 @@
|
|||
/*
|
||||
* 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.protocol.amqp.converter.message;
|
||||
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_DATA;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_NULL;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_SEQUENCE;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_UNKNOWN;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.AMQP_VALUE_BINARY;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.converter.message.AMQPMessageSupport.JMS_AMQP_ORIGINAL_ENCODING;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.jms.JMSException;
|
||||
|
||||
import org.apache.activemq.artemis.core.buffers.impl.ResetLimitWrappedActiveMQBuffer;
|
||||
import org.apache.activemq.artemis.core.server.impl.ServerMessageImpl;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerDestination;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSBytesMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMapMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSObjectMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSStreamMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSTextMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.util.NettyWritable;
|
||||
import org.apache.activemq.artemis.utils.IDGenerator;
|
||||
import org.apache.activemq.artemis.utils.SimpleIDGenerator;
|
||||
import org.apache.qpid.proton.amqp.Binary;
|
||||
import org.apache.qpid.proton.amqp.Symbol;
|
||||
import org.apache.qpid.proton.amqp.messaging.AmqpSequence;
|
||||
import org.apache.qpid.proton.amqp.messaging.AmqpValue;
|
||||
import org.apache.qpid.proton.amqp.messaging.Data;
|
||||
import org.apache.qpid.proton.amqp.messaging.MessageAnnotations;
|
||||
import org.apache.qpid.proton.message.Message;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
public class JMSMappingOutboundTransformerTest {
|
||||
|
||||
private final UUID TEST_OBJECT_VALUE = UUID.fromString("fee14b62-09e0-4ac6-a4c3-4206c630d844");
|
||||
private final String TEST_ADDRESS = "queue://testAddress";
|
||||
|
||||
private IDGenerator idGenerator;
|
||||
private JMSMappingOutboundTransformer transformer;
|
||||
|
||||
public static final byte QUEUE_TYPE = 0x00;
|
||||
public static final byte TOPIC_TYPE = 0x01;
|
||||
public static final byte TEMP_QUEUE_TYPE = 0x02;
|
||||
public static final byte TEMP_TOPIC_TYPE = 0x03;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
idGenerator = new SimpleIDGenerator(0);
|
||||
transformer = new JMSMappingOutboundTransformer(idGenerator);
|
||||
}
|
||||
|
||||
// ----- no-body Message type tests ---------------------------------------//
|
||||
|
||||
@Test
|
||||
public void testConvertMessageToAmqpMessageWithNoBody() throws Exception {
|
||||
ServerJMSMessage outbound = createMessage();
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNull(amqp.getBody());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertTextMessageToAmqpMessageWithNoBodyOriginalEncodingWasNull() throws Exception {
|
||||
ServerJMSTextMessage outbound = createTextMessage();
|
||||
outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_NULL);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNull(amqp.getBody());
|
||||
}
|
||||
|
||||
// ----- BytesMessage type tests ---------------------------------------//
|
||||
|
||||
@Test
|
||||
public void testConvertEmptyBytesMessageToAmqpMessageWithDataBody() throws Exception {
|
||||
ServerJMSBytesMessage outbound = createBytesMessage();
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof Data);
|
||||
assertTrue(((Data) amqp.getBody()).getValue() instanceof Binary);
|
||||
assertEquals(0, ((Data) amqp.getBody()).getValue().getLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertUncompressedBytesMessageToAmqpMessageWithDataBody() throws Exception {
|
||||
byte[] expectedPayload = new byte[] {8, 16, 24, 32};
|
||||
ServerJMSBytesMessage outbound = createBytesMessage();
|
||||
outbound.writeBytes(expectedPayload);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof Data);
|
||||
assertTrue(((Data) amqp.getBody()).getValue() instanceof Binary);
|
||||
assertEquals(4, ((Data) amqp.getBody()).getValue().getLength());
|
||||
|
||||
Binary amqpData = ((Data) amqp.getBody()).getValue();
|
||||
Binary inputData = new Binary(expectedPayload);
|
||||
|
||||
assertTrue(inputData.equals(amqpData));
|
||||
}
|
||||
|
||||
@Ignore("Compressed message body support not yet implemented.")
|
||||
@Test
|
||||
public void testConvertCompressedBytesMessageToAmqpMessageWithDataBody() throws Exception {
|
||||
byte[] expectedPayload = new byte[] {8, 16, 24, 32};
|
||||
ServerJMSBytesMessage outbound = createBytesMessage(true);
|
||||
outbound.writeBytes(expectedPayload);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof Data);
|
||||
assertTrue(((Data) amqp.getBody()).getValue() instanceof Binary);
|
||||
assertEquals(4, ((Data) amqp.getBody()).getValue().getLength());
|
||||
|
||||
Binary amqpData = ((Data) amqp.getBody()).getValue();
|
||||
Binary inputData = new Binary(expectedPayload);
|
||||
|
||||
assertTrue(inputData.equals(amqpData));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertEmptyBytesMessageToAmqpMessageWithAmqpValueBody() throws Exception {
|
||||
ServerJMSBytesMessage outbound = createBytesMessage();
|
||||
outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_BINARY);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof AmqpValue);
|
||||
assertTrue(((AmqpValue) amqp.getBody()).getValue() instanceof Binary);
|
||||
assertEquals(0, ((Binary) ((AmqpValue) amqp.getBody()).getValue()).getLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertUncompressedBytesMessageToAmqpMessageWithAmqpValueBody() throws Exception {
|
||||
byte[] expectedPayload = new byte[] {8, 16, 24, 32};
|
||||
ServerJMSBytesMessage outbound = createBytesMessage();
|
||||
outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_BINARY);
|
||||
outbound.writeBytes(expectedPayload);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof AmqpValue);
|
||||
assertTrue(((AmqpValue) amqp.getBody()).getValue() instanceof Binary);
|
||||
assertEquals(4, ((Binary) ((AmqpValue) amqp.getBody()).getValue()).getLength());
|
||||
|
||||
Binary amqpData = (Binary) ((AmqpValue) amqp.getBody()).getValue();
|
||||
Binary inputData = new Binary(expectedPayload);
|
||||
|
||||
assertTrue(inputData.equals(amqpData));
|
||||
}
|
||||
|
||||
@Ignore("Compressed message body support not yet implemented.")
|
||||
@Test
|
||||
public void testConvertCompressedBytesMessageToAmqpMessageWithAmqpValueBody() throws Exception {
|
||||
byte[] expectedPayload = new byte[] {8, 16, 24, 32};
|
||||
ServerJMSBytesMessage outbound = createBytesMessage(true);
|
||||
outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_BINARY);
|
||||
outbound.writeBytes(expectedPayload);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof AmqpValue);
|
||||
assertTrue(((AmqpValue) amqp.getBody()).getValue() instanceof Binary);
|
||||
assertEquals(4, ((Binary) ((AmqpValue) amqp.getBody()).getValue()).getLength());
|
||||
|
||||
Binary amqpData = (Binary) ((AmqpValue) amqp.getBody()).getValue();
|
||||
Binary inputData = new Binary(expectedPayload);
|
||||
|
||||
assertTrue(inputData.equals(amqpData));
|
||||
}
|
||||
|
||||
// ----- MapMessage type tests --------------------------------------------//
|
||||
|
||||
@Test
|
||||
public void testConvertMapMessageToAmqpMessageWithNoBody() throws Exception {
|
||||
ServerJMSMapMessage outbound = createMapMessage();
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof AmqpValue);
|
||||
assertTrue(((AmqpValue) amqp.getBody()).getValue() instanceof Map);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertMapMessageToAmqpMessageWithByteArrayValueInBody() throws Exception {
|
||||
final byte[] byteArray = new byte[] {1, 2, 3, 4, 5};
|
||||
|
||||
ServerJMSMapMessage outbound = createMapMessage();
|
||||
outbound.setBytes("bytes", byteArray);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof AmqpValue);
|
||||
assertTrue(((AmqpValue) amqp.getBody()).getValue() instanceof Map);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<Object, Object> amqpMap = (Map<Object, Object>) ((AmqpValue) amqp.getBody()).getValue();
|
||||
|
||||
assertEquals(1, amqpMap.size());
|
||||
Binary readByteArray = (Binary) amqpMap.get("bytes");
|
||||
assertNotNull(readByteArray);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertMapMessageToAmqpMessage() throws Exception {
|
||||
ServerJMSMapMessage outbound = createMapMessage();
|
||||
outbound.setString("property-1", "string");
|
||||
outbound.setInt("property-2", 1);
|
||||
outbound.setBoolean("property-3", true);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof AmqpValue);
|
||||
assertTrue(((AmqpValue) amqp.getBody()).getValue() instanceof Map);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<Object, Object> amqpMap = (Map<Object, Object>) ((AmqpValue) amqp.getBody()).getValue();
|
||||
|
||||
assertEquals(3, amqpMap.size());
|
||||
assertTrue("string".equals(amqpMap.get("property-1")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertCompressedMapMessageToAmqpMessage() throws Exception {
|
||||
ServerJMSMapMessage outbound = createMapMessage(true);
|
||||
outbound.setString("property-1", "string");
|
||||
outbound.setInt("property-2", 1);
|
||||
outbound.setBoolean("property-3", true);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof AmqpValue);
|
||||
assertTrue(((AmqpValue) amqp.getBody()).getValue() instanceof Map);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<Object, Object> amqpMap = (Map<Object, Object>) ((AmqpValue) amqp.getBody()).getValue();
|
||||
|
||||
assertEquals(3, amqpMap.size());
|
||||
assertTrue("string".equals(amqpMap.get("property-1")));
|
||||
}
|
||||
|
||||
// ----- StreamMessage type tests -----------------------------------------//
|
||||
|
||||
@Test
|
||||
public void testConvertStreamMessageToAmqpMessageWithAmqpValueBody() throws Exception {
|
||||
ServerJMSStreamMessage outbound = createStreamMessage();
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof AmqpValue);
|
||||
assertTrue(((AmqpValue) amqp.getBody()).getValue() instanceof List);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertStreamMessageToAmqpMessageWithAmqpSequencey() throws Exception {
|
||||
ServerJMSStreamMessage outbound = createStreamMessage();
|
||||
outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_SEQUENCE);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof AmqpSequence);
|
||||
assertTrue(((AmqpSequence) amqp.getBody()).getValue() instanceof List);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertCompressedStreamMessageToAmqpMessageWithAmqpValueBody() throws Exception {
|
||||
ServerJMSStreamMessage outbound = createStreamMessage(true);
|
||||
outbound.writeBoolean(false);
|
||||
outbound.writeString("test");
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof AmqpValue);
|
||||
assertTrue(((AmqpValue) amqp.getBody()).getValue() instanceof List);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object> amqpList = (List<Object>) ((AmqpValue) amqp.getBody()).getValue();
|
||||
|
||||
assertEquals(2, amqpList.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertCompressedStreamMessageToAmqpMessageWithAmqpSequencey() throws Exception {
|
||||
ServerJMSStreamMessage outbound = createStreamMessage(true);
|
||||
outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_SEQUENCE);
|
||||
outbound.writeBoolean(false);
|
||||
outbound.writeString("test");
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof AmqpSequence);
|
||||
assertTrue(((AmqpSequence) amqp.getBody()).getValue() instanceof List);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object> amqpList = ((AmqpSequence) amqp.getBody()).getValue();
|
||||
|
||||
assertEquals(2, amqpList.size());
|
||||
}
|
||||
|
||||
// ----- ObjectMessage type tests -----------------------------------------//
|
||||
|
||||
@Test
|
||||
public void testConvertEmptyObjectMessageToAmqpMessageWithDataBody() throws Exception {
|
||||
ServerJMSObjectMessage outbound = createObjectMessage();
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof Data);
|
||||
assertEquals(5, ((Data) amqp.getBody()).getValue().getLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertEmptyObjectMessageToAmqpMessageUnknownEncodingGetsDataSection() throws Exception {
|
||||
ServerJMSObjectMessage outbound = createObjectMessage();
|
||||
outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_UNKNOWN);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof Data);
|
||||
assertEquals(5, ((Data) amqp.getBody()).getValue().getLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertEmptyObjectMessageToAmqpMessageWithAmqpValueBody() throws Exception {
|
||||
ServerJMSObjectMessage outbound = createObjectMessage();
|
||||
outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_BINARY);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof AmqpValue);
|
||||
assertTrue(((AmqpValue) amqp.getBody()).getValue() instanceof Binary);
|
||||
assertEquals(5, ((Binary) ((AmqpValue) amqp.getBody()).getValue()).getLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertObjectMessageToAmqpMessageWithDataBody() throws Exception {
|
||||
ServerJMSObjectMessage outbound = createObjectMessage(TEST_OBJECT_VALUE);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof Data);
|
||||
assertFalse(0 == ((Data) amqp.getBody()).getValue().getLength());
|
||||
|
||||
Object value = deserialize(((Data) amqp.getBody()).getValue().getArray());
|
||||
assertNotNull(value);
|
||||
assertTrue(value instanceof UUID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertObjectMessageToAmqpMessageUnknownEncodingGetsDataSection() throws Exception {
|
||||
ServerJMSObjectMessage outbound = createObjectMessage(TEST_OBJECT_VALUE);
|
||||
outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_UNKNOWN);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof Data);
|
||||
assertFalse(0 == ((Data) amqp.getBody()).getValue().getLength());
|
||||
|
||||
Object value = deserialize(((Data) amqp.getBody()).getValue().getArray());
|
||||
assertNotNull(value);
|
||||
assertTrue(value instanceof UUID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertObjectMessageToAmqpMessageWithAmqpValueBody() throws Exception {
|
||||
ServerJMSObjectMessage outbound = createObjectMessage(TEST_OBJECT_VALUE);
|
||||
outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_BINARY);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof AmqpValue);
|
||||
assertTrue(((AmqpValue) amqp.getBody()).getValue() instanceof Binary);
|
||||
assertFalse(0 == ((Binary) ((AmqpValue) amqp.getBody()).getValue()).getLength());
|
||||
|
||||
Object value = deserialize(((Binary) ((AmqpValue) amqp.getBody()).getValue()).getArray());
|
||||
assertNotNull(value);
|
||||
assertTrue(value instanceof UUID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertCompressedObjectMessageToAmqpMessageWithDataBody() throws Exception {
|
||||
ServerJMSObjectMessage outbound = createObjectMessage(TEST_OBJECT_VALUE, true);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof Data);
|
||||
assertFalse(0 == ((Data) amqp.getBody()).getValue().getLength());
|
||||
|
||||
Object value = deserialize(((Data) amqp.getBody()).getValue().getArray());
|
||||
assertNotNull(value);
|
||||
assertTrue(value instanceof UUID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertCompressedObjectMessageToAmqpMessageUnknownEncodingGetsDataSection() throws Exception {
|
||||
ServerJMSObjectMessage outbound = createObjectMessage(TEST_OBJECT_VALUE, true);
|
||||
outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_UNKNOWN);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof Data);
|
||||
assertFalse(0 == ((Data) amqp.getBody()).getValue().getLength());
|
||||
|
||||
Object value = deserialize(((Data) amqp.getBody()).getValue().getArray());
|
||||
assertNotNull(value);
|
||||
assertTrue(value instanceof UUID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertCompressedObjectMessageToAmqpMessageWithAmqpValueBody() throws Exception {
|
||||
ServerJMSObjectMessage outbound = createObjectMessage(TEST_OBJECT_VALUE, true);
|
||||
outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_BINARY);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof AmqpValue);
|
||||
assertTrue(((AmqpValue) amqp.getBody()).getValue() instanceof Binary);
|
||||
assertFalse(0 == ((Binary) ((AmqpValue) amqp.getBody()).getValue()).getLength());
|
||||
|
||||
Object value = deserialize(((Binary) ((AmqpValue) amqp.getBody()).getValue()).getArray());
|
||||
assertNotNull(value);
|
||||
assertTrue(value instanceof UUID);
|
||||
}
|
||||
|
||||
// ----- TextMessage type tests -------------------------------------------//
|
||||
|
||||
@Test
|
||||
public void testConvertTextMessageToAmqpMessageWithNoBody() throws Exception {
|
||||
ServerJMSTextMessage outbound = createTextMessage();
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof AmqpValue);
|
||||
assertNull(((AmqpValue) amqp.getBody()).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertTextMessageCreatesBodyUsingOriginalEncodingWithDataSection() throws Exception {
|
||||
String contentString = "myTextMessageContent";
|
||||
ServerJMSTextMessage outbound = createTextMessage(contentString);
|
||||
outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_DATA);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof Data);
|
||||
assertTrue(((Data) amqp.getBody()).getValue() instanceof Binary);
|
||||
|
||||
Binary data = ((Data) amqp.getBody()).getValue();
|
||||
String contents = new String(data.getArray(), data.getArrayOffset(), data.getLength(), StandardCharsets.UTF_8);
|
||||
assertEquals(contentString, contents);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertTextMessageContentNotStoredCreatesBodyUsingOriginalEncodingWithDataSection() throws Exception {
|
||||
String contentString = "myTextMessageContent";
|
||||
ServerJMSTextMessage outbound = createTextMessage(contentString);
|
||||
outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_DATA);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof Data);
|
||||
assertTrue(((Data) amqp.getBody()).getValue() instanceof Binary);
|
||||
|
||||
Binary data = ((Data) amqp.getBody()).getValue();
|
||||
String contents = new String(data.getArray(), data.getArrayOffset(), data.getLength(), StandardCharsets.UTF_8);
|
||||
assertEquals(contentString, contents);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertTextMessageCreatesAmqpValueStringBody() throws Exception {
|
||||
String contentString = "myTextMessageContent";
|
||||
ServerJMSTextMessage outbound = createTextMessage(contentString);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof AmqpValue);
|
||||
assertEquals(contentString, ((AmqpValue) amqp.getBody()).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertTextMessageContentNotStoredCreatesAmqpValueStringBody() throws Exception {
|
||||
String contentString = "myTextMessageContent";
|
||||
ServerJMSTextMessage outbound = createTextMessage(contentString);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof AmqpValue);
|
||||
assertEquals(contentString, ((AmqpValue) amqp.getBody()).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertCompressedTextMessageCreatesDataSectionBody() throws Exception {
|
||||
String contentString = "myTextMessageContent";
|
||||
ServerJMSTextMessage outbound = createTextMessage(contentString, true);
|
||||
outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_DATA);
|
||||
outbound.encode();
|
||||
|
||||
EncodedMessage encoded = transform(outbound);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
assertNotNull(amqp.getBody());
|
||||
assertTrue(amqp.getBody() instanceof Data);
|
||||
assertTrue(((Data) amqp.getBody()).getValue() instanceof Binary);
|
||||
|
||||
Binary data = ((Data) amqp.getBody()).getValue();
|
||||
String contents = new String(data.getArray(), data.getArrayOffset(), data.getLength(), StandardCharsets.UTF_8);
|
||||
assertEquals(contentString, contents);
|
||||
}
|
||||
|
||||
// ----- Test JMSDestination Handling -------------------------------------//
|
||||
|
||||
@Test
|
||||
public void testConvertMessageWithJMSDestinationNull() throws Exception {
|
||||
doTestConvertMessageWithJMSDestination(null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertMessageWithJMSDestinationQueue() throws Exception {
|
||||
doTestConvertMessageWithJMSDestination(createDestination(QUEUE_TYPE), QUEUE_TYPE);
|
||||
}
|
||||
|
||||
@Ignore("Artemis code doesn't provide a means of supplying a typed destination to AMQP")
|
||||
@Test
|
||||
public void testConvertMessageWithJMSDestinationTemporaryQueue() throws Exception {
|
||||
doTestConvertMessageWithJMSDestination(createDestination(TEMP_QUEUE_TYPE), TEMP_QUEUE_TYPE);
|
||||
}
|
||||
|
||||
@Ignore("Artemis code doesn't provide a means of supplying a typed destination to AMQP")
|
||||
@Test
|
||||
public void testConvertMessageWithJMSDestinationTopic() throws Exception {
|
||||
doTestConvertMessageWithJMSDestination(createDestination(TOPIC_TYPE), TOPIC_TYPE);
|
||||
}
|
||||
|
||||
@Ignore("Artemis code doesn't provide a means of supplying a typed destination to AMQP")
|
||||
@Test
|
||||
public void testConvertMessageWithJMSDestinationTemporaryTopic() throws Exception {
|
||||
doTestConvertMessageWithJMSDestination(createDestination(TEMP_TOPIC_TYPE), TEMP_TOPIC_TYPE);
|
||||
}
|
||||
|
||||
private void doTestConvertMessageWithJMSDestination(ServerDestination jmsDestination, Object expectedAnnotationValue) throws Exception {
|
||||
ServerJMSTextMessage textMessage = createTextMessage();
|
||||
textMessage.setText("myTextMessageContent");
|
||||
textMessage.setJMSDestination(jmsDestination);
|
||||
|
||||
EncodedMessage encoded = transform(textMessage);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
MessageAnnotations ma = amqp.getMessageAnnotations();
|
||||
Map<Symbol, Object> maMap = ma == null ? null : ma.getValue();
|
||||
if (maMap != null) {
|
||||
Object actualValue = maMap.get(JMSMappingOutboundTransformer.JMS_DEST_TYPE_MSG_ANNOTATION);
|
||||
assertEquals("Unexpected annotation value", expectedAnnotationValue, actualValue);
|
||||
} else if (expectedAnnotationValue != null) {
|
||||
fail("Expected annotation value, but there were no annotations");
|
||||
}
|
||||
|
||||
if (jmsDestination != null) {
|
||||
assertEquals("Unexpected 'to' address", jmsDestination.getAddress(), amqp.getAddress());
|
||||
}
|
||||
}
|
||||
|
||||
// ----- Test JMSReplyTo Handling -----------------------------------------//
|
||||
|
||||
@Test
|
||||
public void testConvertMessageWithJMSReplyToNull() throws Exception {
|
||||
doTestConvertMessageWithJMSReplyTo(null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertMessageWithJMSReplyToQueue() throws Exception {
|
||||
doTestConvertMessageWithJMSReplyTo(createDestination(QUEUE_TYPE), QUEUE_TYPE);
|
||||
}
|
||||
|
||||
@Ignore("Artemis code doesn't provide a means of supplying a typed destination to AMQP")
|
||||
@Test
|
||||
public void testConvertMessageWithJMSReplyToTemporaryQueue() throws Exception {
|
||||
doTestConvertMessageWithJMSReplyTo(createDestination(TEMP_QUEUE_TYPE), TEMP_QUEUE_TYPE);
|
||||
}
|
||||
|
||||
@Ignore("Artemis code doesn't provide a means of supplying a typed destination to AMQP")
|
||||
@Test
|
||||
public void testConvertMessageWithJMSReplyToTopic() throws Exception {
|
||||
doTestConvertMessageWithJMSReplyTo(createDestination(TOPIC_TYPE), TOPIC_TYPE);
|
||||
}
|
||||
|
||||
@Ignore("Artemis code doesn't provide a means of supplying a typed destination to AMQP")
|
||||
@Test
|
||||
public void testConvertMessageWithJMSReplyToTemporaryTopic() throws Exception {
|
||||
doTestConvertMessageWithJMSReplyTo(createDestination(TEMP_TOPIC_TYPE), TEMP_TOPIC_TYPE);
|
||||
}
|
||||
|
||||
private void doTestConvertMessageWithJMSReplyTo(ServerDestination jmsReplyTo, Object expectedAnnotationValue) throws Exception {
|
||||
ServerJMSTextMessage textMessage = createTextMessage();
|
||||
textMessage.setText("myTextMessageContent");
|
||||
textMessage.setJMSReplyTo(jmsReplyTo);
|
||||
|
||||
EncodedMessage encoded = transform(textMessage);
|
||||
assertNotNull(encoded);
|
||||
|
||||
Message amqp = encoded.decode();
|
||||
|
||||
MessageAnnotations ma = amqp.getMessageAnnotations();
|
||||
Map<Symbol, Object> maMap = ma == null ? null : ma.getValue();
|
||||
if (maMap != null) {
|
||||
Object actualValue = maMap.get(JMSMappingOutboundTransformer.JMS_REPLY_TO_TYPE_MSG_ANNOTATION);
|
||||
assertEquals("Unexpected annotation value", expectedAnnotationValue, actualValue);
|
||||
} else if (expectedAnnotationValue != null) {
|
||||
fail("Expected annotation value, but there were no annotations");
|
||||
}
|
||||
|
||||
if (jmsReplyTo != null) {
|
||||
assertEquals("Unexpected 'reply-to' address", jmsReplyTo.getAddress(), amqp.getReplyTo());
|
||||
}
|
||||
}
|
||||
|
||||
// ----- Utility Methods used for this Test -------------------------------//
|
||||
|
||||
public EncodedMessage transform(ServerJMSMessage message) throws Exception {
|
||||
// Useful for testing but not recommended for real life use.
|
||||
ByteBuf nettyBuffer = Unpooled.buffer(1024);
|
||||
NettyWritable buffer = new NettyWritable(nettyBuffer);
|
||||
|
||||
long messageFormat = transformer.transform(message, buffer);
|
||||
|
||||
EncodedMessage encoded = new EncodedMessage(messageFormat, nettyBuffer.array(), nettyBuffer.arrayOffset() + nettyBuffer.readerIndex(), nettyBuffer.readableBytes());
|
||||
|
||||
return encoded;
|
||||
}
|
||||
|
||||
private ServerDestination createDestination(byte destType) {
|
||||
ServerDestination destination = null;
|
||||
switch (destType) {
|
||||
case QUEUE_TYPE:
|
||||
destination = new ServerDestination(TEST_ADDRESS);
|
||||
break;
|
||||
case TOPIC_TYPE:
|
||||
destination = new ServerDestination(TEST_ADDRESS);
|
||||
break;
|
||||
case TEMP_QUEUE_TYPE:
|
||||
destination = new ServerDestination(TEST_ADDRESS);
|
||||
break;
|
||||
case TEMP_TOPIC_TYPE:
|
||||
destination = new ServerDestination(TEST_ADDRESS);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invliad Destination Type given/");
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
private ServerJMSMessage createMessage() {
|
||||
return new ServerJMSMessage(newMessage(org.apache.activemq.artemis.api.core.Message.DEFAULT_TYPE), 0);
|
||||
}
|
||||
|
||||
private ServerJMSBytesMessage createBytesMessage() {
|
||||
return createBytesMessage(false);
|
||||
}
|
||||
|
||||
private ServerJMSBytesMessage createBytesMessage(boolean compression) {
|
||||
ServerJMSBytesMessage message = new ServerJMSBytesMessage(newMessage(org.apache.activemq.artemis.api.core.Message.BYTES_TYPE), 0);
|
||||
|
||||
if (compression) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private ServerJMSMapMessage createMapMessage() {
|
||||
return createMapMessage(false);
|
||||
}
|
||||
|
||||
private ServerJMSMapMessage createMapMessage(boolean compression) {
|
||||
ServerJMSMapMessage message = new ServerJMSMapMessage(newMessage(org.apache.activemq.artemis.api.core.Message.MAP_TYPE), 0);
|
||||
|
||||
if (compression) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private ServerJMSStreamMessage createStreamMessage() {
|
||||
return createStreamMessage(false);
|
||||
}
|
||||
|
||||
private ServerJMSStreamMessage createStreamMessage(boolean compression) {
|
||||
ServerJMSStreamMessage message = new ServerJMSStreamMessage(newMessage(org.apache.activemq.artemis.api.core.Message.STREAM_TYPE), 0);
|
||||
|
||||
if (compression) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private ServerJMSObjectMessage createObjectMessage() {
|
||||
return createObjectMessage(null);
|
||||
}
|
||||
|
||||
private ServerJMSObjectMessage createObjectMessage(Serializable payload) {
|
||||
return createObjectMessage(payload, false);
|
||||
}
|
||||
|
||||
private ServerJMSObjectMessage createObjectMessage(Serializable payload, boolean compression) {
|
||||
ServerJMSObjectMessage result = AMQPMessageSupport.createObjectMessage(idGenerator);
|
||||
|
||||
if (compression) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos);) {
|
||||
|
||||
oos.writeObject(payload);
|
||||
byte[] data = baos.toByteArray();
|
||||
result.setSerializedForm(new Binary(data));
|
||||
} catch (Exception ex) {
|
||||
throw new AssertionError("Should not fail to setObject in this test");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private ServerJMSTextMessage createTextMessage() {
|
||||
return createTextMessage(null);
|
||||
}
|
||||
|
||||
private ServerJMSTextMessage createTextMessage(String text) {
|
||||
return createTextMessage(text, false);
|
||||
}
|
||||
|
||||
private ServerJMSTextMessage createTextMessage(String text, boolean compression) {
|
||||
ServerJMSTextMessage result = AMQPMessageSupport.createTextMessage(idGenerator);
|
||||
|
||||
if (compression) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
try {
|
||||
result.setText(text);
|
||||
} catch (JMSException e) {
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object deserialize(byte[] payload) throws Exception {
|
||||
try (ByteArrayInputStream bis = new ByteArrayInputStream(payload); ObjectInputStream ois = new ObjectInputStream(bis);) {
|
||||
|
||||
return ois.readObject();
|
||||
}
|
||||
}
|
||||
|
||||
private ServerMessageImpl newMessage(byte messageType) {
|
||||
ServerMessageImpl message = new ServerMessageImpl(idGenerator.generateID(), 512);
|
||||
message.setType(messageType);
|
||||
((ResetLimitWrappedActiveMQBuffer) message.getBodyBuffer()).setMessage(null);
|
||||
return message;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,300 @@
|
|||
/*
|
||||
* 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.protocol.amqp.converter.message;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.activemq.artemis.core.server.ServerMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.ProtonMessageConverter;
|
||||
import org.apache.activemq.artemis.protocol.amqp.util.NettyWritable;
|
||||
import org.apache.activemq.artemis.utils.IDGenerator;
|
||||
import org.apache.activemq.artemis.utils.SimpleIDGenerator;
|
||||
import org.apache.qpid.proton.Proton;
|
||||
import org.apache.qpid.proton.amqp.Symbol;
|
||||
import org.apache.qpid.proton.amqp.messaging.AmqpValue;
|
||||
import org.apache.qpid.proton.amqp.messaging.ApplicationProperties;
|
||||
import org.apache.qpid.proton.amqp.messaging.MessageAnnotations;
|
||||
import org.apache.qpid.proton.message.Message;
|
||||
import org.apache.qpid.proton.message.ProtonJMessage;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
/**
|
||||
* Some simple performance tests for the Message Transformers.
|
||||
*/
|
||||
@Ignore("Useful for profiling but slow and not meant as a unit test")
|
||||
public class JMSTransformationSpeedComparisonTest {
|
||||
|
||||
@Rule
|
||||
public TestName test = new TestName();
|
||||
|
||||
private IDGenerator idGenerator;
|
||||
private ProtonMessageConverter converter;
|
||||
|
||||
private final int WARM_CYCLES = 1000;
|
||||
private final int PROFILE_CYCLES = 1000000;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
idGenerator = new SimpleIDGenerator(0);
|
||||
converter = new ProtonMessageConverter(idGenerator);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBodyOnlyMessage() throws Exception {
|
||||
|
||||
Message message = Proton.message();
|
||||
message.setBody(new AmqpValue("String payload for AMQP message conversion performance testing."));
|
||||
EncodedMessage encoded = encode(message);
|
||||
|
||||
// Warm up
|
||||
for (int i = 0; i < WARM_CYCLES; ++i) {
|
||||
ServerMessage intermediate = converter.inbound(encoded);
|
||||
encode(converter.outbound(intermediate, 1));
|
||||
}
|
||||
|
||||
long totalDuration = 0;
|
||||
|
||||
long startTime = System.nanoTime();
|
||||
for (int i = 0; i < PROFILE_CYCLES; ++i) {
|
||||
ServerMessage intermediate = converter.inbound(encoded);
|
||||
encode(converter.outbound(intermediate, 1));
|
||||
}
|
||||
totalDuration += System.nanoTime() - startTime;
|
||||
|
||||
LOG_RESULTS(totalDuration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageWithNoPropertiesOrAnnotations() throws Exception {
|
||||
|
||||
Message message = Proton.message();
|
||||
|
||||
message.setAddress("queue://test-queue");
|
||||
message.setDeliveryCount(1);
|
||||
message.setCreationTime(System.currentTimeMillis());
|
||||
message.setContentType("text/plain");
|
||||
message.setBody(new AmqpValue("String payload for AMQP message conversion performance testing."));
|
||||
|
||||
EncodedMessage encoded = encode(message);
|
||||
|
||||
// Warm up
|
||||
for (int i = 0; i < WARM_CYCLES; ++i) {
|
||||
ServerMessage intermediate = converter.inbound(encoded);
|
||||
encode(converter.outbound(intermediate, 1));
|
||||
}
|
||||
|
||||
long totalDuration = 0;
|
||||
|
||||
long startTime = System.nanoTime();
|
||||
for (int i = 0; i < PROFILE_CYCLES; ++i) {
|
||||
ServerMessage intermediate = converter.inbound(encoded);
|
||||
encode(converter.outbound(intermediate, 1));
|
||||
}
|
||||
totalDuration += System.nanoTime() - startTime;
|
||||
|
||||
LOG_RESULTS(totalDuration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTypicalQpidJMSMessage() throws Exception {
|
||||
|
||||
EncodedMessage encoded = encode(createTypicalQpidJMSMessage());
|
||||
|
||||
// Warm up
|
||||
for (int i = 0; i < WARM_CYCLES; ++i) {
|
||||
ServerMessage intermediate = converter.inbound(encoded);
|
||||
encode(converter.outbound(intermediate, 1));
|
||||
}
|
||||
|
||||
long totalDuration = 0;
|
||||
|
||||
long startTime = System.nanoTime();
|
||||
for (int i = 0; i < PROFILE_CYCLES; ++i) {
|
||||
ServerMessage intermediate = converter.inbound(encoded);
|
||||
encode(converter.outbound(intermediate, 1));
|
||||
}
|
||||
totalDuration += System.nanoTime() - startTime;
|
||||
|
||||
LOG_RESULTS(totalDuration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComplexQpidJMSMessage() throws Exception {
|
||||
|
||||
EncodedMessage encoded = encode(createComplexQpidJMSMessage());
|
||||
|
||||
// Warm up
|
||||
for (int i = 0; i < WARM_CYCLES; ++i) {
|
||||
ServerMessage intermediate = converter.inbound(encoded);
|
||||
encode(converter.outbound(intermediate, 1));
|
||||
}
|
||||
|
||||
long totalDuration = 0;
|
||||
|
||||
long startTime = System.nanoTime();
|
||||
for (int i = 0; i < PROFILE_CYCLES; ++i) {
|
||||
ServerMessage intermediate = converter.inbound(encoded);
|
||||
encode(converter.outbound(intermediate, 1));
|
||||
}
|
||||
totalDuration += System.nanoTime() - startTime;
|
||||
|
||||
LOG_RESULTS(totalDuration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTypicalQpidJMSMessageInBoundOnly() throws Exception {
|
||||
|
||||
EncodedMessage encoded = encode(createTypicalQpidJMSMessage());
|
||||
|
||||
// Warm up
|
||||
for (int i = 0; i < WARM_CYCLES; ++i) {
|
||||
converter.inbound(encoded);
|
||||
}
|
||||
|
||||
long totalDuration = 0;
|
||||
|
||||
long startTime = System.nanoTime();
|
||||
for (int i = 0; i < PROFILE_CYCLES; ++i) {
|
||||
converter.inbound(encoded);
|
||||
}
|
||||
|
||||
totalDuration += System.nanoTime() - startTime;
|
||||
|
||||
LOG_RESULTS(totalDuration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTypicalQpidJMSMessageOutBoundOnly() throws Exception {
|
||||
|
||||
EncodedMessage encoded = encode(createTypicalQpidJMSMessage());
|
||||
ServerMessage intermediate = converter.inbound(encoded);
|
||||
|
||||
// Warm up
|
||||
for (int i = 0; i < WARM_CYCLES; ++i) {
|
||||
encode(converter.outbound(intermediate, 1));
|
||||
}
|
||||
|
||||
long totalDuration = 0;
|
||||
|
||||
long startTime = System.nanoTime();
|
||||
for (int i = 0; i < PROFILE_CYCLES; ++i) {
|
||||
encode(converter.outbound(intermediate, 1));
|
||||
}
|
||||
|
||||
totalDuration += System.nanoTime() - startTime;
|
||||
|
||||
LOG_RESULTS(totalDuration);
|
||||
}
|
||||
|
||||
private Message createTypicalQpidJMSMessage() {
|
||||
Map<String, Object> applicationProperties = new HashMap<>();
|
||||
Map<Symbol, Object> messageAnnotations = new HashMap<>();
|
||||
|
||||
applicationProperties.put("property-1", "string");
|
||||
applicationProperties.put("property-2", 512);
|
||||
applicationProperties.put("property-3", true);
|
||||
|
||||
messageAnnotations.put(Symbol.valueOf("x-opt-jms-msg-type"), 0);
|
||||
messageAnnotations.put(Symbol.valueOf("x-opt-jms-dest"), 0);
|
||||
|
||||
Message message = Proton.message();
|
||||
|
||||
message.setAddress("queue://test-queue");
|
||||
message.setDeliveryCount(1);
|
||||
message.setApplicationProperties(new ApplicationProperties(applicationProperties));
|
||||
message.setMessageAnnotations(new MessageAnnotations(messageAnnotations));
|
||||
message.setCreationTime(System.currentTimeMillis());
|
||||
message.setContentType("text/plain");
|
||||
message.setBody(new AmqpValue("String payload for AMQP message conversion performance testing."));
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private Message createComplexQpidJMSMessage() {
|
||||
Map<String, Object> applicationProperties = new HashMap<>();
|
||||
Map<Symbol, Object> messageAnnotations = new HashMap<>();
|
||||
|
||||
applicationProperties.put("property-1", "string-1");
|
||||
applicationProperties.put("property-2", 512);
|
||||
applicationProperties.put("property-3", true);
|
||||
applicationProperties.put("property-4", "string-2");
|
||||
applicationProperties.put("property-5", 512);
|
||||
applicationProperties.put("property-6", true);
|
||||
applicationProperties.put("property-7", "string-3");
|
||||
applicationProperties.put("property-8", 512);
|
||||
applicationProperties.put("property-9", true);
|
||||
|
||||
messageAnnotations.put(Symbol.valueOf("x-opt-jms-msg-type"), 0);
|
||||
messageAnnotations.put(Symbol.valueOf("x-opt-jms-dest"), 0);
|
||||
|
||||
Message message = Proton.message();
|
||||
|
||||
// Header Values
|
||||
message.setPriority((short) 9);
|
||||
message.setDurable(true);
|
||||
message.setDeliveryCount(2);
|
||||
message.setTtl(5000);
|
||||
|
||||
// Properties
|
||||
message.setMessageId("ID:SomeQualifier:0:0:1");
|
||||
message.setGroupId("Group-ID-1");
|
||||
message.setGroupSequence(15);
|
||||
message.setAddress("queue://test-queue");
|
||||
message.setReplyTo("queue://reply-queue");
|
||||
message.setCreationTime(System.currentTimeMillis());
|
||||
message.setContentType("text/plain");
|
||||
message.setCorrelationId("ID:SomeQualifier:0:7:9");
|
||||
message.setUserId("username".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// Application Properties / Message Annotations / Body
|
||||
message.setApplicationProperties(new ApplicationProperties(applicationProperties));
|
||||
message.setMessageAnnotations(new MessageAnnotations(messageAnnotations));
|
||||
message.setBody(new AmqpValue("String payload for AMQP message conversion performance testing."));
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private EncodedMessage encode(Object target) {
|
||||
if (target instanceof ProtonJMessage) {
|
||||
ProtonJMessage amqp = (ProtonJMessage) target;
|
||||
|
||||
ByteBuf nettyBuffer = Unpooled.buffer(1024);
|
||||
amqp.encode(new NettyWritable(nettyBuffer));
|
||||
|
||||
return new EncodedMessage(0, nettyBuffer.array(), nettyBuffer.arrayOffset() + nettyBuffer.readerIndex(), nettyBuffer.readableBytes());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void LOG_RESULTS(long duration) {
|
||||
String result = "[JMS] Total time for " + PROFILE_CYCLES + " cycles of transforms = " + TimeUnit.NANOSECONDS.toMillis(duration) + " ms -> "
|
||||
+ test.getMethodName();
|
||||
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* 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.protocol.amqp.converter.message;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.activemq.artemis.core.server.ServerMessage;
|
||||
import org.apache.activemq.artemis.protocol.amqp.converter.ProtonMessageConverter;
|
||||
import org.apache.activemq.artemis.utils.IDGenerator;
|
||||
import org.apache.activemq.artemis.utils.SimpleIDGenerator;
|
||||
import org.apache.qpid.proton.Proton;
|
||||
import org.apache.qpid.proton.amqp.Symbol;
|
||||
import org.apache.qpid.proton.amqp.messaging.AmqpValue;
|
||||
import org.apache.qpid.proton.amqp.messaging.ApplicationProperties;
|
||||
import org.apache.qpid.proton.amqp.messaging.MessageAnnotations;
|
||||
import org.apache.qpid.proton.amqp.messaging.Section;
|
||||
import org.apache.qpid.proton.codec.CompositeWritableBuffer;
|
||||
import org.apache.qpid.proton.codec.DroppingWritableBuffer;
|
||||
import org.apache.qpid.proton.codec.WritableBuffer;
|
||||
import org.apache.qpid.proton.message.Message;
|
||||
import org.apache.qpid.proton.message.ProtonJMessage;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
/**
|
||||
* Tests some basic encode / decode functionality on the transformers.
|
||||
*/
|
||||
public class MessageTransformationTest {
|
||||
|
||||
@Rule
|
||||
public TestName test = new TestName();
|
||||
|
||||
private IDGenerator idGenerator;
|
||||
private ProtonMessageConverter converter;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
idGenerator = new SimpleIDGenerator(0);
|
||||
converter = new ProtonMessageConverter(idGenerator);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeDecodeFidelity() throws Exception {
|
||||
Map<String, Object> applicationProperties = new HashMap<>();
|
||||
Map<Symbol, Object> messageAnnotations = new HashMap<>();
|
||||
|
||||
applicationProperties.put("property-1", "string");
|
||||
applicationProperties.put("property-2", 512);
|
||||
applicationProperties.put("property-3", true);
|
||||
|
||||
messageAnnotations.put(Symbol.valueOf("x-opt-jms-msg-type"), 0);
|
||||
messageAnnotations.put(Symbol.valueOf("x-opt-jms-dest"), 0);
|
||||
|
||||
Message incomingMessage = Proton.message();
|
||||
|
||||
incomingMessage.setAddress("queue://test-queue");
|
||||
incomingMessage.setDeliveryCount(1);
|
||||
incomingMessage.setApplicationProperties(new ApplicationProperties(applicationProperties));
|
||||
incomingMessage.setMessageAnnotations(new MessageAnnotations(messageAnnotations));
|
||||
incomingMessage.setCreationTime(System.currentTimeMillis());
|
||||
incomingMessage.setContentType("text/plain");
|
||||
incomingMessage.setBody(new AmqpValue("String payload for AMQP message conversion performance testing."));
|
||||
|
||||
EncodedMessage encoded = encode(incomingMessage);
|
||||
|
||||
ServerMessage outbound = converter.inbound(encoded);
|
||||
Message outboudMessage = ((EncodedMessage) converter.outbound(outbound, outbound.getLongProperty("JMSXDeliveryCount").intValue())).decode();
|
||||
|
||||
// Test that message details are equal
|
||||
assertEquals(incomingMessage.getAddress(), outboudMessage.getAddress());
|
||||
assertEquals(incomingMessage.getDeliveryCount(), outboudMessage.getDeliveryCount());
|
||||
assertEquals(incomingMessage.getCreationTime(), outboudMessage.getCreationTime());
|
||||
assertEquals(incomingMessage.getContentType(), outboudMessage.getContentType());
|
||||
|
||||
// Test Message annotations
|
||||
ApplicationProperties incomingApplicationProperties = incomingMessage.getApplicationProperties();
|
||||
ApplicationProperties outgoingApplicationProperties = outboudMessage.getApplicationProperties();
|
||||
|
||||
assertEquals(incomingApplicationProperties.getValue(), outgoingApplicationProperties.getValue());
|
||||
|
||||
// Test Message properties
|
||||
MessageAnnotations incomingMessageAnnotations = incomingMessage.getMessageAnnotations();
|
||||
MessageAnnotations outgoingMessageAnnotations = outboudMessage.getMessageAnnotations();
|
||||
|
||||
assertEquals(incomingMessageAnnotations.getValue(), outgoingMessageAnnotations.getValue());
|
||||
|
||||
// Test that bodies are equal
|
||||
assertTrue(incomingMessage.getBody() instanceof AmqpValue);
|
||||
assertTrue(outboudMessage.getBody() instanceof AmqpValue);
|
||||
|
||||
AmqpValue incomingBody = (AmqpValue) incomingMessage.getBody();
|
||||
AmqpValue outgoingBody = (AmqpValue) outboudMessage.getBody();
|
||||
|
||||
assertTrue(incomingBody.getValue() instanceof String);
|
||||
assertTrue(outgoingBody.getValue() instanceof String);
|
||||
|
||||
assertEquals(incomingBody.getValue(), outgoingBody.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBodyOnlyEncodeDecode() throws Exception {
|
||||
|
||||
Message incomingMessage = Proton.message();
|
||||
|
||||
incomingMessage.setBody(new AmqpValue("String payload for AMQP message conversion performance testing."));
|
||||
|
||||
EncodedMessage encoded = encode(incomingMessage);
|
||||
ServerMessage outbound = converter.inbound(encoded);
|
||||
Message outboudMessage = ((EncodedMessage) converter.outbound(outbound, 1)).decode();
|
||||
|
||||
assertNull(outboudMessage.getHeader());
|
||||
assertNull(outboudMessage.getProperties());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropertiesButNoHeadersEncodeDecode() throws Exception {
|
||||
|
||||
Message incomingMessage = Proton.message();
|
||||
|
||||
incomingMessage.setBody(new AmqpValue("String payload for AMQP message conversion performance testing."));
|
||||
incomingMessage.setMessageId("ID:SomeQualifier:0:0:1");
|
||||
|
||||
EncodedMessage encoded = encode(incomingMessage);
|
||||
ServerMessage outbound = converter.inbound(encoded);
|
||||
Message outboudMessage = ((EncodedMessage) converter.outbound(outbound, 1)).decode();
|
||||
|
||||
assertNull(outboudMessage.getHeader());
|
||||
assertNotNull(outboudMessage.getProperties());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeaderButNoPropertiesEncodeDecode() throws Exception {
|
||||
|
||||
Message incomingMessage = Proton.message();
|
||||
|
||||
incomingMessage.setBody(new AmqpValue("String payload for AMQP message conversion performance testing."));
|
||||
incomingMessage.setDurable(true);
|
||||
|
||||
EncodedMessage encoded = encode(incomingMessage);
|
||||
ServerMessage outbound = converter.inbound(encoded);
|
||||
Message outboudMessage = ((EncodedMessage) converter.outbound(outbound, 1)).decode();
|
||||
|
||||
assertNotNull(outboudMessage.getHeader());
|
||||
assertNull(outboudMessage.getProperties());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageWithAmqpValueThatFailsJMSConversion() throws Exception {
|
||||
|
||||
Message incomingMessage = Proton.message();
|
||||
|
||||
incomingMessage.setBody(new AmqpValue(new Boolean(true)));
|
||||
|
||||
EncodedMessage encoded = encode(incomingMessage);
|
||||
ServerMessage outbound = converter.inbound(encoded);
|
||||
Message outboudMessage = ((EncodedMessage) converter.outbound(outbound, 1)).decode();
|
||||
|
||||
Section section = outboudMessage.getBody();
|
||||
assertNotNull(section);
|
||||
assertTrue(section instanceof AmqpValue);
|
||||
AmqpValue amqpValue = (AmqpValue) section;
|
||||
assertNotNull(amqpValue.getValue());
|
||||
assertTrue(amqpValue.getValue() instanceof Boolean);
|
||||
assertEquals(true, amqpValue.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComplexQpidJMSMessageEncodeDecode() throws Exception {
|
||||
|
||||
Map<String, Object> applicationProperties = new HashMap<>();
|
||||
Map<Symbol, Object> messageAnnotations = new HashMap<>();
|
||||
|
||||
applicationProperties.put("property-1", "string-1");
|
||||
applicationProperties.put("property-2", 512);
|
||||
applicationProperties.put("property-3", true);
|
||||
applicationProperties.put("property-4", "string-2");
|
||||
applicationProperties.put("property-5", 512);
|
||||
applicationProperties.put("property-6", true);
|
||||
applicationProperties.put("property-7", "string-3");
|
||||
applicationProperties.put("property-8", 512);
|
||||
applicationProperties.put("property-9", true);
|
||||
|
||||
messageAnnotations.put(Symbol.valueOf("x-opt-jms-msg-type"), 0);
|
||||
messageAnnotations.put(Symbol.valueOf("x-opt-jms-dest"), 0);
|
||||
messageAnnotations.put(Symbol.valueOf("x-opt-jms-reply-to"), 0);
|
||||
messageAnnotations.put(Symbol.valueOf("x-opt-delivery-delay"), 2000);
|
||||
|
||||
Message message = Proton.message();
|
||||
|
||||
// Header Values
|
||||
message.setPriority((short) 9);
|
||||
message.setDurable(true);
|
||||
message.setDeliveryCount(2);
|
||||
message.setTtl(5000);
|
||||
|
||||
// Properties
|
||||
message.setMessageId("ID:SomeQualifier:0:0:1");
|
||||
message.setGroupId("Group-ID-1");
|
||||
message.setGroupSequence(15);
|
||||
message.setAddress("queue://test-queue");
|
||||
message.setReplyTo("queue://reply-queue");
|
||||
message.setCreationTime(System.currentTimeMillis());
|
||||
message.setContentType("text/plain");
|
||||
message.setCorrelationId("ID:SomeQualifier:0:7:9");
|
||||
message.setUserId("username".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// Application Properties / Message Annotations / Body
|
||||
message.setApplicationProperties(new ApplicationProperties(applicationProperties));
|
||||
message.setMessageAnnotations(new MessageAnnotations(messageAnnotations));
|
||||
message.setBody(new AmqpValue("String payload for AMQP message conversion performance testing."));
|
||||
|
||||
EncodedMessage encoded = encode(message);
|
||||
ServerMessage outbound = converter.inbound(encoded);
|
||||
Message outboudMessage = ((EncodedMessage) converter.outbound(outbound, 1)).decode();
|
||||
|
||||
assertNotNull(outboudMessage.getHeader());
|
||||
assertNotNull(outboudMessage.getProperties());
|
||||
assertNotNull(outboudMessage.getMessageAnnotations());
|
||||
assertNotNull(outboudMessage.getApplicationProperties());
|
||||
assertNull(outboudMessage.getDeliveryAnnotations());
|
||||
assertNull(outboudMessage.getFooter());
|
||||
|
||||
assertEquals(9, outboudMessage.getApplicationProperties().getValue().size());
|
||||
assertEquals(4, outboudMessage.getMessageAnnotations().getValue().size());
|
||||
}
|
||||
|
||||
private EncodedMessage encode(Message message) {
|
||||
ProtonJMessage amqp = (ProtonJMessage) message;
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(new byte[1024 * 4]);
|
||||
final DroppingWritableBuffer overflow = new DroppingWritableBuffer();
|
||||
int c = amqp.encode(new CompositeWritableBuffer(new WritableBuffer.ByteBufferWrapper(buffer), overflow));
|
||||
if (overflow.position() > 0) {
|
||||
buffer = ByteBuffer.wrap(new byte[1024 * 4 + overflow.position()]);
|
||||
c = amqp.encode(new WritableBuffer.ByteBufferWrapper(buffer));
|
||||
}
|
||||
|
||||
return new EncodedMessage(1, buffer.array(), 0, c);
|
||||
}
|
||||
}
|
|
@ -203,6 +203,30 @@ public class AmqpMessage {
|
|||
return message.getProperties().getTo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the replyTo address which is applied to the AMQP message reply-to field in the message properties
|
||||
*
|
||||
* @param address The replyTo address that should be applied in the Message To field.
|
||||
*/
|
||||
public void setReplyToAddress(String address) {
|
||||
checkReadOnly();
|
||||
lazyCreateProperties();
|
||||
getWrappedMessage().setReplyTo(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set replyTo address that was set in the Message To field.
|
||||
*
|
||||
* @return the set replyTo address String form or null if not set.
|
||||
*/
|
||||
public String getReplyToAddress() {
|
||||
if (message.getProperties() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return message.getProperties().getReplyTo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the MessageId property on an outbound message using the provided String
|
||||
*
|
||||
|
|
|
@ -16,6 +16,25 @@
|
|||
*/
|
||||
package org.apache.activemq.artemis.tests.integration.amqp;
|
||||
|
||||
import static org.apache.activemq.artemis.protocol.amqp.proton.AmqpSupport.DELAYED_DELIVERY;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.proton.AmqpSupport.PRODUCT;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.proton.AmqpSupport.VERSION;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.proton.AmqpSupport.contains;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.jms.BytesMessage;
|
||||
import javax.jms.Connection;
|
||||
import javax.jms.ConnectionFactory;
|
||||
|
@ -36,21 +55,9 @@ import javax.jms.StreamMessage;
|
|||
import javax.jms.TemporaryQueue;
|
||||
import javax.jms.TextMessage;
|
||||
import javax.jms.Topic;
|
||||
import javax.jms.TopicPublisher;
|
||||
import javax.jms.TopicSession;
|
||||
import javax.jms.TopicSubscriber;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.activemq.artemis.api.core.SimpleString;
|
||||
import org.apache.activemq.artemis.core.postoffice.Bindings;
|
||||
|
@ -83,11 +90,6 @@ import org.junit.Test;
|
|||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import static org.apache.activemq.artemis.protocol.amqp.proton.AmqpSupport.DELAYED_DELIVERY;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.proton.AmqpSupport.PRODUCT;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.proton.AmqpSupport.VERSION;
|
||||
import static org.apache.activemq.artemis.protocol.amqp.proton.AmqpSupport.contains;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class ProtonTest extends ProtonTestBase {
|
||||
|
||||
|
@ -179,6 +181,31 @@ public class ProtonTest extends ProtonTestBase {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendAndReceiveOnTopic() throws Exception {
|
||||
Connection connection = createConnection("myClientId");
|
||||
try {
|
||||
TopicSession session = (TopicSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
||||
Topic topic = session.createTopic("amqp_testtopic");
|
||||
TopicSubscriber consumer = session.createSubscriber(topic);
|
||||
TopicPublisher producer = session.createPublisher(topic);
|
||||
|
||||
TextMessage message = session.createTextMessage("test-message");
|
||||
producer.send(message);
|
||||
producer.close();
|
||||
|
||||
connection.start();
|
||||
|
||||
message = (TextMessage) consumer.receive(1000);
|
||||
assertNotNull(message);
|
||||
assertNotNull(message.getText());
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDurableSubscriptionUnsubscribe() throws Exception {
|
||||
Connection connection = createConnection("myClientId");
|
||||
|
@ -495,7 +522,7 @@ public class ProtonTest extends ProtonTestBase {
|
|||
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
||||
javax.jms.Queue queue = createQueue(address);
|
||||
MessageProducer p = session.createProducer(queue);
|
||||
ArrayList list = new ArrayList();
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add("aString");
|
||||
ObjectMessage objectMessage = session.createObjectMessage(list);
|
||||
p.send(objectMessage);
|
||||
|
@ -507,7 +534,7 @@ public class ProtonTest extends ProtonTestBase {
|
|||
|
||||
objectMessage = (ObjectMessage) cons.receive(5000);
|
||||
assertNotNull(objectMessage);
|
||||
list = (ArrayList) objectMessage.getObject();
|
||||
list = (ArrayList<String>) objectMessage.getObject();
|
||||
assertEquals(list.get(0), "aString");
|
||||
connection.close();
|
||||
}
|
||||
|
@ -586,7 +613,7 @@ public class ProtonTest extends ProtonTestBase {
|
|||
fillAddress(destinationAddress);
|
||||
|
||||
AmqpClient client = new AmqpClient(new URI(tcpAmqpConnectionUri), userName, password);
|
||||
AmqpConnection amqpConnection = amqpConnection = client.connect();
|
||||
AmqpConnection amqpConnection = client.connect();
|
||||
try {
|
||||
AmqpSession session = amqpConnection.createSession();
|
||||
AmqpSender sender = session.createSender(destinationAddress);
|
||||
|
@ -860,7 +887,7 @@ public class ProtonTest extends ProtonTestBase {
|
|||
AmqpMessage request = new AmqpMessage();
|
||||
request.setApplicationProperty("_AMQ_ResourceName", "core.server");
|
||||
request.setApplicationProperty("_AMQ_OperationName", "getQueueNames");
|
||||
request.setApplicationProperty("JMSReplyTo", destinationAddress);
|
||||
request.setReplyToAddress(destinationAddress);
|
||||
request.setText("[]");
|
||||
|
||||
sender.send(request);
|
||||
|
|
Loading…
Reference in New Issue