Package org.apache.activemq.broker.region

Source Code of org.apache.activemq.broker.region.Queue

/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.activemq.broker.region;

import java.io.IOException;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.jms.InvalidSelectorException;
import javax.jms.JMSException;

import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.ConnectionContext;
import org.apache.activemq.broker.ProducerBrokerExchange;
import org.apache.activemq.broker.region.cursors.PendingMessageCursor;
import org.apache.activemq.broker.region.cursors.StoreQueueCursor;
import org.apache.activemq.broker.region.cursors.VMPendingMessageCursor;
import org.apache.activemq.broker.region.group.MessageGroupHashBucketFactory;
import org.apache.activemq.broker.region.group.MessageGroupMap;
import org.apache.activemq.broker.region.group.MessageGroupMapFactory;
import org.apache.activemq.broker.region.group.MessageGroupSet;
import org.apache.activemq.broker.region.policy.DeadLetterStrategy;
import org.apache.activemq.broker.region.policy.DispatchPolicy;
import org.apache.activemq.broker.region.policy.RoundRobinDispatchPolicy;
import org.apache.activemq.broker.region.policy.SharedDeadLetterStrategy;
import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.command.ConsumerId;
import org.apache.activemq.command.ExceptionResponse;
import org.apache.activemq.command.Message;
import org.apache.activemq.command.MessageAck;
import org.apache.activemq.command.MessageId;
import org.apache.activemq.command.ProducerAck;
import org.apache.activemq.command.ProducerInfo;
import org.apache.activemq.command.Response;
import org.apache.activemq.filter.BooleanExpression;
import org.apache.activemq.filter.MessageEvaluationContext;
import org.apache.activemq.kaha.Store;
import org.apache.activemq.selector.SelectorParser;
import org.apache.activemq.store.MessageRecoveryListener;
import org.apache.activemq.store.MessageStore;
import org.apache.activemq.thread.Task;
import org.apache.activemq.thread.TaskRunner;
import org.apache.activemq.thread.TaskRunnerFactory;
import org.apache.activemq.transaction.Synchronization;
import org.apache.activemq.usage.MemoryUsage;
import org.apache.activemq.usage.SystemUsage;
import org.apache.activemq.util.BrokerSupport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* The Queue is a List of MessageEntry objects that are dispatched to matching
* subscriptions.
*
* @version $Revision: 1.28 $
*/
public class Queue extends BaseDestination implements Task {

    final Broker broker;

    private final Log log;
    private final ActiveMQDestination destination;
    private final List<Subscription> consumers = new CopyOnWriteArrayList<Subscription>();
    private final SystemUsage systemUsage;
    private final MemoryUsage memoryUsage;
    private final DestinationStatistics destinationStatistics = new DestinationStatistics();
    private PendingMessageCursor messages;
    private final LinkedList<MessageReference> pagedInMessages = new LinkedList<MessageReference>();
    private LockOwner exclusiveOwner;
    private MessageGroupMap messageGroupOwners;

    private int garbageSize;
    private int garbageSizeBeforeCollection = 1000;
    private DispatchPolicy dispatchPolicy = new RoundRobinDispatchPolicy();
    private final MessageStore store;
    private DeadLetterStrategy deadLetterStrategy = new SharedDeadLetterStrategy();
    private MessageGroupMapFactory messageGroupMapFactory = new MessageGroupHashBucketFactory();
    private int maximumPagedInMessages = garbageSizeBeforeCollection * 2;
    private final MessageEvaluationContext queueMsgConext = new MessageEvaluationContext();
    private final Object exclusiveLockMutex = new Object();
    private final TaskRunner taskRunner;
   
    private final LinkedList<Runnable> messagesWaitingForSpace = new LinkedList<Runnable>();
    private final Runnable sendMessagesWaitingForSpaceTask = new Runnable() {
        public void run() {
            try {
                taskRunner.wakeup();
            } catch (InterruptedException e) {
            }
        };
    };

