/* * 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.Destination; import javax.jms.JMSException; import java.nio.charset.Charset; import java.util.Arrays; import java.util.Map; import java.util.Set; import org.apache.activemq.artemis.core.message.impl.CoreMessage; 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.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; 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; /** * 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 { public static final String JMS_REPLY_TO_TYPE_MSG_ANNOTATION_SYMBOL_NAME = "x-opt-jms-reply-to"; // Message Properties used to map AMQP to JMS and back /** * Attribute used to mark the class type of JMS message that a particular message * instance represents, used internally by the client. */ public static final Symbol JMS_MSG_TYPE = Symbol.getSymbol("x-opt-jms-msg-type"); /** * Attribute used to mark the Application defined delivery time assigned to the message */ public static final Symbol JMS_DELIVERY_TIME = Symbol.getSymbol("x-opt-delivery-time"); /** * Attribute used to mark the Application defined delivery time assigned to the message */ public static final Symbol ROUTING_TYPE = Symbol.getSymbol("x-opt-routing-type"); /** * Value mapping for JMS_MSG_TYPE which indicates the message is a generic JMS Message * which has no body. */ public static final byte JMS_MESSAGE = 0; /** * Value mapping for JMS_MSG_TYPE which indicates the message is a JMS ObjectMessage * which has an Object value serialized in its message body. */ public static final byte JMS_OBJECT_MESSAGE = 1; /** * Value mapping for JMS_MSG_TYPE which indicates the message is a JMS MapMessage * which has an Map instance serialized in its message body. */ public static final byte JMS_MAP_MESSAGE = 2; /** * Value mapping for JMS_MSG_TYPE which indicates the message is a JMS BytesMessage * which has a body that consists of raw bytes. */ public static final byte JMS_BYTES_MESSAGE = 3; /** * Value mapping for JMS_MSG_TYPE which indicates the message is a JMS StreamMessage * which has a body that is a structured collection of primitives values. */ public static final byte JMS_STREAM_MESSAGE = 4; /** * Value mapping for JMS_MSG_TYPE which indicates the message is a JMS TextMessage * which has a body that contains a UTF-8 encoded String. */ public static final byte JMS_TEXT_MESSAGE = 5; /** * Content type used to mark Data sections as containing a serialized java object. */ public static final Symbol SERIALIZED_JAVA_OBJECT_CONTENT_TYPE = Symbol.getSymbol("application/x-java-serialized-object"); 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 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 DURABLE = "DURABLE"; public static final String PRIORITY = "PRIORITY"; 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_HEADER_DURABLE = JMS_AMQP_PREFIX + HEADER + DURABLE; public static final String JMS_AMQP_HEADER_PRIORITY = JMS_AMQP_PREFIX + HEADER + PRIORITY; public static final String JMS_AMQP_PROPERTIES = JMS_AMQP_PREFIX + PROPERTIES; 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; public static final Symbol JMS_DEST_TYPE_MSG_ANNOTATION = getSymbol("x-opt-jms-dest"); public static final Symbol JMS_REPLY_TO_TYPE_MSG_ANNOTATION = getSymbol("x-opt-jms-reply-to"); 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; /** * 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 String toAddress(Destination destination) { if (destination instanceof ActiveMQDestination) { return ((ActiveMQDestination) destination).getAddress(); } return null; } public static ServerJMSBytesMessage createBytesMessage(long id) { return new ServerJMSBytesMessage(newMessage(id, BYTES_TYPE)); } public static ServerJMSBytesMessage createBytesMessage(long id, byte[] array, int arrayOffset, int length) throws JMSException { ServerJMSBytesMessage message = createBytesMessage(id); message.writeBytes(array, arrayOffset, length); return message; } public static ServerJMSStreamMessage createStreamMessage(long id) { return new ServerJMSStreamMessage(newMessage(id, STREAM_TYPE)); } public static ServerJMSMessage createMessage(long id) { return new ServerJMSMessage(newMessage(id, DEFAULT_TYPE)); } public static ServerJMSTextMessage createTextMessage(long id) { return new ServerJMSTextMessage(newMessage(id, TEXT_TYPE)); } public static ServerJMSTextMessage createTextMessage(long id, String text) throws JMSException { ServerJMSTextMessage message = createTextMessage(id); message.setText(text); return message; } public static ServerJMSObjectMessage createObjectMessage(long id) { return new ServerJMSObjectMessage(newMessage(id, OBJECT_TYPE)); } public static ServerJMSMessage createObjectMessage(long id, Binary serializedForm) throws JMSException { ServerJMSObjectMessage message = createObjectMessage(id); message.setSerializedForm(serializedForm); return message; } public static ServerJMSMessage createObjectMessage(long id, byte[] array, int offset, int length) throws JMSException { ServerJMSObjectMessage message = createObjectMessage(id); message.setSerializedForm(new Binary(array, offset, length)); return message; } public static ServerJMSMapMessage createMapMessage(long id) { return new ServerJMSMapMessage(newMessage(id, MAP_TYPE)); } public static ServerJMSMapMessage createMapMessage(long id, Map<String, Object> content) throws JMSException { ServerJMSMapMessage message = createMapMessage(id); 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 CoreMessage newMessage(long id, byte messageType) { CoreMessage message = new CoreMessage(id, 512); message.setType(messageType); // ((ResetLimitWrappedActiveMQBuffer) message.getBodyBuffer()).setMessage(null); return message; } }