Package org.hornetq.core.protocol.proton

Source Code of org.hornetq.core.protocol.proton.ProtonConsumer

/*
* Copyright 2005-2014 Red Hat, Inc.
* Red Hat 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.hornetq.core.protocol.proton;

import java.util.Map;

import org.apache.qpid.proton.amqp.DescribedType;
import org.apache.qpid.proton.amqp.Symbol;
import org.apache.qpid.proton.amqp.messaging.Accepted;
import org.apache.qpid.proton.amqp.messaging.Modified;
import org.apache.qpid.proton.amqp.messaging.Rejected;
import org.apache.qpid.proton.amqp.messaging.Released;
import org.apache.qpid.proton.amqp.transport.DeliveryState;
import org.apache.qpid.proton.amqp.transport.SenderSettleMode;
import org.apache.qpid.proton.engine.Delivery;
import org.apache.qpid.proton.engine.Sender;
import org.apache.qpid.proton.jms.EncodedMessage;
import org.hornetq.api.core.SimpleString;
import org.hornetq.core.client.impl.ClientConsumerImpl;
import org.hornetq.core.protocol.proton.exceptions.HornetQAMQPException;
import org.hornetq.core.server.HornetQServer;
import org.hornetq.core.server.QueueQueryResult;
import org.hornetq.core.server.ServerMessage;

/**
* A this is a wrapper around a HornetQ ServerConsumer for handling outgoing messages and incoming acks via a Proton Sender
*
* @author <a href="mailto:andy.taylor@jboss.org">Andy Taylor</a>
*/
public class ProtonConsumer implements ProtonDeliveryHandler
{
   private static final Symbol SELECTOR = Symbol.getSymbol("jms-selector");
   private static final Symbol COPY = Symbol.valueOf("copy");
   private final ProtonSession protonSession;
   private final HornetQServer server;
   private final Sender sender;
   private final ProtonRemotingConnection connection;
   private final ProtonProtocolManager protonProtocolManager;
   private long consumerID;
   private boolean closed = false;
   private long forcedDeliveryCount = 0;
   private boolean forcingDelivery = false;
   private boolean receivedForcedDelivery = true;

   public ProtonConsumer(ProtonRemotingConnection connection, Sender sender, ProtonSession protonSession, HornetQServer server,
                         ProtonProtocolManager protonProtocolManager)
   {
      this.connection = connection;
      this.sender = sender;
      this.protonSession = protonSession;
      this.server = server;
      this.protonProtocolManager = protonProtocolManager;
   }

   /*
   * start the session
   * */
   public void start() throws HornetQAMQPException
   {
      protonSession.getServerSession().start();

      //todo add flow control
      try
      {
         protonSession.getServerSession().receiveConsumerCredits(consumerID, -1);
      }
      catch (Exception e)
      {
         throw HornetQAMQPProtocolMessageBundle.BUNDLE.errorStartingConsumer(e.getMessage());
      }
   }

   /*
   * create the actual underlying HornetQ Server Consumer
   * */
   public void init() throws HornetQAMQPException
   {
      org.apache.qpid.proton.amqp.messaging.Source source = (org.apache.qpid.proton.amqp.messaging.Source) sender.getRemoteSource();

      SimpleString queue;

      consumerID = server.getStorageManager().generateUniqueID();

      SimpleString selector = null;
      Map filter = source.getFilter();
      if (filter != null)
      {
         DescribedType value = (DescribedType) filter.get(SELECTOR);
         if (value != null)
         {
            selector = new SimpleString(value.getDescribed().toString());
         }
      }

      if (source.getDynamic())
      {
         //if dynamic we have to create the node (queue) and set the address on the target, the node is temporary and
         // will be deleted on closing of the session
         queue = new SimpleString(java.util.UUID.randomUUID().toString());
         try
         {
            protonSession.getServerSession().createQueue(queue, queue, null, true, false);
         }
         catch (Exception e)
         {
            throw HornetQAMQPProtocolMessageBundle.BUNDLE.errorCreatingTemporaryQueue(e.getMessage());
         }
         source.setAddress(queue.toString());
      }
      else
      {
         //if not dynamic then we use the targets address as the address to forward the messages to, however there has to
         //be a queue bound to it so we nee to check this.
         String address = source.getAddress();
         if (address == null)
         {
            throw HornetQAMQPProtocolMessageBundle.BUNDLE.sourceAddressNotSet();
         }

         queue = new SimpleString(source.getAddress());
         QueueQueryResult queryResult;
         try
         {
            queryResult = protonSession.getServerSession().executeQueueQuery(new SimpleString(address));
         }
         catch (Exception e)
         {
            throw HornetQAMQPProtocolMessageBundle.BUNDLE.errorFindingTemporaryQueue(e.getMessage());
         }
         if (!queryResult.isExists())
         {
            throw HornetQAMQPProtocolMessageBundle.BUNDLE.sourceAddressDoesntExist();
         }
      }
      boolean browseOnly = source.getDistributionMode() != null && source.getDistributionMode().equals(COPY);
      try
      {
         protonSession.getServerSession().createConsumer(consumerID, queue, selector, browseOnly);
      }
      catch (Exception e)
      {
         throw HornetQAMQPProtocolMessageBundle.BUNDLE.errorCreatingHornetQConsumer(e.getMessage());
      }
   }