    public Queue(Broker broker, ActiveMQDestination destination, final SystemUsage systemUsage, MessageStore store, DestinationStatistics parentStats,
                 TaskRunnerFactory taskFactory, Store tmpStore) throws Exception {
        this.broker = broker;
        this.destination = destination;
        this.systemUsage=systemUsage;
        this.memoryUsage = new MemoryUsage(systemUsage.getMemoryUsage(), destination.toString());
        this.memoryUsage.setUsagePortion(1.0f);
        this.store = store;
        if (destination.isTemporary() || tmpStore==null ) {
            this.messages = new VMPendingMessageCursor();
        } else {
            this.messages = new StoreQueueCursor(this, tmpStore);
        }

        this.taskRunner = taskFactory.createTaskRunner(this, "Queue  " + destination.getPhysicalName());

        // Let the store know what usage manager we are using so that he can
        // flush messages to disk
        // when usage gets high.
        if (store != null) {
            store.setMemoryUsage(memoryUsage);
        }

        // let's copy the enabled property from the parent DestinationStatistics
        this.destinationStatistics.setEnabled(parentStats.isEnabled());
        destinationStatistics.setParent(parentStats);
        this.log = LogFactory.getLog(getClass().getName() + "." + destination.getPhysicalName());

    }

    public void initialize() throws Exception {
        if (store != null) {
            // Restore the persistent messages.
            messages.setSystemUsage(systemUsage);
            messages.setEnableAudit(isEnableAudit());
            messages.setMaxAuditDepth(getMaxAuditDepth());
            messages.setMaxProducersToAudit(getMaxProducersToAudit());
            if (messages.isRecoveryRequired()) {
                store.recover(new MessageRecoveryListener() {

                    public boolean recoverMessage(Message message) {
                        // Message could have expired while it was being
                        // loaded..
                        if (broker.isExpired(message)) {
                            broker.messageExpired(createConnectionContext(), message);
                            destinationStatistics.getMessages().decrement();
                            return true;
                        }
                        if (hasSpace()) {
                            message.setRegionDestination(Queue.this);
                            synchronized (messages) {
                                try {
                                    messages.addMessageLast(message);
                                } catch (Exception e) {
                                    log.fatal("Failed to add message to cursor", e);
                                }
                            }
                            destinationStatistics.getMessages().increment();
                            return true;
                        }
                        return false;
                    }

                    public boolean recoverMessageReference(MessageId messageReference) throws Exception {
                        throw new RuntimeException("Should not be called.");
                    }

                    public boolean hasSpace() {
                        return true;
                    }
                });
            }
        }
    }

    /**
     * Lock a node
     *
     * @param node
     * @param lockOwner
     * @return true if can be locked
     * @see org.apache.activemq.broker.region.Destination#lock(org.apache.activemq.broker.region.MessageReference,
     *      org.apache.activemq.broker.region.LockOwner)
     */
    public boolean lock(MessageReference node, LockOwner lockOwner) {
        synchronized (exclusiveLockMutex) {
            if (exclusiveOwner == lockOwner) {
                return true;
            }
            if (exclusiveOwner != null) {
                return false;
            }
        }
        return true;
    }

    public synchronized  void addSubscription(ConnectionContext context, Subscription sub) throws Exception {
        sub.add(context, this);
        destinationStatistics.getConsumers().increment();
        maximumPagedInMessages += sub.getConsumerInfo().getPrefetchSize();

        MessageEvaluationContext msgContext = context.getMessageEvaluationContext();
        try {
         
          //needs to be synchronized - so no contention with dispatching
            synchronized (consumers) {
                consumers.add(sub);
                if (sub.getConsumerInfo().isExclusive()) {
                    LockOwner owner = (LockOwner)sub;
                    if (exclusiveOwner == null) {
                        exclusiveOwner = owner;
                    } else {
                        // switch the owner if the priority is higher.
                        if (owner.getLockPriority() > exclusiveOwner.getLockPriority()) {
                            exclusiveOwner = owner;
                        }
                    }
                }
            }
           
            //we hold the lock on the dispatchValue - so lets build the paged in
            //list directly;
            buildList(false);
          
            // synchronize with dispatch method so that no new messages are sent
            // while
            // setting up a subscription. avoid out of order messages,
            // duplicates
            // etc. 
           
        
           
                msgContext.setDestination(destination);
                synchronized (pagedInMessages) {
                    // Add all the matching messages in the queue to the
                    // subscription.
                    for (Iterator<MessageReference> i = pagedInMessages.iterator(); i.hasNext();) {
                        QueueMessageReference node = (QueueMessageReference)i.next();
                        if (node.isDropped() ||  (!sub.getConsumerInfo().isBrowser() && node.getLockOwner()!=null)) {
                            continue;
                        }
                        try {
                            msgContext.setMessageReference(node);
                            if (sub.matches(node, msgContext)) {
                                sub.add(node);
                            }
                        } catch (IOException e) {
                            log.warn("Could not load message: " + e, e);
                        }
                    }
                }
         
           
           
        } finally {
            msgContext.clear();
        }
    }

