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.JMS_AMQP_CONTENT_ENCODING;
020import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.JMS_AMQP_CONTENT_TYPE;
021import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.JMS_AMQP_FIRST_ACQUIRER;
022import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.JMS_AMQP_FOOTER_PREFIX;
023import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.JMS_AMQP_HEADER;
024import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.JMS_AMQP_MESSAGE_ANNOTATION_PREFIX;
025import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.JMS_AMQP_PROPERTIES;
026import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.JMS_AMQP_REPLYTO_GROUP_ID;
027
028import java.nio.charset.StandardCharsets;
029import java.util.Map;
030import java.util.Set;
031
032import javax.jms.JMSException;
033import javax.jms.Message;
034
035import org.apache.activemq.ScheduledMessage;
036import org.apache.activemq.command.ActiveMQDestination;
037import org.apache.activemq.command.ActiveMQMessage;
038import org.apache.activemq.transport.amqp.AmqpProtocolException;
039import org.apache.qpid.proton.amqp.Binary;
040import org.apache.qpid.proton.amqp.Decimal128;
041import org.apache.qpid.proton.amqp.Decimal32;
042import org.apache.qpid.proton.amqp.Decimal64;
043import org.apache.qpid.proton.amqp.Symbol;
044import org.apache.qpid.proton.amqp.UnsignedByte;
045import org.apache.qpid.proton.amqp.UnsignedInteger;
046import org.apache.qpid.proton.amqp.UnsignedLong;
047import org.apache.qpid.proton.amqp.UnsignedShort;
048import org.apache.qpid.proton.amqp.messaging.ApplicationProperties;
049import org.apache.qpid.proton.amqp.messaging.Footer;
050import org.apache.qpid.proton.amqp.messaging.Header;
051import org.apache.qpid.proton.amqp.messaging.MessageAnnotations;
052import org.apache.qpid.proton.amqp.messaging.Properties;
053
054public abstract class InboundTransformer {
055
056    public static final String TRANSFORMER_NATIVE = "native";
057    public static final String TRANSFORMER_RAW = "raw";
058    public static final String TRANSFORMER_JMS = "jms";
059
060    public abstract String getTransformerName();
061
062    public abstract InboundTransformer getFallbackTransformer();
063
064    public final ActiveMQMessage transform(EncodedMessage amqpMessage) throws Exception {
065        InboundTransformer transformer = this;
066        ActiveMQMessage message = null;
067
068        while (transformer != null) {
069            try {
070                message = transformer.doTransform(amqpMessage);
071                break;
072            } catch (Exception e) {
073                transformer = transformer.getFallbackTransformer();
074            }
075        }
076
077        if (message == null) {
078            throw new AmqpProtocolException("Failed to transform incoming delivery, skipping.", false);
079        }
080
081        return message;
082    }
083
084    protected abstract ActiveMQMessage doTransform(EncodedMessage amqpMessage) throws Exception;
085
086    @SuppressWarnings("unchecked")
087    protected void populateMessage(ActiveMQMessage jms, org.apache.qpid.proton.message.Message amqp) throws Exception {
088        Header header = amqp.getHeader();
089        if (header != null) {
090            jms.setBooleanProperty(JMS_AMQP_HEADER, true);
091
092            if (header.getDurable() != null) {
093                jms.setPersistent(header.getDurable().booleanValue());
094            } else {
095                jms.setPersistent(false);
096            }
097
098            if (header.getPriority() != null) {
099                jms.setJMSPriority(header.getPriority().intValue());
100            } else {
101                jms.setPriority((byte) Message.DEFAULT_PRIORITY);
102            }
103
104            if (header.getFirstAcquirer() != null) {
105                jms.setBooleanProperty(JMS_AMQP_FIRST_ACQUIRER, header.getFirstAcquirer());
106            }
107
108            if (header.getDeliveryCount() != null) {
109                jms.setRedeliveryCounter(header.getDeliveryCount().intValue());
110            }
111        } else {
112            jms.setPriority((byte) Message.DEFAULT_PRIORITY);
113            jms.setPersistent(false);
114        }
115
116        final MessageAnnotations ma = amqp.getMessageAnnotations();
117        if (ma != null) {
118            for (Map.Entry<?, ?> entry : ma.getValue().entrySet()) {
119                String key = entry.getKey().toString();
120                if ("x-opt-delivery-time".equals(key) && entry.getValue() != null) {
121                    long deliveryTime = ((Number) entry.getValue()).longValue();
122                    long delay = deliveryTime - System.currentTimeMillis();
123                    if (delay > 0) {
124                        jms.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, delay);
125                    }
126                } else if ("x-opt-delivery-delay".equals(key) && entry.getValue() != null) {
127                    long delay = ((Number) entry.getValue()).longValue();
128                    if (delay > 0) {
129                        jms.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, delay);
130                    }
131                } else if ("x-opt-delivery-repeat".equals(key) && entry.getValue() != null) {
132                    int repeat = ((Number) entry.getValue()).intValue();
133                    if (repeat > 0) {
134                        jms.setIntProperty(ScheduledMessage.AMQ_SCHEDULED_REPEAT, repeat);
135                    }
136                } else if ("x-opt-delivery-period".equals(key) && entry.getValue() != null) {
137                    long period = ((Number) entry.getValue()).longValue();
138                    if (period > 0) {
139                        jms.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_PERIOD, period);
140                    }
141                } else if ("x-opt-delivery-cron".equals(key) && entry.getValue() != null) {
142                    String cronEntry = (String) entry.getValue();
143                    if (cronEntry != null) {
144                        jms.setStringProperty(ScheduledMessage.AMQ_SCHEDULED_CRON, cronEntry);
145                    }
146                }
147
148                setProperty(jms, JMS_AMQP_MESSAGE_ANNOTATION_PREFIX + key, entry.getValue());
149            }
150        }
151
152        final ApplicationProperties ap = amqp.getApplicationProperties();
153        if (ap != null) {
154            for (Map.Entry<String, Object> entry : ((Map<String, Object>) ap.getValue()).entrySet()) {
155                setProperty(jms,  entry.getKey(), entry.getValue());
156            }
157        }
158
159        final Properties properties = amqp.getProperties();
160        if (properties != null) {
161            jms.setBooleanProperty(JMS_AMQP_PROPERTIES, true);
162            if (properties.getMessageId() != null) {
163                jms.setJMSMessageID(AMQPMessageIdHelper.INSTANCE.toBaseMessageIdString(properties.getMessageId()));
164            }
165            Binary userId = properties.getUserId();
166            if (userId != null) {
167                jms.setUserID(new String(userId.getArray(), userId.getArrayOffset(), userId.getLength(), StandardCharsets.UTF_8));
168            }
169            if (properties.getTo() != null) {
170                jms.setDestination((ActiveMQDestination.createDestination(properties.getTo(), ActiveMQDestination.QUEUE_TYPE)));
171            }
172            if (properties.getSubject() != null) {
173                jms.setType(properties.getSubject());
174            }
175            if (properties.getReplyTo() != null) {
176                jms.setReplyTo((ActiveMQDestination.createDestination(properties.getReplyTo(), ActiveMQDestination.QUEUE_TYPE)));
177            }
178            if (properties.getCorrelationId() != null) {
179                jms.setCorrelationId(AMQPMessageIdHelper.INSTANCE.toBaseMessageIdString(properties.getCorrelationId()));
180            }
181            if (properties.getContentType() != null) {
182                jms.setStringProperty(JMS_AMQP_CONTENT_TYPE, properties.getContentType().toString());
183            }
184            if (properties.getContentEncoding() != null) {
185                jms.setStringProperty(JMS_AMQP_CONTENT_ENCODING, properties.getContentEncoding().toString());
186            }
187            if (properties.getCreationTime() != null) {
188                jms.setTimestamp(properties.getCreationTime().getTime());
189            }
190            if (properties.getGroupId() != null) {
191                jms.setGroupID(properties.getGroupId());
192            }
193            if (properties.getGroupSequence() != null) {
194                jms.setGroupSequence(properties.getGroupSequence().intValue());
195            }
196            if (properties.getReplyToGroupId() != null) {
197                jms.setStringProperty(JMS_AMQP_REPLYTO_GROUP_ID, properties.getReplyToGroupId());
198            }
199            if (properties.getAbsoluteExpiryTime() != null) {
200                jms.setExpiration(properties.getAbsoluteExpiryTime().getTime());
201            }
202        }
203
204        // If the jms expiration has not yet been set...
205        if (header != null && jms.getJMSExpiration() == 0) {
206            // Then lets try to set it based on the message ttl.
207            long ttl = Message.DEFAULT_TIME_TO_LIVE;
208            if (header.getTtl() != null) {
209                ttl = header.getTtl().longValue();
210            }
211
212            if (ttl != javax.jms.Message.DEFAULT_TIME_TO_LIVE) {
213                jms.setExpiration(System.currentTimeMillis() + ttl);
214            }
215        }
216
217        final Footer fp = amqp.getFooter();
218        if (fp != null) {
219            for (Map.Entry<Object, Object> entry : (Set<Map.Entry<Object, Object>>) fp.getValue().entrySet()) {
220                String key = entry.getKey().toString();
221                setProperty(jms, JMS_AMQP_FOOTER_PREFIX + key, entry.getValue());
222            }
223        }
224    }
225
226    private void setProperty(Message msg, String key, Object value) throws JMSException {
227        if (value instanceof UnsignedLong) {
228            long v = ((UnsignedLong) value).longValue();
229            msg.setLongProperty(key, v);
230        } else if (value instanceof UnsignedInteger) {
231            long v = ((UnsignedInteger) value).longValue();
232            if (Integer.MIN_VALUE <= v && v <= Integer.MAX_VALUE) {
233                msg.setIntProperty(key, (int) v);
234            } else {
235                msg.setLongProperty(key, v);
236            }
237        } else if (value instanceof UnsignedShort) {
238            int v = ((UnsignedShort) value).intValue();
239            if (Short.MIN_VALUE <= v && v <= Short.MAX_VALUE) {
240                msg.setShortProperty(key, (short) v);
241            } else {
242                msg.setIntProperty(key, v);
243            }
244        } else if (value instanceof UnsignedByte) {
245            short v = ((UnsignedByte) value).shortValue();
246            if (Byte.MIN_VALUE <= v && v <= Byte.MAX_VALUE) {
247                msg.setByteProperty(key, (byte) v);
248            } else {
249                msg.setShortProperty(key, v);
250            }
251        } else if (value instanceof Symbol) {
252            msg.setStringProperty(key, value.toString());
253        } else if (value instanceof Decimal128) {
254            msg.setDoubleProperty(key, ((Decimal128) value).doubleValue());
255        } else if (value instanceof Decimal64) {
256            msg.setDoubleProperty(key, ((Decimal64) value).doubleValue());
257        } else if (value instanceof Decimal32) {
258            msg.setFloatProperty(key, ((Decimal32) value).floatValue());
259        } else if (value instanceof Binary) {
260            msg.setStringProperty(key, value.toString());
261        } else {
262            msg.setObjectProperty(key, value);
263        }
264    }
265}