   /*
   * close the session
   * */
   public synchronized void close() throws HornetQAMQPException
   {
      closed = true;
      protonSession.removeConsumer(consumerID);
   }

   public long getConsumerID()
   {
      return consumerID;
   }

   /*
   * handle an out going message from HornetQ, send via the Proton Sender
   * */
   public synchronized int handleDelivery(ServerMessage message, int deliveryCount)
   {
      if (closed)
      {
         return 0;
      }
      if (message.containsProperty(ClientConsumerImpl.FORCED_DELIVERY_MESSAGE))
      {
         if (forcingDelivery)
         {
            sender.drained();
         }
         else
         {
            receivedForcedDelivery = true;
            forcingDelivery = false;
         }
         return 0;
      }
      //if we get here then a forced delivery has pushed some messages thru and we continue
      if (forcingDelivery)
      {
         forcingDelivery = false;
      }
      //presettle means we can ack the message on the proton side before we send it, i.e. for browsers
      boolean preSettle = sender.getRemoteSenderSettleMode() == SenderSettleMode.SETTLED;
      //we only need a tag if we are going to ack later
      byte[] tag = preSettle ? new byte[0] : protonSession.getTag();
      //encode the message
      EncodedMessage encodedMessage = ProtonUtils.OUTBOUND.transform(message, deliveryCount);
      //now handle the delivery
      protonProtocolManager.handleDelivery(sender, tag, encodedMessage, message, connection, preSettle);

      return encodedMessage.getLength();
   }

   @Override
   /*
   * handle an incoming Ack from Proton, basically pass to HornetQ to handle
   * */
   public void onMessage(Delivery delivery) throws HornetQAMQPException
   {
      ServerMessage message = (ServerMessage) delivery.getContext();

      boolean preSettle = sender.getRemoteSenderSettleMode() == SenderSettleMode.SETTLED;


      DeliveryState remoteState = delivery.getRemoteState();

      if (remoteState != null)
      {
         if (remoteState instanceof Accepted)
         {
            //we have to individual ack as we can't guarantee we will get the delivery updates (including acks) in order
            // from proton, a perf hit but a must
            try
            {
               protonSession.getServerSession().individualAcknowledge(consumerID, message.getMessageID());
            }
            catch (Exception e)
            {
               throw HornetQAMQPProtocolMessageBundle.BUNDLE.errorAcknowledgingMessage(message.getMessageID(), e.getMessage());
            }
         }
         else if (remoteState instanceof Released)
         {
            try
            {
               protonSession.getServerSession().individualCancel(consumerID, message.getMessageID(), false);
            }
            catch (Exception e)
            {
               throw HornetQAMQPProtocolMessageBundle.BUNDLE.errorCancellingMessage(message.getMessageID(), e.getMessage());
            }
         }
         else if (remoteState instanceof Rejected || remoteState instanceof Modified)
         {
            try
            {
               protonSession.getServerSession().individualCancel(consumerID, message.getMessageID(), true);
            }
            catch (Exception e)
            {
               throw HornetQAMQPProtocolMessageBundle.BUNDLE.errorCancellingMessage(message.getMessageID(), e.getMessage());
            }
         }

         synchronized (connection.getDeliveryLock())
         {
            delivery.settle();
         }
         //todo add tag caching
         if (!preSettle)
         {
            protonSession.replaceTag(delivery.getTag());
         }
         sender.offer(1);
      }
      else
      {
         //todo not sure if we need to do anything here
      }
   }

   /*
   * check the state of the consumer, i.e. are there any more messages. only really needed for browsers?
   * */
   public synchronized void checkState()
   {
      if (!forcingDelivery && receivedForcedDelivery)
      {
         try
         {
            forcingDelivery = true;
            receivedForcedDelivery = false;
            protonSession.getServerSession().forceConsumerDelivery(consumerID, forcedDeliveryCount++);
         }
         catch (Exception e)
         {
            e.printStackTrace();
         }
      }
   }

   public Sender getSender()
   {
      return sender;
   }

   private String formatTag(byte[] tag)
   {
      StringBuffer sb = new StringBuffer();
      for (byte b : tag)
      {
         sb.append(b).append(":");
      }
      return sb.toString();
   }

   int x = 5;
}
TOP

Related Classes of org.hornetq.core.protocol.proton.ProtonConsumer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.