    public synchronized void removeSubscription(ConnectionContext context,
          Subscription sub) throws Exception{
    destinationStatistics.getConsumers().decrement();
    maximumPagedInMessages-=sub.getConsumerInfo().getPrefetchSize();
    // synchronize with dispatch method so that no new messages are sent
    // while
    // removing up a subscription.
    synchronized(consumers){
      consumers.remove(sub);
      if(sub.getConsumerInfo().isExclusive()){
        LockOwner owner=(LockOwner)sub;
        // Did we loose the exclusive owner??
        if(exclusiveOwner==owner){
          // Find the exclusive consumer with the higest Lock
          // Priority.
          exclusiveOwner=null;
          for(Iterator<Subscription> iter=consumers.iterator();iter
                  .hasNext();){
            Subscription s=iter.next();
            LockOwner so=(LockOwner)s;
            if(s.getConsumerInfo().isExclusive()
                    &&(exclusiveOwner==null||so.getLockPriority()>exclusiveOwner
                            .getLockPriority())){
              exclusiveOwner=so;
            }
          }
        }
      }
      if(consumers.isEmpty()){
        messages.gc();
      }
    }
    sub.remove(context,this);
    boolean wasExclusiveOwner=false;
    if(exclusiveOwner==sub){
      exclusiveOwner=null;
      wasExclusiveOwner=true;
    }
    ConsumerId consumerId=sub.getConsumerInfo().getConsumerId();
    MessageGroupSet ownedGroups=getMessageGroupOwners().removeConsumer(
            consumerId);
    if(!sub.getConsumerInfo().isBrowser()){
      MessageEvaluationContext msgContext=context
              .getMessageEvaluationContext();
      try{
        msgContext.setDestination(destination);
        // lets copy the messages to dispatch to avoid deadlock
        List<QueueMessageReference> messagesToDispatch=new ArrayList<QueueMessageReference>();
        synchronized(pagedInMessages){
          for(Iterator<MessageReference> i=pagedInMessages.iterator();i
                  .hasNext();){
            QueueMessageReference node=(QueueMessageReference)i
                    .next();
            if(node.isDropped()){
              continue;
            }
            String groupID=node.getGroupID();
            // Re-deliver all messages that the sub locked
            if(node.getLockOwner()==sub
                    ||wasExclusiveOwner
                    ||(groupID!=null&&ownedGroups.contains(groupID))){
              messagesToDispatch.add(node);
            }
          }
        }
        // now lets dispatch from the copy of the collection to
        // avoid deadlocks
        for(Iterator<QueueMessageReference> iter=messagesToDispatch
                .iterator();iter.hasNext();){
          QueueMessageReference node=iter.next();
          node.incrementRedeliveryCounter();
          node.unlock();
          msgContext.setMessageReference(node);
          dispatchPolicy.dispatch(node,msgContext,consumers);
        }
      }finally{
        msgContext.clear();
      }
    }
  }

