001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.activemq.transport.amqp.message; 018 019import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.AMQP_DATA; 020import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.AMQP_NULL; 021import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.AMQP_SEQUENCE; 022import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.AMQP_VALUE_BINARY; 023import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.AMQP_VALUE_LIST; 024import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.AMQP_VALUE_MAP; 025import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.AMQP_VALUE_NULL; 026import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.AMQP_VALUE_STRING; 027import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.JMS_AMQP_MESSAGE_FORMAT; 028import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.JMS_AMQP_ORIGINAL_ENCODING; 029import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.OCTET_STREAM_CONTENT_TYPE; 030import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.SERIALIZED_JAVA_OBJECT_CONTENT_TYPE; 031import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.getCharsetForTextualContent; 032import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.isContentType; 033 034import java.nio.ByteBuffer; 035import java.nio.CharBuffer; 036import java.nio.charset.CharacterCodingException; 037import java.nio.charset.Charset; 038import java.nio.charset.StandardCharsets; 039import java.util.Arrays; 040import java.util.List; 041import java.util.Map; 042import java.util.Set; 043 044import javax.jms.JMSException; 045import javax.jms.MessageNotWriteableException; 046 047import org.apache.activemq.command.ActiveMQBytesMessage; 048import org.apache.activemq.command.ActiveMQMapMessage; 049import org.apache.activemq.command.ActiveMQMessage; 050import org.apache.activemq.command.ActiveMQObjectMessage; 051import org.apache.activemq.command.ActiveMQStreamMessage; 052import org.apache.activemq.command.ActiveMQTextMessage; 053import org.apache.activemq.transport.amqp.AmqpProtocolException; 054import org.apache.activemq.util.ByteSequence; 055import org.apache.qpid.proton.amqp.Binary; 056import org.apache.qpid.proton.amqp.messaging.AmqpSequence; 057import org.apache.qpid.proton.amqp.messaging.AmqpValue; 058import org.apache.qpid.proton.amqp.messaging.Data; 059import org.apache.qpid.proton.amqp.messaging.Section; 060import org.apache.qpid.proton.message.Message; 061 062public class JMSMappingInboundTransformer extends InboundTransformer { 063 064 @Override 065 public String getTransformerName() { 066 return TRANSFORMER_JMS; 067 } 068 069 @Override 070 public InboundTransformer getFallbackTransformer() { 071 return new AMQPNativeInboundTransformer(); 072 } 073 074 @Override 075 protected ActiveMQMessage doTransform(EncodedMessage amqpMessage) throws Exception { 076 Message amqp = amqpMessage.decode(); 077 078 ActiveMQMessage result = createMessage(amqp, amqpMessage); 079 080 populateMessage(result, amqp); 081 082 if (amqpMessage.getMessageFormat() != 0) { 083 result.setLongProperty(JMS_AMQP_MESSAGE_FORMAT, amqpMessage.getMessageFormat()); 084 } 085 086 return result; 087 } 088 089 @SuppressWarnings({ "unchecked" }) 090 private ActiveMQMessage createMessage(Message message, EncodedMessage original) throws Exception { 091 092 Section body = message.getBody(); 093 ActiveMQMessage result; 094 095 if (body == null) { 096 if (isContentType(SERIALIZED_JAVA_OBJECT_CONTENT_TYPE, message)) { 097 result = new ActiveMQObjectMessage(); 098 } else if (isContentType(OCTET_STREAM_CONTENT_TYPE, message) || isContentType(null, message)) { 099 result = new ActiveMQBytesMessage(); 100 } else { 101 Charset charset = getCharsetForTextualContent(message.getContentType()); 102 if (charset != null) { 103 result = new ActiveMQTextMessage(); 104 } else { 105 result = new ActiveMQMessage(); 106 } 107 } 108 109 result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_NULL); 110 } else if (body instanceof Data) { 111 Binary payload = ((Data) body).getValue(); 112 113 if (isContentType(SERIALIZED_JAVA_OBJECT_CONTENT_TYPE, message)) { 114 result = createObjectMessage(payload.getArray(), payload.getArrayOffset(), payload.getLength()); 115 } else if (isContentType(OCTET_STREAM_CONTENT_TYPE, message)) { 116 result = createBytesMessage(payload.getArray(), payload.getArrayOffset(), payload.getLength()); 117 } else { 118 Charset charset = getCharsetForTextualContent(message.getContentType()); 119 if (StandardCharsets.UTF_8.equals(charset)) { 120 ByteBuffer buf = ByteBuffer.wrap(payload.getArray(), payload.getArrayOffset(), payload.getLength()); 121 122 try { 123 CharBuffer chars = charset.newDecoder().decode(buf); 124 result = createTextMessage(String.valueOf(chars)); 125 } catch (CharacterCodingException e) { 126 result = createBytesMessage(payload.getArray(), payload.getArrayOffset(), payload.getLength()); 127 } 128 } else { 129 result = createBytesMessage(payload.getArray(), payload.getArrayOffset(), payload.getLength()); 130 } 131 } 132 133 result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_DATA); 134 } else if (body instanceof AmqpSequence) { 135 AmqpSequence sequence = (AmqpSequence) body; 136 ActiveMQStreamMessage m = new ActiveMQStreamMessage(); 137 for (Object item : sequence.getValue()) { 138 m.writeObject(item); 139 } 140 141 result = m; 142 result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_SEQUENCE); 143 } else if (body instanceof AmqpValue) { 144 Object value = ((AmqpValue) body).getValue(); 145 if (value == null || value instanceof String) { 146 result = createTextMessage((String) value); 147 148 result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, value == null ? AMQP_VALUE_NULL : AMQP_VALUE_STRING); 149 } else if (value instanceof Binary) { 150 Binary payload = (Binary) value; 151 152 if (isContentType(SERIALIZED_JAVA_OBJECT_CONTENT_TYPE, message)) { 153 result = createObjectMessage(payload.getArray(), payload.getArrayOffset(), payload.getLength()); 154 } else { 155 result = createBytesMessage(payload.getArray(), payload.getArrayOffset(), payload.getLength()); 156 } 157 158 result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_BINARY); 159 } else if (value instanceof List) { 160 ActiveMQStreamMessage m = new ActiveMQStreamMessage(); 161 for (Object item : (List<Object>) value) { 162 m.writeObject(item); 163 } 164 result = m; 165 result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_LIST); 166 } else if (value instanceof Map) { 167 result = createMapMessage((Map<String, Object>) value); 168 result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_MAP); 169 } else { 170 // Trigger fall-back to native encoder which generates BytesMessage with the 171 // original message stored in the message body. 172 throw new AmqpProtocolException("Unable to encode to ActiveMQ JMS Message", false); 173 } 174 } else { 175 throw new RuntimeException("Unexpected body type: " + body.getClass()); 176 } 177 178 return result; 179 } 180 181 private static ActiveMQBytesMessage createBytesMessage(byte[] content, int offset, int length) { 182 ActiveMQBytesMessage message = new ActiveMQBytesMessage(); 183 message.setContent(new ByteSequence(content, offset, length)); 184 return message; 185 } 186 187 public static ActiveMQTextMessage createTextMessage(String text) { 188 ActiveMQTextMessage message = new ActiveMQTextMessage(); 189 try { 190 message.setText(text); 191 } catch (MessageNotWriteableException ex) {} 192 193 return message; 194 } 195 196 public static ActiveMQObjectMessage createObjectMessage(byte[] content, int offset, int length) { 197 ActiveMQObjectMessage message = new ActiveMQObjectMessage(); 198 message.setContent(new ByteSequence(content, offset, length)); 199 return message; 200 } 201 202 public static ActiveMQMapMessage createMapMessage(Map<String, Object> content) throws JMSException { 203 ActiveMQMapMessage message = new ActiveMQMapMessage(); 204 final Set<Map.Entry<String, Object>> set = content.entrySet(); 205 for (Map.Entry<String, Object> entry : set) { 206 Object value = entry.getValue(); 207 if (value instanceof Binary) { 208 Binary binary = (Binary) value; 209 value = Arrays.copyOfRange(binary.getArray(), binary.getArrayOffset(), binary.getLength()); 210 } 211 message.setObject(entry.getKey(), value); 212 } 213 return message; 214 } 215}