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.network;
018
019import java.io.IOException;
020
021import org.apache.activemq.broker.region.RegionBroker;
022import org.apache.activemq.broker.region.Subscription;
023import org.apache.activemq.broker.region.TopicRegion;
024import org.apache.activemq.command.ActiveMQDestination;
025import org.apache.activemq.command.ConsumerId;
026import org.apache.activemq.command.ConsumerInfo;
027import org.apache.activemq.command.RemoveSubscriptionInfo;
028import org.apache.activemq.filter.DestinationFilter;
029import org.apache.activemq.transport.Transport;
030import org.apache.activemq.util.NetworkBridgeUtils;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034/**
035 * Consolidates subscriptions
036 */
037public class DurableConduitBridge extends ConduitBridge {
038    private static final Logger LOG = LoggerFactory.getLogger(DurableConduitBridge.class);
039
040    @Override
041    public String toString() {
042        return "DurableConduitBridge:" + configuration.getBrokerName() + "->" + getRemoteBrokerName();
043    }
044    /**
045     * Constructor
046     *
047     * @param configuration
048     *
049     * @param localBroker
050     * @param remoteBroker
051     */
052    public DurableConduitBridge(NetworkBridgeConfiguration configuration, Transport localBroker,
053                                Transport remoteBroker) {
054        super(configuration, localBroker, remoteBroker);
055    }
056
057    /**
058     * Subscriptions for these destinations are always created
059     *
060     */
061    @Override
062    protected void setupStaticDestinations() {
063        super.setupStaticDestinations();
064        ActiveMQDestination[] dests = configuration.isDynamicOnly() ? null : durableDestinations;
065        if (dests != null) {
066            for (ActiveMQDestination dest : dests) {
067                if (isPermissableDestination(dest) && !doesConsumerExist(dest)) {
068                    try {
069                        //Filtering by non-empty subscriptions, see AMQ-5875
070                        if (dest.isTopic()) {
071                            RegionBroker regionBroker = (RegionBroker) brokerService.getRegionBroker();
072                            TopicRegion topicRegion = (TopicRegion) regionBroker.getTopicRegion();
073
074                            String candidateSubName = getSubscriberName(dest);
075                            for (Subscription subscription : topicRegion.getDurableSubscriptions().values()) {
076                                String subName = subscription.getConsumerInfo().getSubscriptionName();
077                                if (subName != null && subName.equals(candidateSubName)) {
078                                    DemandSubscription sub = createDemandSubscription(dest, subName);
079                                    sub.getLocalInfo().setSubscriptionName(getSubscriberName(dest));
080                                    sub.setStaticallyIncluded(true);
081                                    addSubscription(sub);
082                                    break;
083                                }
084                            }
085                        }
086                    } catch (IOException e) {
087                        LOG.error("Failed to add static destination {}", dest, e);
088                    }
089                    LOG.trace("Forwarding messages for durable destination: {}", dest);
090                } else if (configuration.isSyncDurableSubs() && !isPermissableDestination(dest)) {
091                    if (dest.isTopic()) {
092                        RegionBroker regionBroker = (RegionBroker) brokerService.getRegionBroker();
093                        TopicRegion topicRegion = (TopicRegion) regionBroker.getTopicRegion();
094
095                        String candidateSubName = getSubscriberName(dest);
096                        for (Subscription subscription : topicRegion.getDurableSubscriptions().values()) {
097                            String subName = subscription.getConsumerInfo().getSubscriptionName();
098                            if (subName != null && subName.equals(candidateSubName)) {
099                               try {
100                                    // remove the NC subscription as it is no longer for a permissable dest
101                                    RemoveSubscriptionInfo sending = new RemoveSubscriptionInfo();
102                                    sending.setClientId(localClientId);
103                                    sending.setSubscriptionName(subName);
104                                    sending.setConnectionId(this.localConnectionInfo.getConnectionId());
105                                    localBroker.oneway(sending);
106                                } catch (IOException e) {
107                                    LOG.debug("Exception removing NC durable subscription: {}", subName, e);
108                                    serviceRemoteException(e);
109                                }
110                                break;
111                            }
112                        }
113                    }
114                }
115            }
116        }
117    }
118
119    @Override
120    protected DemandSubscription createDemandSubscription(ConsumerInfo info) throws IOException {
121        boolean isForcedDurable = NetworkBridgeUtils.isForcedDurable(info,
122                dynamicallyIncludedDestinations, staticallyIncludedDestinations);
123
124        if (addToAlreadyInterestedConsumers(info, isForcedDurable)) {
125            return null; // don't want this subscription added
126        }
127        //add our original id to ourselves
128        info.addNetworkConsumerId(info.getConsumerId());
129        ConsumerId forcedDurableId = isForcedDurable ? info.getConsumerId() : null;
130
131        if(info.isDurable() || isForcedDurable) {
132            // set the subscriber name to something reproducible
133            info.setSubscriptionName(getSubscriberName(info.getDestination()));
134            // and override the consumerId with something unique so that it won't
135            // be removed if the durable subscriber (at the other end) goes away
136            info.setConsumerId(new ConsumerId(localSessionInfo.getSessionId(),
137                               consumerIdGenerator.getNextSequenceId()));
138        }
139        info.setSelector(null);
140        DemandSubscription demandSubscription = doCreateDemandSubscription(info);
141        if (forcedDurableId != null) {
142            demandSubscription.addForcedDurableConsumer(forcedDurableId);
143            forcedDurableRemoteId.add(forcedDurableId);
144        }
145        return demandSubscription;
146    }
147
148    protected String getSubscriberName(ActiveMQDestination dest) {
149        String subscriberName = DURABLE_SUB_PREFIX + configuration.getBrokerName() + "_" + dest.getPhysicalName();
150        return subscriberName;
151    }
152
153    protected boolean doesConsumerExist(ActiveMQDestination dest) {
154        DestinationFilter filter = DestinationFilter.parseFilter(dest);
155        for (DemandSubscription ds : subscriptionMapByLocalId.values()) {
156            if (filter.matches(ds.getLocalInfo().getDestination())) {
157                return true;
158            }
159        }
160        return false;
161    }
162}