    public void send(final ProducerBrokerExchange producerExchange, final Message message) throws Exception {
        final ConnectionContext context = producerExchange.getConnectionContext();
        // There is delay between the client sending it and it arriving at the
        // destination.. it may have expired.

        final ProducerInfo producerInfo = producerExchange.getProducerState().getInfo();
        final boolean sendProducerAck = !message.isResponseRequired() && producerInfo.getWindowSize() > 0 && !context.isInRecoveryMode();
        if (message.isExpired()) {
            broker.messageExpired(context, message);
            destinationStatistics.getMessages().decrement();
            if (sendProducerAck) {
                ProducerAck ack = new ProducerAck(producerInfo.getProducerId(), message.getSize());
                context.getConnection().dispatchAsync(ack);
            }
            return;
        }
        if (isProducerFlowControl() && context.isProducerFlowControl() && memoryUsage.isFull()) {
            if (systemUsage.isSendFailIfNoSpace()) {
                throw new javax.jms.ResourceAllocationException("SystemUsage memory limit reached");
            }

            // We can avoid blocking due to low usage if the producer is sending
            // a sync message or
            // if it is using a producer window
            if (producerInfo.getWindowSize() > 0 || message.isResponseRequired()) {
                synchronized (messagesWaitingForSpace) {
                    messagesWaitingForSpace.add(new Runnable() {
                        public void run() {

                            try {

                                // While waiting for space to free up... the
                                // message may have expired.
                                if (broker.isExpired(message)) {
                                    broker.messageExpired(context, message);
                                    destinationStatistics.getMessages().decrement();
                                } else {
                                    doMessageSend(producerExchange, message);
                                }

                                if (sendProducerAck) {
                                    ProducerAck ack = new ProducerAck(producerInfo.getProducerId(), message.getSize());
                                    context.getConnection().dispatchAsync(ack);
                                } else {
                                    Response response = new Response();
                                    response.setCorrelationId(message.getCommandId());
                                    context.getConnection().dispatchAsync(response);
                                }

                            } catch (Exception e) {
                                if (!sendProducerAck && !context.isInRecoveryMode()) {
                                    ExceptionResponse response = new ExceptionResponse(e);
                                    response.setCorrelationId(message.getCommandId());
                                    context.getConnection().dispatchAsync(response);
                                }
                            }
                        }
                    });

                    // If the user manager is not full, then the task will not
                    // get called..
                    if (!memoryUsage.notifyCallbackWhenNotFull(sendMessagesWaitingForSpaceTask)) {
                        // so call it directly here.
                        sendMessagesWaitingForSpaceTask.run();
                    }
                    context.setDontSendReponse(true);
                    return;
                }

            } else {

                // Producer flow control cannot be used, so we have do the flow
                // control at the broker
                // by blocking this thread until there is space available.
                while (!memoryUsage.waitForSpace(1000)) {
                    if (context.getStopping().get()) {
                        throw new IOException("Connection closed, send aborted.");
                    }
                }

                // The usage manager could have delayed us by the time
                // we unblock the message could have expired..
                if (message.isExpired()) {
                    if (log.isDebugEnabled()) {
                        log.debug("Expired message: " + message);
                    }
                    return;
                }
            }
        }
        doMessageSend(producerExchange, message);
        if (sendProducerAck) {
            ProducerAck ack = new ProducerAck(producerInfo.getProducerId(), message.getSize());
            context.getConnection().dispatchAsync(ack);
        }
    }

    synchronized void doMessageSend(final ProducerBrokerExchange producerExchange, final Message message) throws IOException, Exception {
        final ConnectionContext context = producerExchange.getConnectionContext();
        message.setRegionDestination(this);
        if (store != null && message.isPersistent()) {
            while (!systemUsage.getStoreUsage().waitForSpace(1000)) {
                if (context.getStopping().get()) {
                    throw new IOException("Connection closed, send aborted.");
                }
            }
            store.addMessage(context, message);
        }
        if (context.isInTransaction()) {
            // If this is a transacted message.. increase the usage now so that
            // a big TX does not blow up
            // our memory. This increment is decremented once the tx finishes..
            message.incrementReferenceCount();
            context.getTransaction().addSynchronization(new Synchronization() {
                public void afterCommit() throws Exception {
                    try {
                        // It could take while before we receive the commit
                        // op, by that time the message could have expired..
                        if (broker.isExpired(message)) {
                            broker.messageExpired(context, message);
                            destinationStatistics.getMessages().decrement();
                            return;
                        }
                        sendMessage(context, message);
                    } finally {
                        message.decrementReferenceCount();
                    }
                }

                @Override
                public void afterRollback() throws Exception {
                    message.decrementReferenceCount();
                }
            });
        } else {
            // Add to the pending list, this takes care of incrementing the
            // usage manager.
            sendMessage(context, message);
        }
    }

    public void dispose(ConnectionContext context) throws IOException {
        if (store != null) {
            store.removeAllMessages(context);
        }
        destinationStatistics.setParent(null);
    }

