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.protocol;
018
019import org.apache.activemq.command.LocalTransactionId;
020import org.apache.activemq.transport.amqp.AmqpProtocolException;
021import org.apache.qpid.proton.amqp.transport.ReceiverSettleMode;
022import org.apache.qpid.proton.engine.Delivery;
023import org.apache.qpid.proton.engine.Receiver;
024import org.fusesource.hawtbuf.Buffer;
025import org.fusesource.hawtbuf.ByteArrayOutputStream;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029/**
030 * Abstract base that provides common services for AMQP Receiver types.
031 */
032public abstract class AmqpAbstractReceiver extends AmqpAbstractLink<Receiver> {
033
034    private static final Logger LOG = LoggerFactory.getLogger(AmqpAbstractReceiver.class);
035
036    protected ByteArrayOutputStream current = new ByteArrayOutputStream();
037    protected final byte[] recvBuffer = new byte[1024 * 8];
038    protected final int configuredCredit;
039
040    /**
041     * Handle create of new AMQP Receiver instance.
042     *
043     * @param session
044     *        the AmqpSession that servers as the parent of this Link.
045     * @param endpoint
046     *        the Receiver endpoint being managed by this class.
047     */
048    public AmqpAbstractReceiver(AmqpSession session, Receiver endpoint) {
049        super(session, endpoint);
050        this.configuredCredit = session.getConnection().getConfiguredReceiverCredit();
051
052        // We don't support second so enforce it as First and let remote decide what to do
053        this.endpoint.setReceiverSettleMode(ReceiverSettleMode.FIRST);
054
055        // Match what the sender mode is
056        this.endpoint.setSenderSettleMode(endpoint.getRemoteSenderSettleMode());
057    }
058
059    @Override
060    public void detach() {
061    }
062
063    @Override
064    public void flow() throws Exception {
065    }
066
067    /**
068     * Returns the amount of receiver credit that has been configured for this AMQP
069     * transport.  If no value was configured on the TransportConnector URI then a
070     * sensible default is used.
071     *
072     * @return the configured receiver credit to grant.
073     */
074    public int getConfiguredReceiverCredit() {
075        return configuredCredit;
076    }
077
078    /**
079     * Provide the receiver endpoint with the given amount of credits.
080     *
081     * @param credits
082     *        the credit value to pass on to the wrapped Receiver.
083     */
084    public void flow(int credits) {
085        getEndpoint().flow(credits);
086    }
087
088    @Override
089    public void commit(LocalTransactionId txnId) throws Exception {
090    }
091
092    @Override
093    public void rollback(LocalTransactionId txnId) throws Exception {
094    }
095
096    @Override
097    public void delivery(Delivery delivery) throws Exception {
098
099        if (!delivery.isReadable()) {
100            LOG.debug("Delivery was not readable!");
101            return;
102        }
103
104        if (current == null) {
105            current = new ByteArrayOutputStream();
106        }
107
108        int count;
109        while ((count = getEndpoint().recv(recvBuffer, 0, recvBuffer.length)) > 0) {
110            current.write(recvBuffer, 0, count);
111
112            if (current.size() > session.getMaxFrameSize()) {
113                throw new AmqpProtocolException("Frame size of " + current.size() + " larger than max allowed " + session.getMaxFrameSize());
114            }
115        }
116
117        // Expecting more deliveries..
118        if (count == 0) {
119            return;
120        }
121
122        try {
123            processDelivery(delivery, current.toBuffer());
124        } finally {
125            getEndpoint().advance();
126            current = null;
127        }
128    }
129
130    protected abstract void processDelivery(Delivery delivery, Buffer deliveryBytes) throws Exception;
131
132}