Package org.activemq.service.impl

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

/**
* 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.InvalidDestinationException;
import javax.jms.JMSException;
import javax.jms.Topic;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.activemq.DuplicateDurableSubscriptionException;
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.ActiveMQTopic;
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.RedeliveryPolicy;
import org.activemq.service.Subscription;
import org.activemq.service.SubscriptionContainer;
import org.activemq.service.TopicMessageContainer;
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 Topic messages for durable consumers
*
* @version $Revision: 1.1.1.1 $
*/
public class DurableTopicMessageContainerManager extends MessageContainerManagerSupport {
    private static final Log log = LogFactory.getLog(DurableTopicMessageContainerManager.class);
    private PersistenceAdapter persistenceAdapter;
    protected SubscriptionContainer subscriptionContainer;
    protected FilterFactory filterFactory;
    protected Map activeSubscriptions = new ConcurrentHashMap();
    private DestinationMap destinationMap = new DestinationMap();
    private ConcurrentHashMap durableSubscriptions = new ConcurrentHashMap();
   
    public DurableTopicMessageContainerManager(PersistenceAdapter persistenceAdapter, RedeliveryPolicy redeliveryPolicy,DeadLetterPolicy deadLetterPolicy) {
        this(persistenceAdapter, new DurableTopicSubscriptionContainerImpl(redeliveryPolicy,deadLetterPolicy), new FilterFactoryImpl(),
                new DispatcherImpl());
    }

    public DurableTopicMessageContainerManager(PersistenceAdapter persistenceAdapter,
            SubscriptionContainer subscriptionContainer, FilterFactory filterFactory, Dispatcher dispatcher) {
        super(dispatcher);
        this.persistenceAdapter = persistenceAdapter;
        this.subscriptionContainer = subscriptionContainer;
        this.filterFactory = filterFactory;
        try {
            loadAllMessageContainers();
        }
        catch (JMSException e) {
            log.error("Failed to load initial Topic Containers",e);
        }
    }

    public void addMessageConsumer(BrokerClient client, ConsumerInfo info) throws JMSException {
        if (info.isDurableTopic()) {
            if (log.isDebugEnabled()) {
                log.debug("Adding consumer: " + info);
            }

            doAddMessageConsumer(client, info);
        }
    }

    public void removeMessageConsumer(BrokerClient client, ConsumerInfo info) throws JMSException {
        // we should not remove a durable topic subscription from the subscriptionContainer
        // unless via the deleteSubscription() method
       
//        subscriptionContainer.removeSubscription(info.getConsumerId());
//        subscription.clear();
       
        Subscription sub = (Subscription) activeSubscriptions.remove(info.getConsumerId());
        if (sub != null) {
            sub.setActive(false);
            dispatcher.removeActiveSubscription(client, sub);
        }
    }

    /**
     * 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 {
       
        String consumerKey = ConsumerInfo.generateConsumerKey(clientId, subscriberName);       
        Subscription sub = (Subscription) durableSubscriptions.remove(consumerKey);
        if( sub!=null ) {
            //only delete if not active
            if (sub.isActive()) {
                throw new JMSException("The Consummer " + subscriberName + " is still active");
            }
            else {
                subscriptionContainer.removeSubscription(sub.getConsumerId());
                sub.clear();
               
                Set containers = destinationMap.get(sub.getDestination());
                for (Iterator iter = containers.iterator();iter.hasNext();) {
                    TopicMessageContainer container = (TopicMessageContainer) iter.next();
                    if (container instanceof DurableTopicMessageContainer) {
                        ((DurableTopicMessageContainer) container).deleteSubscription(sub.getPersistentKey());
                    }
                }
               
            }
        } else {
            throw new InvalidDestinationException("The Consumer " + subscriberName + " does not exist for client: "
                    + clientId);
        }
    }

    /**
     * @param client
     * @param message
     * @throws javax.jms.JMSException
     */
    public void sendMessage(final BrokerClient client, final ActiveMQMessage message) throws JMSException {
        ActiveMQDestination dest = (ActiveMQDestination) message.getJMSDestination();
        if (dest != null && dest.isTopic() && message.getJMSDeliveryMode() == DeliveryMode.PERSISTENT) {
            // note that we still need to persist the message even if there are no matching
            // subscribers as they may come along later
            // plus we don't pre-load subscription information               
            final MessageContainer container = getContainer(dest.getPhysicalName());
            container.addMessage(message);
            TransactionManager.getContexTransaction().addPostCommitTask(new TransactionTask() {
                public void execute() throws Throwable {
                    doSendMessage(client, message, container);
                }
            });
        }
    }

    /**
     * @param client
     * @param message
     * @throws JMSException
     */
    private void doSendMessage(BrokerClient client, ActiveMQMessage message, MessageContainer container) throws JMSException {
        Set matchingSubscriptions = subscriptionContainer.getSubscriptions(message.getJMSActiveMQDestination());
        if (!matchingSubscriptions.isEmpty()) {
            for (Iterator i = matchingSubscriptions.iterator();i.hasNext();) {
                Subscription sub = (Subscription) i.next();
                if (sub.isTarget(message)) {
                    sub.addMessage(container, message);
                }
            }
            updateSendStats(client, message);
        }       
    }