    public void dropEvent() {
        dropEvent(false);
    }

    public void dropEvent(boolean skipGc) {
        // TODO: need to also decrement when messages expire.
        destinationStatistics.getMessages().decrement();
        synchronized (pagedInMessages) {
            garbageSize++;
        }
        if (!skipGc && garbageSize > garbageSizeBeforeCollection) {
            gc();
        }
        try {
            taskRunner.wakeup();
        } catch (InterruptedException e) {
            log.warn("Task Runner failed to wakeup ", e);
        }
    }

    public void gc() {
        synchronized (pagedInMessages) {
            for (Iterator<MessageReference> i = pagedInMessages.iterator(); i.hasNext();) {
                // Remove dropped messages from the queue.
                QueueMessageReference node = (QueueMessageReference)i.next();
                if (node.isDropped()) {
                    garbageSize--;
                    i.remove();
                    continue;
                }
            }
        }
    }

    public void acknowledge(ConnectionContext context, Subscription sub, MessageAck ack, MessageReference node) throws IOException {
        if (store != null && node.isPersistent()) {
            // the original ack may be a ranged ack, but we are trying to delete
            // a specific
            // message store here so we need to convert to a non ranged ack.
            if (ack.getMessageCount() > 0) {
                // Dup the ack
                MessageAck a = new MessageAck();
                ack.copy(a);
                ack = a;
                // Convert to non-ranged.
                ack.setFirstMessageId(node.getMessageId());
                ack.setLastMessageId(node.getMessageId());
                ack.setMessageCount(1);
            }
            store.removeMessage(context, ack);
        }
    }

    Message loadMessage(MessageId messageId) throws IOException {
        Message msg = store.getMessage(messageId);
        if (msg != null) {
            msg.setRegionDestination(this);
        }
        return msg;
    }

    public String toString() {
        int size = 0;
        synchronized (messages) {
            size = messages.size();
        }
        return "Queue: destination=" + destination.getPhysicalName() + ", subscriptions=" + consumers.size() + ", memory=" + memoryUsage.getPercentUsage() + "%, size=" + size
               + ", in flight groups=" + messageGroupOwners;
    }

    public void start() throws Exception {
        if (memoryUsage != null) {
            memoryUsage.start();
        }
        messages.start();
        doPageIn(false);
    }

    public void stop() throws Exception{
        if (taskRunner != null) {
            taskRunner.shutdown();
        }
        if (messages != null) {
            messages.stop();
        }
        if (memoryUsage != null) {
            memoryUsage.stop();
        }
    }

    // Properties
    // -------------------------------------------------------------------------
    public ActiveMQDestination getActiveMQDestination() {
        return destination;
    }

    public String getDestination() {
        return destination.getPhysicalName();
    }

    public MemoryUsage getBrokerMemoryUsage() {
        return memoryUsage;
    }

    public DestinationStatistics getDestinationStatistics() {
        return destinationStatistics;
    }

    public MessageGroupMap getMessageGroupOwners() {
        if (messageGroupOwners == null) {
            messageGroupOwners = getMessageGroupMapFactory().createMessageGroupMap();
        }
        return messageGroupOwners;
    }

    public DispatchPolicy getDispatchPolicy() {
        return dispatchPolicy;
    }

    public void setDispatchPolicy(DispatchPolicy dispatchPolicy) {
        this.dispatchPolicy = dispatchPolicy;
    }

    public DeadLetterStrategy getDeadLetterStrategy() {
        return deadLetterStrategy;
    }

    public void setDeadLetterStrategy(DeadLetterStrategy deadLetterStrategy) {
        this.deadLetterStrategy = deadLetterStrategy;
    }

    public MessageGroupMapFactory getMessageGroupMapFactory() {
        return messageGroupMapFactory;
    }

    public void setMessageGroupMapFactory(MessageGroupMapFactory messageGroupMapFactory) {
        this.messageGroupMapFactory = messageGroupMapFactory;
    }

    public String getName() {
        return getActiveMQDestination().getPhysicalName();
    }

    public PendingMessageCursor getMessages() {
        return this.messages;
    }

    public void setMessages(PendingMessageCursor messages) {
        this.messages = messages;
    }

