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_MESSAGE_FORMAT;
020import static org.apache.activemq.transport.amqp.message.AmqpMessageSupport.getBinaryFromMessageBody;
021
022import javax.jms.JMSException;
023import javax.jms.MessageFormatException;
024
025import org.apache.activemq.command.ActiveMQBytesMessage;
026import org.apache.activemq.command.ActiveMQMessage;
027import org.apache.qpid.proton.amqp.Binary;
028import org.apache.qpid.proton.amqp.UnsignedInteger;
029import org.apache.qpid.proton.amqp.messaging.Header;
030import org.apache.qpid.proton.message.ProtonJMessage;
031
032public class AMQPNativeOutboundTransformer implements OutboundTransformer {
033
034    @Override
035    public EncodedMessage transform(ActiveMQMessage message) throws Exception {
036        if (message == null || !(message instanceof ActiveMQBytesMessage)) {
037            return null;
038        }
039
040        return transform(this, (ActiveMQBytesMessage) message);
041    }
042
043    static EncodedMessage transform(OutboundTransformer options, ActiveMQBytesMessage message) throws JMSException {
044        final long messageFormat;
045        if (message.propertyExists(JMS_AMQP_MESSAGE_FORMAT)) {
046            try {
047                messageFormat = message.getLongProperty(JMS_AMQP_MESSAGE_FORMAT);
048            } catch (MessageFormatException e) {
049                return null;
050            }
051        } else {
052            messageFormat = 0;
053        }
054
055        Binary encodedMessage = getBinaryFromMessageBody(message);
056        byte encodedData[] = encodedMessage.getArray();
057        int encodedSize = encodedMessage.getLength();
058
059        int count = message.getRedeliveryCounter();
060        if (count >= 1) {
061
062            // decode...
063            ProtonJMessage amqp = (ProtonJMessage) org.apache.qpid.proton.message.Message.Factory.create();
064            int offset = 0;
065            int len = encodedSize;
066            while (len > 0) {
067                final int decoded = amqp.decode(encodedData, offset, len);
068                assert decoded > 0 : "Make progress decoding the message";
069                offset += decoded;
070                len -= decoded;
071            }
072
073            // Update the DeliveryCount header...
074            // The AMQP delivery-count field only includes prior failed delivery attempts,
075            // whereas JMSXDeliveryCount includes the first/current delivery attempt. Subtract 1.
076            if (amqp.getHeader() == null) {
077                amqp.setHeader(new Header());
078            }
079
080            amqp.getHeader().setDeliveryCount(new UnsignedInteger(count));
081
082            // Re-encode...
083            final AmqpWritableBuffer buffer = new AmqpWritableBuffer();
084            int written = amqp.encode(buffer);
085
086            encodedData = buffer.getArray();
087            encodedSize = written;
088        }
089
090        return new EncodedMessage(messageFormat, encodedData, 0, encodedSize);
091    }
092}