    /**
     * 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);
    }

    /**
     * Acknowledge a message as being read and consumed byh the Consumer
     *
     * @param client
     * @param ack
     * @throws javax.jms.JMSException
     */
    public void acknowledgeMessage(BrokerClient client, final MessageAck ack) throws JMSException {
        if ( !ack.getDestination().isTopic() || !ack.isPersistent())
            return;
           
        Subscription sub = (Subscription) activeSubscriptions.get(ack.getConsumerId());
        if (sub == null) {
            return;
        }
       
        sub.messageConsumed(ack);
    }

    /**
     * poll or messages
     *
     * @throws javax.jms.JMSException
     */
    public void poll() throws JMSException {
        //do nothing
    }

    // Implementation methods
    //-------------------------------------------------------------------------
    protected MessageContainer createContainer(String destinationName) throws JMSException {
        TopicMessageContainer topicMessageContainer =
           new DurableTopicMessageContainer(this, persistenceAdapter.createTopicMessageStore(destinationName), destinationName);
        destinationMap.put(new ActiveMQTopic(destinationName), topicMessageContainer);
        return topicMessageContainer;
    }

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

    public boolean isConsumerActiveOnDestination(ActiveMQDestination dest) {
        for (Iterator iter = activeSubscriptions.values().iterator();iter.hasNext();) {
            Subscription subscription = (Subscription) iter.next();
            if( subscription.getDestination().equals(dest) ) {
                return true;
            }
        }
        return false;
    }
   
    protected void doAddMessageConsumer(BrokerClient client, ConsumerInfo info) throws JMSException {
        boolean shouldRecover = false;
        if (info.getConsumerName() != null && info.getClientId() != null) {
            for (Iterator iter = activeSubscriptions.values().iterator();iter.hasNext();) {
                Subscription subscription = (Subscription) iter.next();
                if (subscription.isSameDurableSubscription(info)) {
                    throw new DuplicateDurableSubscriptionException(info);
                }
            }
        }       
        Subscription subscription = (Subscription) durableSubscriptions.get(info.getConsumerKey())
        //subscriptionContainer.getSubscription(info.getConsumerId());
        if (subscription != null) {
            //check the subscription hasn't changed
            if (!equal(subscription.getDestination(), info.getDestination())
                    || !equal(subscription.getSelector(), info.getSelector())) {
                subscriptionContainer.removeSubscription(info.getConsumerId());
                subscription.clear();
                subscription = subscriptionContainer.makeSubscription(dispatcher, client, info, createFilter(info));
                durableSubscriptions.put(info.getConsumerKey(), subscription);
            }
        }
        else {
            subscription = subscriptionContainer.makeSubscription(dispatcher, client,info, createFilter(info));
            shouldRecover = true;
            durableSubscriptions.put(info.getConsumerKey(), subscription);
        }
        subscription.setActiveConsumer(client,info);
        activeSubscriptions.put(info.getConsumerId(), subscription);
        dispatcher.addActiveSubscription(client, subscription);
       
        // load the container
        getContainer(subscription.getDestination().getPhysicalName());
       
        Set containers = destinationMap.get(subscription.getDestination());
        for (Iterator iter = containers.iterator();iter.hasNext();) {
            TopicMessageContainer container = (TopicMessageContainer) iter.next();
            if (container instanceof DurableTopicMessageContainer) {
                ((DurableTopicMessageContainer) container).storeSubscription(info, subscription);
            }
        }
        if (shouldRecover) {
            recoverSubscriptions(subscription);
        }
        // lets not make the subscription active until later
        // as we can't start dispatching until we've sent back the receipt
        // TODO we might wish to register a post-receipt action here
        // to perform the wakeup
        subscription.setActive(true);
        //dispatcher.wakeup(subscription);
    }

    /**
     * Returns true if the two objects are null or are equal
     */
    protected final boolean equal(Object o1, Object o2) {
        return o1 == o2 || (o1 != null && o1.equals(o2));
    }

    /**
     * This method is called when a new durable subscription is started and so we need to go through each matching
     * message container and dispatch any matching messages that may be outstanding
     *
     * @param subscription
     */
    protected void recoverSubscriptions(Subscription subscription) throws JMSException {
        // we should load all of the message containers from disk if we're a wildcard
       
        getContainer(subscription.getDestination().getPhysicalName());
        Set containers = destinationMap.get(subscription.getDestination());
        for (Iterator iter = containers.iterator();iter.hasNext();) {
            TopicMessageContainer container = (TopicMessageContainer) iter.next();
            container.recoverSubscription(subscription);
        }
    }

    /**
     * Called when recovering a wildcard subscription where we need to load all the durable message containers (for
     * which we have any outstanding messages to deliver) into RAM
     */
    protected void loadAllMessageContainers() throws JMSException {
        Map destinations = persistenceAdapter.getInitialDestinations();
        if (destinations != null) {
            for (Iterator iter = destinations.entrySet().iterator();iter.hasNext();) {
                Map.Entry entry = (Map.Entry) iter.next();
                String name = (String) entry.getKey();
                Destination destination = (Destination) entry.getValue();
                if ( destination instanceof Topic ) {
                    loadContainer(name, destination);
                }
            }
        }
    }

    /**
     * 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 topics.
        if(!dest.isTopic())
            return;
        super.createMessageContainer(dest);
    }
   
    public synchronized void destroyMessageContainer(ActiveMQDestination dest) throws JMSException {
        // This container only does topics.
        if(!dest.isTopic())
            return;
        super.destroyMessageContainer(dest);
        destinationMap.removeAll(dest);
    }
}
TOP

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

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.