    // Implementation methods
    // -------------------------------------------------------------------------
    private MessageReference createMessageReference(Message message) {
        MessageReference result = new IndirectMessageReference(this, store, message);
        result.decrementReferenceCount();
        return result;
    }

    public MessageStore getMessageStore() {
        return store;
    }

    public Message[] browse() {
        List<Message> l = new ArrayList<Message>();
        try {
            doPageIn(true);
        } catch (Exception e) {
            log.error("caught an exception browsing " + this, e);
        }
        synchronized (pagedInMessages) {
            for (Iterator<MessageReference> i = pagedInMessages.iterator(); i.hasNext();) {
                MessageReference r = i.next();
                r.incrementReferenceCount();
                try {
                    Message m = r.getMessage();
                    if (m != null) {
                        l.add(m);
                    }
                } catch (IOException e) {
                    log.error("caught an exception browsing " + this, e);
                } finally {
                    r.decrementReferenceCount();
                }
            }
        }
        synchronized (messages) {
            try {
                messages.reset();
                while (messages.hasNext()) {
                    try {
                        MessageReference r = messages.next();
                        r.incrementReferenceCount();
                        try {
                            Message m = r.getMessage();
                            if (m != null) {
                                l.add(m);
                            }
                        } finally {
                            r.decrementReferenceCount();
                        }
                    } catch (IOException e) {
                        log.error("caught an exception brwsing " + this, e);
                    }
                }
            } finally {
                messages.release();
            }
        }

        return l.toArray(new Message[l.size()]);
    }

    public Message getMessage(String messageId) {
        synchronized (messages) {
            try {
                messages.reset();
                while (messages.hasNext()) {
                    try {
                        MessageReference r = messages.next();
                        if (messageId.equals(r.getMessageId().toString())) {
                            r.incrementReferenceCount();
                            try {
                                Message m = r.getMessage();
                                if (m != null) {
                                    return m;
                                }
                            } finally {
                                r.decrementReferenceCount();
                            }
                            break;
                        }
                    } catch (IOException e) {
                        log.error("got an exception retrieving message " + messageId);
                    }
                }
            } finally {
                messages.release();
            }
        }
        return null;
    }

    public void purge() throws Exception {

        pageInMessages();

        synchronized (pagedInMessages) {
            ConnectionContext c = createConnectionContext();
            for (Iterator<MessageReference> i = pagedInMessages.iterator(); i.hasNext();) {
                try {
                    QueueMessageReference r = (QueueMessageReference)i.next();

                    // We should only delete messages that can be locked.
                    if (r.lock(LockOwner.HIGH_PRIORITY_LOCK_OWNER)) {
                        MessageAck ack = new MessageAck();
                        ack.setAckType(MessageAck.STANDARD_ACK_TYPE);
                        ack.setDestination(destination);
                        ack.setMessageID(r.getMessageId());
                        acknowledge(c, null, ack, r);
                        r.drop();
                        dropEvent(true);
                    }
                } catch (IOException e) {
                }
            }

            // Run gc() by hand. Had we run it in the loop it could be
            // quite expensive.
            gc();
        }
    }

    /**
     * Removes the message matching the given messageId
     */
    public boolean removeMessage(String messageId) throws Exception {
        return removeMatchingMessages(createMessageIdFilter(messageId), 1) > 0;
    }

    /**
     * Removes the messages matching the given selector
     *
     * @return the number of messages removed
     */
    public int removeMatchingMessages(String selector) throws Exception {
        return removeMatchingMessages(selector, -1);
    }

    /**
     * Removes the messages matching the given selector up to the maximum number
     * of matched messages
     *
     * @return the number of messages removed
     */
    public int removeMatchingMessages(String selector, int maximumMessages) throws Exception {
        return removeMatchingMessages(createSelectorFilter(selector), maximumMessages);
    }

    /**
     * Removes the messages matching the given filter up to the maximum number
     * of matched messages
     *
     * @return the number of messages removed
     */
    public int removeMatchingMessages(MessageReferenceFilter filter, int maximumMessages) throws Exception {
        pageInMessages();
        int counter = 0;
        synchronized (pagedInMessages) {
            ConnectionContext c = createConnectionContext();
            for (Iterator<MessageReference> i = pagedInMessages.iterator(); i.hasNext();) {
                IndirectMessageReference r = (IndirectMessageReference)i.next();
                if (filter.evaluate(c, r)) {
                    removeMessage(c, r);
                    if (++counter >= maximumMessages && maximumMessages > 0) {
                        break;
                    }

                }
            }
        }
        return counter;
    }

