blob: f626365fa982e748bf04d7ff9828060ef4442d5c [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.activemq.broker.region.policy;
import org.apache.activemq.ActiveMQMessageAudit;
import org.apache.activemq.broker.region.Destination;
import org.apache.activemq.broker.region.DurableTopicSubscription;
import org.apache.activemq.broker.region.Subscription;
import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.apache.activemq.command.Message;
import org.apache.activemq.util.LRUCache;
/**
* A {@link DeadLetterStrategy} where each destination has its own individual
* DLQ using the subject naming hierarchy.
*
* @org.apache.xbean.XBean
*
*/
public class IndividualDeadLetterStrategy extends AbstractDeadLetterStrategy {
private String topicPrefix = "ActiveMQ.DLQ.Topic.";
private String queuePrefix = "ActiveMQ.DLQ.Queue.";
private String topicSuffix;
private String queueSuffix;
private boolean useQueueForQueueMessages = true;
private boolean useQueueForTopicMessages = true;
private boolean destinationPerDurableSubscriber;
private int maxAuditDepth = ActiveMQMessageAudit.DEFAULT_WINDOW_SIZE;
private int maxProducersToAudit = ActiveMQMessageAudit.MAXIMUM_PRODUCER_COUNT;
private final LRUCache<String,ActiveMQMessageAudit> dedicatedMessageAudits = new LRUCache<>(10_000);
public ActiveMQDestination getDeadLetterQueueFor(Message message, Subscription subscription) {
if (message.getDestination().isQueue()) {
return createDestination(message, queuePrefix, queueSuffix, useQueueForQueueMessages, subscription);
} else {
return createDestination(message, topicPrefix, topicSuffix, useQueueForTopicMessages, subscription);
}
}
// Properties
// -------------------------------------------------------------------------
public int getMaxDestinationsToAudit() {
return dedicatedMessageAudits.getMaxCacheSize();
}
public void maxDestinationsToAudit(int maxDestinationsToAudit) {
this.dedicatedMessageAudits.setMaxCacheSize(maxDestinationsToAudit);
}
public String getQueuePrefix() {
return queuePrefix;
}
/**
* Sets the prefix to use for all dead letter queues for queue messages
*/
public void setQueuePrefix(String queuePrefix) {
this.queuePrefix = queuePrefix;
}
public String getTopicPrefix() {
return topicPrefix;
}
/**
* Sets the prefix to use for all dead letter queues for topic messages
*/
public void setTopicPrefix(String topicPrefix) {
this.topicPrefix = topicPrefix;
}
public String getQueueSuffix() {
return queueSuffix;
}
/**
* Sets the suffix to use for all dead letter queues for queue messages
*/
public void setQueueSuffix(String queueSuffix) {
this.queueSuffix = queueSuffix;
}
public String getTopicSuffix() {
return topicSuffix;
}
/**
* Sets the suffix to use for all dead letter queues for topic messages
*/
public void setTopicSuffix(String topicSuffix) {
this.topicSuffix = topicSuffix;
}
public boolean isUseQueueForQueueMessages() {
return useQueueForQueueMessages;
}
/**
* Sets whether a queue or topic should be used for queue messages sent to a
* DLQ. The default is to use a Queue
*/
public void setUseQueueForQueueMessages(boolean useQueueForQueueMessages) {
this.useQueueForQueueMessages = useQueueForQueueMessages;
}
public boolean isUseQueueForTopicMessages() {
return useQueueForTopicMessages;
}
/**
* Sets whether a queue or topic should be used for topic messages sent to a
* DLQ. The default is to use a Queue
*/
public void setUseQueueForTopicMessages(boolean useQueueForTopicMessages) {
this.useQueueForTopicMessages = useQueueForTopicMessages;
}
public boolean isDestinationPerDurableSubscriber() {
return destinationPerDurableSubscriber;
}
/**
* sets whether durable topic subscriptions are to get individual dead letter destinations.
* When true, the DLQ is of the form 'topicPrefix.clientId:subscriptionName'
* The default is false.
* @param destinationPerDurableSubscriber
*/
public void setDestinationPerDurableSubscriber(boolean destinationPerDurableSubscriber) {
this.destinationPerDurableSubscriber = destinationPerDurableSubscriber;
}
@Override
public int getMaxProducersToAudit() {
return this.maxProducersToAudit;
}
@Override
public void setMaxProducersToAudit(int maxProducersToAudit) {
this.maxProducersToAudit = maxProducersToAudit;
}
@Override
public void setMaxAuditDepth(int maxAuditDepth) {
this.maxAuditDepth = maxAuditDepth;
}
@Override
public int getMaxAuditDepth() {
return this.maxAuditDepth;
}
// Implementation methods
// -------------------------------------------------------------------------
protected ActiveMQDestination createDestination(Message message,
String prefix,
String suffix,
boolean useQueue,
Subscription subscription ) {
String name = null;
Destination regionDestination = (Destination) message.getRegionDestination();
if (regionDestination != null
&& regionDestination.getActiveMQDestination() != null
&& regionDestination.getActiveMQDestination().getPhysicalName() != null
&& !regionDestination.getActiveMQDestination().getPhysicalName().isEmpty()){
name = prefix + regionDestination.getActiveMQDestination().getPhysicalName();
} else {
name = prefix + message.getDestination().getPhysicalName();
}
if (destinationPerDurableSubscriber && subscription instanceof DurableTopicSubscription) {
name += "." + ((DurableTopicSubscription)subscription).getSubscriptionKey();
}
if (suffix != null && !suffix.isEmpty()) {
name += suffix;
}
if (useQueue) {
return new ActiveMQQueue(name);
} else {
return new ActiveMQTopic(name);
}
}
@Override
protected ActiveMQMessageAudit lookupActiveMQMessageAudit(Message message, boolean rollback) {
ActiveMQMessageAudit messageAudit;
synchronized(dedicatedMessageAudits) {
// Normally we want to just use the destination property on the message as the key for the map for
// caching the messageAudit object for each destination. However, when rolling back, the message
// provided here has had its destination changed to the individual DLQ destination and is no longer
// the original destination. So to find the correct messageAudit to rollback we need to use
// the originalDestination property on the message to get the correct destination that was
// used to first cache the messageAudit.
final String destinationName = rollback && message.getOriginalDestination() != null ?
message.getOriginalDestination().getQualifiedName() : message.getDestination().getQualifiedName();
messageAudit = dedicatedMessageAudits.get(destinationName);
if(messageAudit == null) {
messageAudit = new ActiveMQMessageAudit(getMaxAuditDepth(), getMaxProducersToAudit());
dedicatedMessageAudits.put(destinationName, messageAudit);
}
return messageAudit;
}
}
}