Package org.activemq.service.impl

Source Code of org.activemq.service.impl.DurableQueueMessageContainerManager

/**
*
* Copyright 2004 Protique Ltd
*
* Licensed 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.activemq.service.impl;

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.JMSException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.activemq.broker.BrokerClient;
import org.activemq.filter.AndFilter;
import org.activemq.filter.DestinationMap;
import org.activemq.filter.Filter;
import org.activemq.filter.FilterFactory;
import org.activemq.filter.FilterFactoryImpl;
import org.activemq.filter.NoLocalFilter;
import org.activemq.message.ActiveMQDestination;
import org.activemq.message.ActiveMQMessage;
import org.activemq.message.ActiveMQQueue;
import org.activemq.message.ConsumerInfo;
import org.activemq.message.MessageAck;
import org.activemq.service.DeadLetterPolicy;
import org.activemq.service.Dispatcher;
import org.activemq.service.MessageContainer;
import org.activemq.service.QueueList;
import org.activemq.service.QueueListEntry;
import org.activemq.service.QueueMessageContainer;
import org.activemq.service.QueueMessageContainerManager;
import org.activemq.service.RedeliveryPolicy;
import org.activemq.service.Subscription;
import org.activemq.service.SubscriptionContainer;
import org.activemq.service.TransactionManager;
import org.activemq.service.TransactionTask;
import org.activemq.store.PersistenceAdapter;

import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;

/**
* A default Broker used for Queue messages
*
* @version $Revision: 1.1.1.1 $
*/
public class DurableQueueMessageContainerManager extends MessageContainerManagerSupport implements QueueMessageContainerManager {
    private static final Log log = LogFactory.getLog(DurableQueueMessageContainerManager.class);
    private static final int MAX_MESSAGES_DISPATCHED_FROM_POLL = 50;

    private PersistenceAdapter persistenceAdapter;
    protected SubscriptionContainer subscriptionContainer;
    protected FilterFactory filterFactory;
    protected Map activeSubscriptions = new ConcurrentHashMap();
    protected Map browsers = new ConcurrentHashMap();
    protected Map messagePartSubscribers = new ConcurrentHashMap();
    protected DestinationMap destinationMap = new DestinationMap();
    private Object subscriptionMutex = new Object();
   
  
  

    public DurableQueueMessageContainerManager(PersistenceAdapter persistenceAdapter, RedeliveryPolicy redeliveryPolicy,DeadLetterPolicy deadLetterPolicy) {
        this(persistenceAdapter, new SubscriptionContainerImpl(redeliveryPolicy,deadLetterPolicy), new FilterFactoryImpl(), new DispatcherImpl());
    }

    public DurableQueueMessageContainerManager(PersistenceAdapter persistenceAdapter, SubscriptionContainer subscriptionContainer, FilterFactory filterFactory, Dispatcher dispatcher) {
        super(dispatcher);
        this.persistenceAdapter = persistenceAdapter;
        this.subscriptionContainer = subscriptionContainer;
        this.filterFactory = filterFactory;
    }

    /**
     * Answers true if this ContainerManager is interested in managing the destination.
     *
   * @param destination
     * @param b
   * @return
   */
  private boolean isManagerFor(ActiveMQDestination destination) {
    return destination!=null && destination.isQueue() && !destination.isTemporary();
  }

  /**
     * Answers true if this ContainerManager is interested in handing a operation of
     * on the provided destination.  persistentOp is true when the opperation is persistent.
     *
   * @param destination
   * @param persistentOp
     * @param b
   * @return
   */
  private boolean isManagerFor(ActiveMQDestination destination, boolean persistentOp) {
    // We are going to handle both persistent and non persistent operations for now.
    return isManagerFor(destination) && persistentOp;
  }

    /**
     * Returns an unmodifiable map, indexed by String name, of all the {@link javax.jms.Destination}
     * objects used by non-broker consumers directly connected to this container
     *
     * @return
     */
    public Map getLocalDestinations() {
        Map localDestinations = new HashMap();
        for (Iterator iter = subscriptionContainer.subscriptionIterator(); iter.hasNext();) {
            Subscription sub = (Subscription) iter.next();
            if (sub.isLocalSubscription()) {
                final ActiveMQDestination dest = sub.getDestination();
                localDestinations.put(dest.getPhysicalName(), dest);
            }
        }
        return Collections.unmodifiableMap(localDestinations);
    }