    /**
     * Copies the message matching the given messageId
     */
    public boolean copyMessageTo(ConnectionContext context, String messageId, ActiveMQDestination dest) throws Exception {
        return copyMatchingMessages(context, createMessageIdFilter(messageId), dest, 1) > 0;
    }

    /**
     * Copies the messages matching the given selector
     *
     * @return the number of messages copied
     */
    public int copyMatchingMessagesTo(ConnectionContext context, String selector, ActiveMQDestination dest) throws Exception {
        return copyMatchingMessagesTo(context, selector, dest, -1);
    }

    /**
     * Copies the messages matching the given selector up to the maximum number
     * of matched messages
     *
     * @return the number of messages copied
     */
    public int copyMatchingMessagesTo(ConnectionContext context, String selector, ActiveMQDestination dest, int maximumMessages) throws Exception {
        return copyMatchingMessages(context, createSelectorFilter(selector), dest, maximumMessages);
    }

    /**
     * Copies the messages matching the given filter up to the maximum number of
     * matched messages
     *
     * @return the number of messages copied
     */
    public int copyMatchingMessages(ConnectionContext context, MessageReferenceFilter filter, ActiveMQDestination dest, int maximumMessages) throws Exception {
        pageInMessages();
        int counter = 0;
        synchronized (pagedInMessages) {
            for (Iterator<MessageReference> i = pagedInMessages.iterator(); i.hasNext();) {
                MessageReference r = i.next();
                if (filter.evaluate(context, r)) {
                    r.incrementReferenceCount();
                    try {
                        Message m = r.getMessage();
                        BrokerSupport.resend(context, m, dest);
                        if (++counter >= maximumMessages && maximumMessages > 0) {
                            break;
                        }
                    } finally {
                        r.decrementReferenceCount();
                    }
                }
            }
        }
        return counter;
    }

    /**
     * Moves the message matching the given messageId
     */
    public boolean moveMessageTo(ConnectionContext context, String messageId, ActiveMQDestination dest) throws Exception {
        return moveMatchingMessagesTo(context, createMessageIdFilter(messageId), dest, 1) > 0;
    }

    /**
     * Moves the messages matching the given selector
     *
     * @return the number of messages removed
     */
    public int moveMatchingMessagesTo(ConnectionContext context, String selector, ActiveMQDestination dest) throws Exception {
        return moveMatchingMessagesTo(context, selector, dest, -1);
    }

    /**
     * Moves the messages matching the given selector up to the maximum number
     * of matched messages
     */
    public int moveMatchingMessagesTo(ConnectionContext context, String selector, ActiveMQDestination dest, int maximumMessages) throws Exception {
        return moveMatchingMessagesTo(context, createSelectorFilter(selector), dest, maximumMessages);
    }

    /**
     * Moves the messages matching the given filter up to the maximum number of
     * matched messages
     */
    public int moveMatchingMessagesTo(ConnectionContext context, MessageReferenceFilter filter, ActiveMQDestination dest, int maximumMessages) throws Exception {
        pageInMessages();
        int counter = 0;
        synchronized (pagedInMessages) {
            for (Iterator<MessageReference> i = pagedInMessages.iterator(); i.hasNext();) {
                IndirectMessageReference r = (IndirectMessageReference)i.next();
                if (filter.evaluate(context, r)) {
                    // We should only move messages that can be locked.
                    if (lockMessage(r)) {
                        r.incrementReferenceCount();
                        try {
                            Message m = r.getMessage();
                            BrokerSupport.resend(context, m, dest);
                            removeMessage(context, r);
                            if (++counter >= maximumMessages && maximumMessages > 0) {
                                break;
                            }
                        } finally {
                            r.decrementReferenceCount();
                        }
                    }
                }
            }
        }
        return counter;
    }

