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}