  /**
     * @param client
     * @param info
     * @throws javax.jms.JMSException
     */
    public void addMessageConsumer(BrokerClient client, ConsumerInfo info) throws JMSException {
     
      // Are we not intrested in handling that destination?
        if( !isManagerFor(info.getDestination()) ) {
          return;
        }

        if (log.isDebugEnabled()) {
            log.debug("Adding consumer: " + info);
        }
         
        //ensure a matching container exists for the destination
        getContainer(info.getDestination().getPhysicalName());
       
        Subscription sub = subscriptionContainer.makeSubscription(dispatcher, client,info, createFilter(info));
        dispatcher.addActiveSubscription(client, sub);
        updateActiveSubscriptions(sub);

        // set active last in case we end up dispatching some messages
        // while recovering
        sub.setActive(true);
    }

  /**
     * @param client
     * @param info
     * @throws javax.jms.JMSException
     */
    public void removeMessageConsumer(BrokerClient client, ConsumerInfo info) throws JMSException {
        if (log.isDebugEnabled()) {
            log.debug("Removing consumer: " + info);
        }
        if (info.getDestination() != null && info.getDestination().isQueue()) {
            synchronized (subscriptionMutex) {
                Subscription sub = (Subscription) subscriptionContainer.removeSubscription(info.getConsumerId());
                if (sub != null) {
                    sub.setActive(false);
                    sub.clear();//resets entries in the QueueMessageContainer
                    dispatcher.removeActiveSubscription(client, sub);
                    //need to do wildcards for this - but for now use exact matches
                    for (Iterator iter = messageContainers.values().iterator(); iter.hasNext();) {
                        QueueMessageContainer container = (QueueMessageContainer) iter.next();
                        //should change this for wild cards ...
                        if (container.getDestinationName().equals(sub.getDestination().getPhysicalName())) {
                            QueueList list = getSubscriptionList(container);
                            list.remove(sub);
                            if (list.isEmpty()) {
                                activeSubscriptions.remove(sub.getDestination().getPhysicalName());
                            }
                            list = getBrowserList(container);
                            list.remove(sub);
                            if (list.isEmpty()) {
                                browsers.remove(sub.getDestination().getPhysicalName());
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Delete a durable subscriber
     *
     * @param clientId
     * @param subscriberName
     * @throws javax.jms.JMSException if the subscriber doesn't exist or is still active
     */
    public void deleteSubscription(String clientId, String subscriberName) throws JMSException {
    }

    /**
     * @param client
     * @param message
     * @throws javax.jms.JMSException
     */
    public void sendMessage(final BrokerClient client, final ActiveMQMessage message) throws JMSException {

        ActiveMQDestination dest = (ActiveMQDestination) message.getJMSDestination();
      // Are we not intrested in handling that destination?
        if( !isManagerFor(dest, message.getJMSDeliveryMode()==DeliveryMode.PERSISTENT) ) {
          return;
        }
       
        if (log.isDebugEnabled()) {
            log.debug("Dispaching message: " + message);
        }
        //ensure a matching container exists for the destination
        getContainer(((ActiveMQDestination) message.getJMSDestination()).getPhysicalName());
        Set set = destinationMap.get(message.getJMSActiveMQDestination());
        for (Iterator i = set.iterator();i.hasNext();) {
            QueueMessageContainer container = (QueueMessageContainer) i.next();
            container.addMessage(message);
            // Once transaction has completed.. dispatch the message.
            TransactionManager.getContexTransaction().addPostCommitTask(new TransactionTask(){
                public void execute() throws Throwable {
                    dispatcher.wakeup();
                    updateSendStats(client, message);
                }
            });     
           
        }
    }

    /**
     * Acknowledge a message as being read and consumed by the Consumer
     *
     * @param client
     * @param ack
     * @throws javax.jms.JMSException
     */
    public void acknowledgeMessage(final BrokerClient client, final MessageAck ack) throws JMSException {
        // Are we not intrested in handling that destination?
        if( !isManagerFor(ack.getDestination(), ack.isPersistent()) ) {
          return;
        }
        final Subscription sub = subscriptionContainer.getSubscription(ack.getConsumerId());
        if (sub == null){
            return;
        }

        sub.messageConsumed(ack);
        if (ack.isMessageRead()) {
            updateAcknowledgeStats(client, sub);
        }
    }
   
    /**
     * Poll for messages
     *
     * @throws javax.jms.JMSException
     */
    public void poll() throws JMSException {
        synchronized (subscriptionMutex) {
            for (Iterator iter = activeSubscriptions.keySet().iterator(); iter.hasNext();) {
                QueueMessageContainer container = (QueueMessageContainer) iter.next();

                QueueList browserList = (QueueList) browsers.get(container);
                doPeek(container, browserList);
                QueueList list = (QueueList) activeSubscriptions.get(container);
                doPoll(container, list);
            }
        }
    }

    public MessageContainer getContainer(String destinationName) throws JMSException {
        MessageContainer container = (MessageContainer) messageContainers.get(destinationName);
        if (container == null) {
            synchronized (subscriptionMutex) {
                container = super.getContainer(destinationName);
            }
        }
        return container;
    }

    // Implementation methods
    //-------------------------------------------------------------------------

    protected MessageContainer createContainer(String destinationName) throws JMSException {
        QueueMessageContainer container = new DurableQueueMessageContainer(persistenceAdapter, persistenceAdapter.createQueueMessageStore(destinationName), destinationName);
       
        //Add any interested Subscriptions to the new Container
        for (Iterator iter = subscriptionContainer.subscriptionIterator(); iter.hasNext();) {
            Subscription sub = (Subscription) iter.next();
            if (sub.isBrowser()) {
                updateBrowsers(container, sub);
            }
            else {
                updateActiveSubscriptions(container, sub);
            }
        }
       
        ActiveMQDestination key = new ActiveMQQueue(destinationName);
        destinationMap.put(key, container);
        return container;
    }

    protected Destination createDestination(String destinationName) {
        return new ActiveMQQueue(destinationName);
    }

    private void doPeek(QueueMessageContainer container, QueueList browsers) throws JMSException {
        if (browsers != null && browsers.size() > 0) {
            for (int i = 0; i < browsers.size(); i++) {
                SubscriptionImpl sub = (SubscriptionImpl) browsers.get(i);
                int count = 0;
                ActiveMQMessage msg = null;
                do {
                    msg = container.peekNext(sub.getLastMessageIdentity());
                    if (msg != null) {
                        if (sub.isTarget(msg)) {
                            System.out.println("browser dispatch: "+msg.getJMSMessageID());
                            sub.addMessage(container, msg);
                            dispatcher.wakeup(sub);
                        }
                        else {
                            sub.setLastMessageIdentifier(msg.getJMSMessageIdentity());
                        }
                    }
                }
                while (msg != null && !sub.isAtPrefetchLimit() && count++ < MAX_MESSAGES_DISPATCHED_FROM_POLL);
            }
        }
    }

    private void doPoll(QueueMessageContainer container, QueueList subList) throws JMSException {
        int count = 0;
        ActiveMQMessage msg = null;
        if (subList != null && subList.size() > 0) {
            do {
                boolean dispatched = false;
                msg = container.poll();
                if (msg != null) {
                    QueueListEntry entry = subList.getFirstEntry();
                    boolean targeted = false;
                    while (entry != null) {
                        SubscriptionImpl sub = (SubscriptionImpl) entry.getElement();
                        if (sub.isTarget(msg)) {
                            targeted = true;
                            if (msg.isMessagePart()){
                                SubscriptionImpl sameTarget = (SubscriptionImpl)messagePartSubscribers.get(msg.getParentMessageID());
                                if (sameTarget == null){
                                    sameTarget = sub;
                                    messagePartSubscribers.put(msg.getParentMessageID(), sameTarget);
                                }
                                sameTarget.addMessage(container,msg);
                                if (msg.isLastMessagePart()){
                                    messagePartSubscribers.remove(msg.getParentMessageID());
                                }
                                dispatched = true;
                                dispatcher.wakeup(sameTarget);
                                break;
                            }else if (!sub.isAtPrefetchLimit()) {
                                System.out.println("dispatching: "+msg.getJMSMessageID());
                                sub.addMessage(container, msg);
                                dispatched = true;
                                dispatcher.wakeup(sub);
                                subList.rotate(); //round-robin the list
                                break;
                            }
                        }
                        entry = subList.getNextEntry(entry);
                    }
                    if (!dispatched) {
                        if (targeted) { //ie. it can be selected by current active consumers - but they are at
                            // pre-fectch
                            // limit
                            container.returnMessage(msg.getJMSMessageIdentity());
                        }
                        break;
                    }
                }
            }
            while (msg != null && count++ < MAX_MESSAGES_DISPATCHED_FROM_POLL);
        }
    }

    private void updateActiveSubscriptions(Subscription subscription) throws JMSException {
        //need to do wildcards for this - but for now use exact matches
        synchronized (subscriptionMutex) {
            boolean processedSubscriptionContainer = false;

            String subscriptionPhysicalName = subscription.getDestination().getPhysicalName();
            for (Iterator iter = messageContainers.entrySet().iterator(); iter.hasNext();) {
                Map.Entry entry = (Map.Entry) iter.next();
                String destinationName = (String) entry.getKey();
                QueueMessageContainer container = (QueueMessageContainer) entry.getValue();

                if (destinationName.equals(subscriptionPhysicalName)) {
                    processedSubscriptionContainer = true;
                }
                processSubscription(subscription, container);
            }
            if (!processedSubscriptionContainer) {
                processSubscription(subscription, (QueueMessageContainer) getContainer(subscriptionPhysicalName));
            }
        }
    }

    protected void processSubscription(Subscription subscription, QueueMessageContainer container) throws JMSException {
        // TODO should change this for wild cards ...
        if (subscription.isBrowser()) {
            updateBrowsers(container, subscription);
        }
        else {
            updateActiveSubscriptions(container, subscription);
        }
    }

    private void updateActiveSubscriptions(QueueMessageContainer container, Subscription sub) throws JMSException {
        //need to do wildcards for this - but for now use exact matches
        //should change this for wild cards ...
        if (container.getDestinationName().equals(sub.getDestination().getPhysicalName())) {
            container.reset();//reset container - flushing all filter out messages to new consumer
            QueueList list = getSubscriptionList(container);
            if (!list.contains(sub)) {
                list.add(sub);
            }
        }
    }

    private QueueList getSubscriptionList(QueueMessageContainer container) {
        QueueList list = (QueueList) activeSubscriptions.get(container);
        if (list == null) {
            list = new DefaultQueueList();
            activeSubscriptions.put(container, list);
        }
        return list;
    }

    private void updateBrowsers(QueueMessageContainer container, Subscription sub) throws JMSException {
        //need to do wildcards for this - but for now use exact matches
        //should change this for wild cards ...
        if (container.getDestinationName().equals(sub.getDestination().getPhysicalName())) {
            container.reset();//reset container - flushing all filter out messages to new consumer
            QueueList list = getBrowserList(container);
            if (!list.contains(sub)) {
                list.add(sub);
            }
        }
    }

    private QueueList getBrowserList(QueueMessageContainer container) {
        QueueList list = (QueueList) browsers.get(container);
        if (list == null) {
            list = new DefaultQueueList();
            browsers.put(container, list);
        }
        return list;
    }

    /**
     * Create filter for a Consumer
     *
     * @param info
     * @return the Fitler
     * @throws javax.jms.JMSException
     */
    protected Filter createFilter(ConsumerInfo info) throws JMSException {
        Filter filter = filterFactory.createFilter(info.getDestination(), info.getSelector());
        if (info.isNoLocal()) {
            filter = new AndFilter(filter, new NoLocalFilter(info.getClientId()));
        }
        return filter;
    }

    public void createMessageContainer(ActiveMQDestination dest) throws JMSException {
        // This container only does queues.
        if(!dest.isQueue())
            return;
        super.createMessageContainer(dest);
    }
   
    public synchronized void destroyMessageContainer(ActiveMQDestination dest) throws JMSException {
        // This container only does queues.
        if(!dest.isQueue())
            return;
        super.destroyMessageContainer(dest);
        destinationMap.removeAll(dest);
    }
   
    /**
     * Add a message to a dead letter queue
     * @param deadLetterName
     * @param message
     * @throws JMSException
     */
    public void sendToDeadLetterQueue(String deadLetterName,ActiveMQMessage message) throws JMSException{
        QueueMessageContainer container = (QueueMessageContainer)getContainer(deadLetterName);
        container.setDeadLetterQueue(true);
        container.addMessage(message);
        dispatcher.wakeup();
    }
}
TOP

Related Classes of org.activemq.service.impl.DurableQueueMessageContainerManager

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.