    /**
     * @return
     * @see org.apache.activemq.thread.Task#iterate()
     */
    public boolean iterate() {

        while (!memoryUsage.isFull() && !messagesWaitingForSpace.isEmpty()) {
            Runnable op = messagesWaitingForSpace.removeFirst();
            op.run();
        }

        try {
            pageInMessages(false);
        } catch (Exception e) {
            log.error("Failed to page in more queue messages ", e);
        }
        return false;
    }

    protected MessageReferenceFilter createMessageIdFilter(final String messageId) {
        return new MessageReferenceFilter() {
            public boolean evaluate(ConnectionContext context, MessageReference r) {
                return messageId.equals(r.getMessageId().toString());
            }
        };
    }

    protected MessageReferenceFilter createSelectorFilter(String selector) throws InvalidSelectorException {
        final BooleanExpression selectorExpression = new SelectorParser().parse(selector);

        return new MessageReferenceFilter() {
            public boolean evaluate(ConnectionContext context, MessageReference r) throws JMSException {
                MessageEvaluationContext messageEvaluationContext = context.getMessageEvaluationContext();

                messageEvaluationContext.setMessageReference(r);
                if (messageEvaluationContext.getDestination() == null) {
                    messageEvaluationContext.setDestination(getActiveMQDestination());
                }

                return selectorExpression.matches(messageEvaluationContext);
            }
        };
    }

    protected void removeMessage(ConnectionContext c, IndirectMessageReference r) throws IOException {
        MessageAck ack = new MessageAck();
        ack.setAckType(MessageAck.STANDARD_ACK_TYPE);
        ack.setDestination(destination);
        ack.setMessageID(r.getMessageId());
        acknowledge(c, null, ack, r);
        r.drop();
        dropEvent();
    }

    protected boolean lockMessage(IndirectMessageReference r) {
        return r.lock(LockOwner.HIGH_PRIORITY_LOCK_OWNER);
    }

    protected ConnectionContext createConnectionContext() {
        ConnectionContext answer = new ConnectionContext();
        answer.getMessageEvaluationContext().setDestination(getActiveMQDestination());
        return answer;
    }

    final void sendMessage(final ConnectionContext context, Message msg) throws Exception {
        synchronized (messages) {
            messages.addMessageLast(msg);
        }
        destinationStatistics.getEnqueues().increment();
        destinationStatistics.getMessages().increment();
        pageInMessages(false);
    }

   
    private   List<MessageReference> doPageIn(boolean force) throws Exception {
       List<MessageReference> result  = null;
        result  = buildList(force);
      return  result;
    }

    private   synchronized List<MessageReference> buildList(boolean force) throws Exception {

        final int toPageIn = maximumPagedInMessages - pagedInMessages.size();
        List<MessageReference> result = null;
        if ((force || !consumers.isEmpty()) && toPageIn > 0) {
            messages.setMaxBatchSize(toPageIn);
            try {
                int count = 0;
                result = new ArrayList<MessageReference>(toPageIn);
                synchronized (messages) {

                    try {
                        messages.reset();
                        while (messages.hasNext() && count < toPageIn) {
                            MessageReference node = messages.next();
                            messages.remove();
                            if (!broker.isExpired(node)) {
                                node = createMessageReference(node.getMessage());
                                result.add(node);
                                count++;
                            } else {
                                broker.messageExpired(createConnectionContext(), node);
                                destinationStatistics.getMessages().decrement();
                            }
                        }
                    } finally {
                        messages.release();
                    }
                }
                synchronized (pagedInMessages) {
                    pagedInMessages.addAll(result);
                }
            } finally {
                queueMsgConext.clear();
            }
        }
        return result;
    }

    private  synchronized void doDispatch(List<MessageReference> list) throws Exception {
        if (list != null && !list.isEmpty()) {
            try {
                for (int i = 0; i < list.size(); i++) {
                    MessageReference node = list.get(i);
                    queueMsgConext.setDestination(destination);
                    queueMsgConext.setMessageReference(node);
                    dispatchPolicy.dispatch(node, queueMsgConext, consumers);
                }
            } finally {
                queueMsgConext.clear();
            }
        }
    }

    private void pageInMessages() throws Exception {
        pageInMessages(true);
    }

    private void pageInMessages(boolean force) throws Exception {
            doDispatch(doPageIn(force));
    }

}
TOP

Related Classes of org.apache.activemq.broker.region.Queue

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.