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.broker; 018 019import org.apache.activemq.broker.region.Destination; 020import org.apache.activemq.broker.region.Region; 021import org.apache.activemq.command.Message; 022import org.apache.activemq.command.MessageId; 023import org.apache.activemq.state.ProducerState; 024import org.slf4j.Logger; 025import org.slf4j.LoggerFactory; 026 027import java.io.IOException; 028import java.util.concurrent.atomic.AtomicBoolean; 029import java.util.concurrent.atomic.AtomicLong; 030 031/** 032 * Holds internal state in the broker for a MessageProducer 033 */ 034public class ProducerBrokerExchange { 035 036 private static final Logger LOG = LoggerFactory.getLogger(ProducerBrokerExchange.class); 037 private ConnectionContext connectionContext; 038 private Destination regionDestination; 039 private Region region; 040 private ProducerState producerState; 041 private boolean mutable = true; 042 private AtomicLong lastSendSequenceNumber = new AtomicLong(-1); 043 private boolean auditProducerSequenceIds; 044 private boolean isNetworkProducer; 045 private BrokerService brokerService; 046 private FlowControlInfo flowControlInfo = new FlowControlInfo(); 047 048 public ProducerBrokerExchange() { 049 } 050 051 public ProducerBrokerExchange copy() { 052 ProducerBrokerExchange rc = new ProducerBrokerExchange(); 053 rc.connectionContext = connectionContext.copy(); 054 rc.regionDestination = regionDestination; 055 rc.region = region; 056 rc.producerState = producerState; 057 rc.mutable = mutable; 058 rc.flowControlInfo = flowControlInfo; 059 return rc; 060 } 061 062 063 /** 064 * @return the connectionContext 065 */ 066 public ConnectionContext getConnectionContext() { 067 return this.connectionContext; 068 } 069 070 /** 071 * @param connectionContext the connectionContext to set 072 */ 073 public void setConnectionContext(ConnectionContext connectionContext) { 074 this.connectionContext = connectionContext; 075 } 076 077 /** 078 * @return the mutable 079 */ 080 public boolean isMutable() { 081 return this.mutable; 082 } 083 084 /** 085 * @param mutable the mutable to set 086 */ 087 public void setMutable(boolean mutable) { 088 this.mutable = mutable; 089 } 090 091 /** 092 * @return the regionDestination 093 */ 094 public Destination getRegionDestination() { 095 return this.regionDestination; 096 } 097 098 /** 099 * @param regionDestination the regionDestination to set 100 */ 101 public void setRegionDestination(Destination regionDestination) { 102 this.regionDestination = regionDestination; 103 } 104 105 /** 106 * @return the region 107 */ 108 public Region getRegion() { 109 return this.region; 110 } 111 112 /** 113 * @param region the region to set 114 */ 115 public void setRegion(Region region) { 116 this.region = region; 117 } 118 119 /** 120 * @return the producerState 121 */ 122 public ProducerState getProducerState() { 123 return this.producerState; 124 } 125 126 /** 127 * @param producerState the producerState to set 128 */ 129 public void setProducerState(ProducerState producerState) { 130 this.producerState = producerState; 131 } 132 133 /** 134 * Enforce duplicate suppression using info from persistence adapter 135 * 136 * @return false if message should be ignored as a duplicate 137 */ 138 public boolean canDispatch(Message messageSend) { 139 boolean canDispatch = true; 140 if (auditProducerSequenceIds && messageSend.isPersistent()) { 141 final long producerSequenceId = messageSend.getMessageId().getProducerSequenceId(); 142 if (isNetworkProducer) { 143 // messages are multiplexed on this producer so we need to query the persistenceAdapter 144 long lastStoredForMessageProducer = getStoredSequenceIdForMessage(messageSend.getMessageId()); 145 if (producerSequenceId <= lastStoredForMessageProducer) { 146 canDispatch = false; 147 LOG.warn("suppressing duplicate message send [{}] from network producer with producerSequence [{}] less than last stored: {}", 148 (LOG.isTraceEnabled() ? messageSend : messageSend.getMessageId()), producerSequenceId, lastStoredForMessageProducer); 149 } 150 } else if (producerSequenceId <= lastSendSequenceNumber.get()) { 151 canDispatch = false; 152 if (messageSend.isInTransaction()) { 153 LOG.warn("suppressing duplicated message send [{}] with producerSequenceId [{}] <= last stored: {}", 154 (LOG.isTraceEnabled() ? messageSend : messageSend.getMessageId()), producerSequenceId, lastSendSequenceNumber); 155 } else { 156 LOG.debug("suppressing duplicated message send [{}] with producerSequenceId [{}] <= last stored: {}", 157 (LOG.isTraceEnabled() ? messageSend : messageSend.getMessageId()), producerSequenceId, lastSendSequenceNumber); 158 159 } 160 } else { 161 // track current so we can suppress duplicates later in the stream 162 lastSendSequenceNumber.set(producerSequenceId); 163 } 164 } 165 return canDispatch; 166 } 167 168 private long getStoredSequenceIdForMessage(MessageId messageId) { 169 try { 170 return brokerService.getPersistenceAdapter().getLastProducerSequenceId(messageId.getProducerId()); 171 } catch (IOException ignored) { 172 LOG.debug("Failed to determine last producer sequence id for: {}", messageId, ignored); 173 } 174 return -1; 175 } 176 177 public void setLastStoredSequenceId(long l) { 178 auditProducerSequenceIds = true; 179 if (connectionContext.isNetworkConnection()) { 180 brokerService = connectionContext.getBroker().getBrokerService(); 181 isNetworkProducer = true; 182 } 183 lastSendSequenceNumber.set(l); 184 LOG.debug("last stored sequence id set: {}", l); 185 } 186 187 public void incrementSend() { 188 flowControlInfo.incrementSend(); 189 } 190 191 public void blockingOnFlowControl(boolean blockingOnFlowControl) { 192 flowControlInfo.setBlockingOnFlowControl(blockingOnFlowControl); 193 } 194 195 public void incrementTimeBlocked(Destination destination, long timeBlocked) { 196 flowControlInfo.incrementTimeBlocked(timeBlocked); 197 } 198 199 200 public boolean isBlockedForFlowControl() { 201 return flowControlInfo.isBlockingOnFlowControl(); 202 } 203 204 public void resetFlowControl() { 205 flowControlInfo.reset(); 206 } 207 208 public long getTotalTimeBlocked() { 209 return flowControlInfo.getTotalTimeBlocked(); 210 } 211 212 public int getPercentageBlocked() { 213 double value = flowControlInfo.getTotalSends() == 0 ? 0 : flowControlInfo.getSendsBlocked() / flowControlInfo.getTotalSends(); 214 return (int) value * 100; 215 } 216 217 218 public static class FlowControlInfo { 219 private AtomicBoolean blockingOnFlowControl = new AtomicBoolean(); 220 private AtomicLong totalSends = new AtomicLong(); 221 private AtomicLong sendsBlocked = new AtomicLong(); 222 private AtomicLong totalTimeBlocked = new AtomicLong(); 223 224 225 public boolean isBlockingOnFlowControl() { 226 return blockingOnFlowControl.get(); 227 } 228 229 public void setBlockingOnFlowControl(boolean blockingOnFlowControl) { 230 this.blockingOnFlowControl.set(blockingOnFlowControl); 231 if (blockingOnFlowControl) { 232 incrementSendBlocked(); 233 } 234 } 235 236 237 public long getTotalSends() { 238 return totalSends.get(); 239 } 240 241 public void incrementSend() { 242 this.totalSends.incrementAndGet(); 243 } 244 245 public long getSendsBlocked() { 246 return sendsBlocked.get(); 247 } 248 249 public void incrementSendBlocked() { 250 this.sendsBlocked.incrementAndGet(); 251 } 252 253 public long getTotalTimeBlocked() { 254 return totalTimeBlocked.get(); 255 } 256 257 public void incrementTimeBlocked(long time) { 258 this.totalTimeBlocked.addAndGet(time); 259 } 260 261 public void reset() { 262 blockingOnFlowControl.set(false); 263 totalSends.set(0); 264 sendsBlocked.set(0); 265 totalTimeBlocked.set(0); 266 267 } 268 } 269}