mirror of
https://github.com/apache/activemq.git
synced 2025-02-10 12:06:05 +00:00
When destinations are removed via JMX or by auto remove of inactive destinations an existing producer registration can be left with a reference to a resource that no longer exists but appears valid. The destination should be marked as disposed so that a producer send can check to see if it should discard its current reference to the destination and either recreate it or update its cache to point to the currect destination resource. git-svn-id: https://svn.apache.org/repos/asf/activemq/trunk@1140770 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
007bd4cc56
commit
b5a19163bc
@ -43,7 +43,7 @@ import org.apache.activemq.usage.Usage;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class BaseDestination implements Destination {
|
public abstract class BaseDestination implements Destination {
|
||||||
/**
|
/**
|
||||||
@ -97,6 +97,7 @@ public abstract class BaseDestination implements Destination {
|
|||||||
private long lastActiveTime=0l;
|
private long lastActiveTime=0l;
|
||||||
private boolean reduceMemoryFootprint = false;
|
private boolean reduceMemoryFootprint = false;
|
||||||
protected final Scheduler scheduler;
|
protected final Scheduler scheduler;
|
||||||
|
private boolean disposed = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param brokerService
|
* @param brokerService
|
||||||
@ -122,7 +123,7 @@ public abstract class BaseDestination implements Destination {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* initialize the destination
|
* initialize the destination
|
||||||
*
|
*
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public void initialize() throws Exception {
|
public void initialize() throws Exception {
|
||||||
@ -151,7 +152,7 @@ public abstract class BaseDestination implements Destination {
|
|||||||
* Set's the interval at which warnings about producers being blocked by
|
* Set's the interval at which warnings about producers being blocked by
|
||||||
* resource usage will be triggered. Values of 0 or less will disable
|
* resource usage will be triggered. Values of 0 or less will disable
|
||||||
* warnings
|
* warnings
|
||||||
*
|
*
|
||||||
* @param blockedProducerWarningInterval the interval at which warning about
|
* @param blockedProducerWarningInterval the interval at which warning about
|
||||||
* blocked producers will be triggered.
|
* blocked producers will be triggered.
|
||||||
*/
|
*/
|
||||||
@ -160,7 +161,7 @@ public abstract class BaseDestination implements Destination {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @return the interval at which warning about blocked producers will be
|
* @return the interval at which warning about blocked producers will be
|
||||||
* triggered.
|
* triggered.
|
||||||
*/
|
*/
|
||||||
@ -218,7 +219,7 @@ public abstract class BaseDestination implements Destination {
|
|||||||
public void removeProducer(ConnectionContext context, ProducerInfo info) throws Exception {
|
public void removeProducer(ConnectionContext context, ProducerInfo info) throws Exception {
|
||||||
destinationStatistics.getProducers().decrement();
|
destinationStatistics.getProducers().decrement();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSubscription(ConnectionContext context, Subscription sub) throws Exception{
|
public void addSubscription(ConnectionContext context, Subscription sub) throws Exception{
|
||||||
destinationStatistics.getConsumers().increment();
|
destinationStatistics.getConsumers().increment();
|
||||||
this.lastActiveTime=0l;
|
this.lastActiveTime=0l;
|
||||||
@ -420,7 +421,7 @@ public abstract class BaseDestination implements Destination {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* set the dead letter strategy
|
* set the dead letter strategy
|
||||||
*
|
*
|
||||||
* @param deadLetterStrategy
|
* @param deadLetterStrategy
|
||||||
*/
|
*/
|
||||||
public void setDeadLetterStrategy(DeadLetterStrategy deadLetterStrategy) {
|
public void setDeadLetterStrategy(DeadLetterStrategy deadLetterStrategy) {
|
||||||
@ -437,7 +438,7 @@ public abstract class BaseDestination implements Destination {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* called when message is consumed
|
* called when message is consumed
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param messageReference
|
* @param messageReference
|
||||||
*/
|
*/
|
||||||
@ -449,7 +450,7 @@ public abstract class BaseDestination implements Destination {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when message is delivered to the broker
|
* Called when message is delivered to the broker
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param messageReference
|
* @param messageReference
|
||||||
*/
|
*/
|
||||||
@ -462,7 +463,7 @@ public abstract class BaseDestination implements Destination {
|
|||||||
/**
|
/**
|
||||||
* Called when a message is discarded - e.g. running low on memory This will
|
* Called when a message is discarded - e.g. running low on memory This will
|
||||||
* happen only if the policy is enabled - e.g. non durable topics
|
* happen only if the policy is enabled - e.g. non durable topics
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param messageReference
|
* @param messageReference
|
||||||
*/
|
*/
|
||||||
@ -474,7 +475,7 @@ public abstract class BaseDestination implements Destination {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when there is a slow consumer
|
* Called when there is a slow consumer
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param subs
|
* @param subs
|
||||||
*/
|
*/
|
||||||
@ -489,7 +490,7 @@ public abstract class BaseDestination implements Destination {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to notify a producer is too fast
|
* Called to notify a producer is too fast
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param producerInfo
|
* @param producerInfo
|
||||||
*/
|
*/
|
||||||
@ -501,7 +502,7 @@ public abstract class BaseDestination implements Destination {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a Usage reaches a limit
|
* Called when a Usage reaches a limit
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param usage
|
* @param usage
|
||||||
*/
|
*/
|
||||||
@ -518,6 +519,11 @@ public abstract class BaseDestination implements Destination {
|
|||||||
}
|
}
|
||||||
this.destinationStatistics.setParent(null);
|
this.destinationStatistics.setParent(null);
|
||||||
this.memoryUsage.stop();
|
this.memoryUsage.stop();
|
||||||
|
this.disposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDisposed() {
|
||||||
|
return this.disposed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -585,7 +591,7 @@ public abstract class BaseDestination implements Destination {
|
|||||||
protected final void waitForSpace(ConnectionContext context, Usage<?> usage, String warning) throws IOException, InterruptedException, ResourceAllocationException {
|
protected final void waitForSpace(ConnectionContext context, Usage<?> usage, String warning) throws IOException, InterruptedException, ResourceAllocationException {
|
||||||
waitForSpace(context, usage, 100, warning);
|
waitForSpace(context, usage, 100, warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void waitForSpace(ConnectionContext context, Usage<?> usage, int highWaterMark, String warning) throws IOException, InterruptedException, ResourceAllocationException {
|
protected final void waitForSpace(ConnectionContext context, Usage<?> usage, int highWaterMark, String warning) throws IOException, InterruptedException, ResourceAllocationException {
|
||||||
if (systemUsage.isSendFailIfNoSpace()) {
|
if (systemUsage.isSendFailIfNoSpace()) {
|
||||||
getLog().debug("sendFailIfNoSpace, forcing exception on send, usage: " + usage + ": " + warning);
|
getLog().debug("sendFailIfNoSpace, forcing exception on send, usage: " + usage + ": " + warning);
|
||||||
@ -603,7 +609,7 @@ public abstract class BaseDestination implements Destination {
|
|||||||
if (context.getStopping().get()) {
|
if (context.getStopping().get()) {
|
||||||
throw new IOException("Connection closed, send aborted.");
|
throw new IOException("Connection closed, send aborted.");
|
||||||
}
|
}
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
if (now >= nextWarn) {
|
if (now >= nextWarn) {
|
||||||
getLog().info("" + usage + ": " + warning + " (blocking for: " + (now - start) / 1000 + "s)");
|
getLog().info("" + usage + ": " + warning + " (blocking for: " + (now - start) / 1000 + "s)");
|
||||||
@ -623,7 +629,7 @@ public abstract class BaseDestination implements Destination {
|
|||||||
return this.slowConsumerStrategy;
|
return this.slowConsumerStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isPrioritizedMessages() {
|
public boolean isPrioritizedMessages() {
|
||||||
return this.prioritizedMessages;
|
return this.prioritizedMessages;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ import org.apache.activemq.usage.MemoryUsage;
|
|||||||
import org.apache.activemq.usage.Usage;
|
import org.apache.activemq.usage.Usage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public interface Destination extends Service, Task {
|
public interface Destination extends Service, Task {
|
||||||
|
|
||||||
@ -62,6 +62,8 @@ public interface Destination extends Service, Task {
|
|||||||
|
|
||||||
void dispose(ConnectionContext context) throws IOException;
|
void dispose(ConnectionContext context) throws IOException;
|
||||||
|
|
||||||
|
boolean isDisposed();
|
||||||
|
|
||||||
DestinationStatistics getDestinationStatistics();
|
DestinationStatistics getDestinationStatistics();
|
||||||
|
|
||||||
DeadLetterStrategy getDeadLetterStrategy();
|
DeadLetterStrategy getDeadLetterStrategy();
|
||||||
@ -80,14 +82,14 @@ public interface Destination extends Service, Task {
|
|||||||
* Set's the interval at which warnings about producers being blocked by
|
* Set's the interval at which warnings about producers being blocked by
|
||||||
* resource usage will be triggered. Values of 0 or less will disable
|
* resource usage will be triggered. Values of 0 or less will disable
|
||||||
* warnings
|
* warnings
|
||||||
*
|
*
|
||||||
* @param blockedProducerWarningInterval the interval at which warning about
|
* @param blockedProducerWarningInterval the interval at which warning about
|
||||||
* blocked producers will be triggered.
|
* blocked producers will be triggered.
|
||||||
*/
|
*/
|
||||||
public void setBlockedProducerWarningInterval(long blockedProducerWarningInterval);
|
public void setBlockedProducerWarningInterval(long blockedProducerWarningInterval);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @return the interval at which warning about blocked producers will be
|
* @return the interval at which warning about blocked producers will be
|
||||||
* triggered.
|
* triggered.
|
||||||
*/
|
*/
|
||||||
@ -140,14 +142,14 @@ public interface Destination extends Service, Task {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* set the lazy dispatch - default is false
|
* set the lazy dispatch - default is false
|
||||||
*
|
*
|
||||||
* @param value
|
* @param value
|
||||||
*/
|
*/
|
||||||
public void setLazyDispatch(boolean value);
|
public void setLazyDispatch(boolean value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inform the Destination a message has expired
|
* Inform the Destination a message has expired
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param subs
|
* @param subs
|
||||||
* @param node
|
* @param node
|
||||||
@ -156,7 +158,7 @@ public interface Destination extends Service, Task {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* called when message is consumed
|
* called when message is consumed
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param messageReference
|
* @param messageReference
|
||||||
*/
|
*/
|
||||||
@ -164,7 +166,7 @@ public interface Destination extends Service, Task {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when message is delivered to the broker
|
* Called when message is delivered to the broker
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param messageReference
|
* @param messageReference
|
||||||
*/
|
*/
|
||||||
@ -173,16 +175,16 @@ public interface Destination extends Service, Task {
|
|||||||
/**
|
/**
|
||||||
* Called when a message is discarded - e.g. running low on memory This will
|
* Called when a message is discarded - e.g. running low on memory This will
|
||||||
* happen only if the policy is enabled - e.g. non durable topics
|
* happen only if the policy is enabled - e.g. non durable topics
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param messageReference
|
* @param messageReference
|
||||||
* @param sub
|
* @param sub
|
||||||
*/
|
*/
|
||||||
void messageDiscarded(ConnectionContext context, Subscription sub, MessageReference messageReference);
|
void messageDiscarded(ConnectionContext context, Subscription sub, MessageReference messageReference);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when there is a slow consumer
|
* Called when there is a slow consumer
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param subs
|
* @param subs
|
||||||
*/
|
*/
|
||||||
@ -190,7 +192,7 @@ public interface Destination extends Service, Task {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to notify a producer is too fast
|
* Called to notify a producer is too fast
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param producerInfo
|
* @param producerInfo
|
||||||
*/
|
*/
|
||||||
@ -198,7 +200,7 @@ public interface Destination extends Service, Task {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a Usage reaches a limit
|
* Called when a Usage reaches a limit
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param usage
|
* @param usage
|
||||||
*/
|
*/
|
||||||
@ -209,12 +211,12 @@ public interface Destination extends Service, Task {
|
|||||||
/**
|
/**
|
||||||
* called on Queues in slave mode to allow dispatch to follow subscription
|
* called on Queues in slave mode to allow dispatch to follow subscription
|
||||||
* choice of master
|
* choice of master
|
||||||
*
|
*
|
||||||
* @param messageDispatchNotification
|
* @param messageDispatchNotification
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
void processDispatchNotification(MessageDispatchNotification messageDispatchNotification) throws Exception;
|
void processDispatchNotification(MessageDispatchNotification messageDispatchNotification) throws Exception;
|
||||||
|
|
||||||
boolean isPrioritizedMessages();
|
boolean isPrioritizedMessages();
|
||||||
|
|
||||||
SlowConsumerStrategy getSlowConsumerStrategy();
|
SlowConsumerStrategy getSlowConsumerStrategy();
|
||||||
|
@ -34,8 +34,8 @@ import org.apache.activemq.usage.MemoryUsage;
|
|||||||
import org.apache.activemq.usage.Usage;
|
import org.apache.activemq.usage.Usage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class DestinationFilter implements Destination {
|
public class DestinationFilter implements Destination {
|
||||||
|
|
||||||
@ -61,6 +61,10 @@ public class DestinationFilter implements Destination {
|
|||||||
next.dispose(context);
|
next.dispose(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isDisposed() {
|
||||||
|
return next.isDisposed();
|
||||||
|
}
|
||||||
|
|
||||||
public void gc() {
|
public void gc() {
|
||||||
next.gc();
|
next.gc();
|
||||||
}
|
}
|
||||||
@ -107,7 +111,7 @@ public class DestinationFilter implements Destination {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a message to the given destination which may be a wildcard
|
* Sends a message to the given destination which may be a wildcard
|
||||||
*
|
*
|
||||||
* @param context broker context
|
* @param context broker context
|
||||||
* @param message message to send
|
* @param message message to send
|
||||||
* @param destination possibly wildcard destination to send the message to
|
* @param destination possibly wildcard destination to send the message to
|
||||||
@ -137,7 +141,7 @@ public class DestinationFilter implements Destination {
|
|||||||
public void setBlockedProducerWarningInterval(long blockedProducerWarningInterval) {
|
public void setBlockedProducerWarningInterval(long blockedProducerWarningInterval) {
|
||||||
next.setBlockedProducerWarningInterval(blockedProducerWarningInterval);
|
next.setBlockedProducerWarningInterval(blockedProducerWarningInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getBlockedProducerWarningInterval() {
|
public long getBlockedProducerWarningInterval() {
|
||||||
return next.getBlockedProducerWarningInterval();
|
return next.getBlockedProducerWarningInterval();
|
||||||
}
|
}
|
||||||
|
@ -72,8 +72,8 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Routes Broker operations to the correct messaging regions for processing.
|
* Routes Broker operations to the correct messaging regions for processing.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class RegionBroker extends EmptyBroker {
|
public class RegionBroker extends EmptyBroker {
|
||||||
public static final String ORIGINAL_EXPIRATION = "originalExpiration";
|
public static final String ORIGINAL_EXPIRATION = "originalExpiration";
|
||||||
@ -232,17 +232,17 @@ public class RegionBroker extends EmptyBroker {
|
|||||||
synchronized (clientIdSet) {
|
synchronized (clientIdSet) {
|
||||||
ConnectionContext oldContext = clientIdSet.get(clientId);
|
ConnectionContext oldContext = clientIdSet.get(clientId);
|
||||||
if (oldContext != null) {
|
if (oldContext != null) {
|
||||||
if (context.isFaultTolerant() || context.isNetworkConnection()){
|
if (context.isFaultTolerant() || context.isNetworkConnection()){
|
||||||
//remove the old connection
|
//remove the old connection
|
||||||
try{
|
try{
|
||||||
removeConnection(oldContext, info, new Exception("remove stale client"));
|
removeConnection(oldContext, info, new Exception("remove stale client"));
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
LOG.warn("Failed to remove stale connection ",e);
|
LOG.warn("Failed to remove stale connection ",e);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
throw new InvalidClientIDException("Broker: " + getBrokerName() + " - Client: " + clientId + " already connected from "
|
throw new InvalidClientIDException("Broker: " + getBrokerName() + " - Client: " + clientId + " already connected from "
|
||||||
+ oldContext.getConnection().getRemoteAddress());
|
+ oldContext.getConnection().getRemoteAddress());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
clientIdSet.put(clientId, context);
|
clientIdSet.put(clientId, context);
|
||||||
}
|
}
|
||||||
@ -496,7 +496,8 @@ public class RegionBroker extends EmptyBroker {
|
|||||||
public void send(ProducerBrokerExchange producerExchange, Message message) throws Exception {
|
public void send(ProducerBrokerExchange producerExchange, Message message) throws Exception {
|
||||||
message.setBrokerInTime(System.currentTimeMillis());
|
message.setBrokerInTime(System.currentTimeMillis());
|
||||||
if (producerExchange.isMutable() || producerExchange.getRegion() == null
|
if (producerExchange.isMutable() || producerExchange.getRegion() == null
|
||||||
|| (producerExchange.getRegion() != null && producerExchange.getRegion().getDestinationMap().get(message.getDestination()) == null)) {
|
|| (producerExchange.getRegion() != null && producerExchange.getRegion().getDestinationMap().get(message.getDestination()) == null)
|
||||||
|
|| (producerExchange.getRegionDestination() != null && producerExchange.getRegionDestination().isDisposed())) {
|
||||||
ActiveMQDestination destination = message.getDestination();
|
ActiveMQDestination destination = message.getDestination();
|
||||||
// ensure the destination is registered with the RegionBroker
|
// ensure the destination is registered with the RegionBroker
|
||||||
producerExchange.getConnectionContext().getBroker().addDestination(producerExchange.getConnectionContext(), destination, isAllowTempAutoCreationOnSend());
|
producerExchange.getConnectionContext().getBroker().addDestination(producerExchange.getConnectionContext(), destination, isAllowTempAutoCreationOnSend());
|
||||||
@ -520,6 +521,7 @@ public class RegionBroker extends EmptyBroker {
|
|||||||
producerExchange.setRegion(region);
|
producerExchange.setRegion(region);
|
||||||
producerExchange.setRegionDestination(null);
|
producerExchange.setRegionDestination(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
producerExchange.getRegion().send(producerExchange, message);
|
producerExchange.getRegion().send(producerExchange, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -793,18 +795,18 @@ public class RegionBroker extends EmptyBroker {
|
|||||||
}
|
}
|
||||||
return expired;
|
return expired;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean stampAsExpired(Message message) throws IOException {
|
private boolean stampAsExpired(Message message) throws IOException {
|
||||||
boolean stamped=false;
|
boolean stamped=false;
|
||||||
if (message.getProperty(ORIGINAL_EXPIRATION) == null) {
|
if (message.getProperty(ORIGINAL_EXPIRATION) == null) {
|
||||||
long expiration=message.getExpiration();
|
long expiration=message.getExpiration();
|
||||||
message.setProperty(ORIGINAL_EXPIRATION,new Long(expiration));
|
message.setProperty(ORIGINAL_EXPIRATION,new Long(expiration));
|
||||||
stamped = true;
|
stamped = true;
|
||||||
}
|
}
|
||||||
return stamped;
|
return stamped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void messageExpired(ConnectionContext context, MessageReference node, Subscription subscription) {
|
public void messageExpired(ConnectionContext context, MessageReference node, Subscription subscription) {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
@ -812,51 +814,51 @@ public class RegionBroker extends EmptyBroker {
|
|||||||
}
|
}
|
||||||
getRoot().sendToDeadLetterQueue(context, node, subscription);
|
getRoot().sendToDeadLetterQueue(context, node, subscription);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendToDeadLetterQueue(ConnectionContext context,
|
public void sendToDeadLetterQueue(ConnectionContext context,
|
||||||
MessageReference node, Subscription subscription){
|
MessageReference node, Subscription subscription){
|
||||||
try{
|
try{
|
||||||
if(node!=null){
|
if(node!=null){
|
||||||
Message message=node.getMessage();
|
Message message=node.getMessage();
|
||||||
if(message!=null && node.getRegionDestination()!=null){
|
if(message!=null && node.getRegionDestination()!=null){
|
||||||
DeadLetterStrategy deadLetterStrategy=node
|
DeadLetterStrategy deadLetterStrategy=node
|
||||||
.getRegionDestination().getDeadLetterStrategy();
|
.getRegionDestination().getDeadLetterStrategy();
|
||||||
if(deadLetterStrategy!=null){
|
if(deadLetterStrategy!=null){
|
||||||
if(deadLetterStrategy.isSendToDeadLetterQueue(message)){
|
if(deadLetterStrategy.isSendToDeadLetterQueue(message)){
|
||||||
// message may be inflight to other subscriptions so do not modify
|
// message may be inflight to other subscriptions so do not modify
|
||||||
message = message.copy();
|
message = message.copy();
|
||||||
stampAsExpired(message);
|
stampAsExpired(message);
|
||||||
message.setExpiration(0);
|
message.setExpiration(0);
|
||||||
if(!message.isPersistent()){
|
if(!message.isPersistent()){
|
||||||
message.setPersistent(true);
|
message.setPersistent(true);
|
||||||
message.setProperty("originalDeliveryMode",
|
message.setProperty("originalDeliveryMode",
|
||||||
"NON_PERSISTENT");
|
"NON_PERSISTENT");
|
||||||
}
|
}
|
||||||
// The original destination and transaction id do
|
// The original destination and transaction id do
|
||||||
// not get filled when the message is first sent,
|
// not get filled when the message is first sent,
|
||||||
// it is only populated if the message is routed to
|
// it is only populated if the message is routed to
|
||||||
// another destination like the DLQ
|
// another destination like the DLQ
|
||||||
ActiveMQDestination deadLetterDestination=deadLetterStrategy
|
ActiveMQDestination deadLetterDestination=deadLetterStrategy
|
||||||
.getDeadLetterQueueFor(message, subscription);
|
.getDeadLetterQueueFor(message, subscription);
|
||||||
if (context.getBroker()==null) {
|
if (context.getBroker()==null) {
|
||||||
context.setBroker(getRoot());
|
context.setBroker(getRoot());
|
||||||
}
|
}
|
||||||
BrokerSupport.resendNoCopy(context,message,
|
BrokerSupport.resendNoCopy(context,message,
|
||||||
deadLetterDestination);
|
deadLetterDestination);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Dead Letter message with no DLQ strategy in place, message id: "
|
LOG.debug("Dead Letter message with no DLQ strategy in place, message id: "
|
||||||
+ message.getMessageId() + ", destination: " + message.getDestination());
|
+ message.getMessageId() + ", destination: " + message.getDestination());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
LOG.warn("Caught an exception sending to DLQ: "+node,e);
|
LOG.warn("Caught an exception sending to DLQ: "+node,e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Broker getRoot() {
|
public Broker getRoot() {
|
||||||
@ -867,7 +869,7 @@ public class RegionBroker extends EmptyBroker {
|
|||||||
throw new RuntimeException("The broker from the BrokerService should not throw an exception");
|
throw new RuntimeException("The broker from the BrokerService should not throw an exception");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the broker sequence id
|
* @return the broker sequence id
|
||||||
*/
|
*/
|
||||||
@ -877,17 +879,17 @@ public class RegionBroker extends EmptyBroker {
|
|||||||
return sequenceGenerator.getNextSequenceId();
|
return sequenceGenerator.getNextSequenceId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Scheduler getScheduler() {
|
public Scheduler getScheduler() {
|
||||||
return this.scheduler;
|
return this.scheduler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ThreadPoolExecutor getExecutor() {
|
public ThreadPoolExecutor getExecutor() {
|
||||||
return this.executor;
|
return this.executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processConsumerControl(ConsumerBrokerExchange consumerExchange, ConsumerControl control) {
|
public void processConsumerControl(ConsumerBrokerExchange consumerExchange, ConsumerControl control) {
|
||||||
ActiveMQDestination destination = control.getDestination();
|
ActiveMQDestination destination = control.getDestination();
|
||||||
@ -899,20 +901,20 @@ public class RegionBroker extends EmptyBroker {
|
|||||||
case ActiveMQDestination.TOPIC_TYPE:
|
case ActiveMQDestination.TOPIC_TYPE:
|
||||||
topicRegion.processConsumerControl(consumerExchange, control);
|
topicRegion.processConsumerControl(consumerExchange, control);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ActiveMQDestination.TEMP_QUEUE_TYPE:
|
case ActiveMQDestination.TEMP_QUEUE_TYPE:
|
||||||
tempQueueRegion.processConsumerControl(consumerExchange, control);
|
tempQueueRegion.processConsumerControl(consumerExchange, control);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ActiveMQDestination.TEMP_TOPIC_TYPE:
|
case ActiveMQDestination.TEMP_TOPIC_TYPE:
|
||||||
tempTopicRegion.processConsumerControl(consumerExchange, control);
|
tempTopicRegion.processConsumerControl(consumerExchange, control);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LOG.warn("unmatched destination: " + destination + ", in consumerControl: " + control);
|
LOG.warn("unmatched destination: " + destination + ", in consumerControl: " + control);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addBrokerInClusterUpdate() {
|
protected void addBrokerInClusterUpdate() {
|
||||||
List<TransportConnector> connectors = this.brokerService.getTransportConnectors();
|
List<TransportConnector> connectors = this.brokerService.getTransportConnectors();
|
||||||
for (TransportConnector connector : connectors) {
|
for (TransportConnector connector : connectors) {
|
||||||
@ -930,7 +932,7 @@ public class RegionBroker extends EmptyBroker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void purgeInactiveDestinations() {
|
protected void purgeInactiveDestinations() {
|
||||||
synchronized (purgeInactiveDestinationsTask) {
|
synchronized (purgeInactiveDestinationsTask) {
|
||||||
List<BaseDestination> list = new ArrayList<BaseDestination>();
|
List<BaseDestination> list = new ArrayList<BaseDestination>();
|
||||||
|
@ -0,0 +1,159 @@
|
|||||||
|
/**
|
||||||
|
* 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.usecases;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.jms.DeliveryMode;
|
||||||
|
import javax.jms.JMSException;
|
||||||
|
import javax.jms.Message;
|
||||||
|
import javax.jms.MessageConsumer;
|
||||||
|
import javax.jms.MessageProducer;
|
||||||
|
import javax.jms.Queue;
|
||||||
|
import javax.jms.QueueConnection;
|
||||||
|
import javax.jms.QueueSession;
|
||||||
|
import javax.jms.Session;
|
||||||
|
import javax.jms.TextMessage;
|
||||||
|
import javax.management.MBeanServerConnection;
|
||||||
|
import javax.management.ObjectName;
|
||||||
|
import javax.management.remote.JMXConnector;
|
||||||
|
import javax.management.remote.JMXConnectorFactory;
|
||||||
|
import javax.management.remote.JMXServiceURL;
|
||||||
|
|
||||||
|
import org.apache.activemq.ActiveMQConnectionFactory;
|
||||||
|
import org.apache.activemq.broker.BrokerService;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class JMXRemoveQueueThenSendIgnoredTest {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(JMXRemoveQueueThenSendIgnoredTest.class);
|
||||||
|
|
||||||
|
private BrokerService brokerService;
|
||||||
|
private MessageProducer producer;
|
||||||
|
private QueueSession session;
|
||||||
|
private QueueConnection connection;
|
||||||
|
private Queue queue;
|
||||||
|
private int count = 1;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
brokerService = new BrokerService();
|
||||||
|
brokerService.setBrokerName("dev");
|
||||||
|
brokerService.setPersistent(false);
|
||||||
|
brokerService.setUseJmx(true);
|
||||||
|
brokerService.addConnector("tcp://localhost:0");
|
||||||
|
brokerService.start();
|
||||||
|
|
||||||
|
final String brokerUri = brokerService.getTransportConnectors().get(0).getPublishableConnectString();
|
||||||
|
|
||||||
|
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(brokerUri);
|
||||||
|
connection = activeMQConnectionFactory.createQueueConnection();
|
||||||
|
session = connection.createQueueSession(true, Session.AUTO_ACKNOWLEDGE/*SESSION_TRANSACTED*/);
|
||||||
|
queue = session.createQueue("myqueue");
|
||||||
|
producer = session.createProducer(queue);
|
||||||
|
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
|
||||||
|
|
||||||
|
connection.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveQueueAndProduceAfterNewConsumerAdded() throws Exception {
|
||||||
|
MessageConsumer firstConsumer = registerConsumer();
|
||||||
|
produceMessage();
|
||||||
|
Message message = firstConsumer.receive(5000);
|
||||||
|
LOG.debug("Received message " + message);
|
||||||
|
|
||||||
|
assertEquals(1, numberOfMessages());
|
||||||
|
firstConsumer.close();
|
||||||
|
session.commit();
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
removeQueue();
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
MessageConsumer secondConsumer = registerConsumer();
|
||||||
|
produceMessage();
|
||||||
|
message = secondConsumer.receive(5000);
|
||||||
|
LOG.debug("Received message " + message);
|
||||||
|
|
||||||
|
assertEquals(1, numberOfMessages());
|
||||||
|
secondConsumer.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveQueueAndProduceBeforeNewConsumerAdded() throws Exception {
|
||||||
|
MessageConsumer firstConsumer = registerConsumer();
|
||||||
|
produceMessage();
|
||||||
|
Message message = firstConsumer.receive(5000);
|
||||||
|
LOG.debug("Received message " + message);
|
||||||
|
|
||||||
|
assertEquals(1, numberOfMessages());
|
||||||
|
firstConsumer.close();
|
||||||
|
session.commit();
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
removeQueue();
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
produceMessage();
|
||||||
|
MessageConsumer secondConsumer = registerConsumer();
|
||||||
|
message = secondConsumer.receive(5000);
|
||||||
|
LOG.debug("Received message " + message);
|
||||||
|
|
||||||
|
assertEquals(1, numberOfMessages());
|
||||||
|
secondConsumer.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MessageConsumer registerConsumer() throws JMSException {
|
||||||
|
MessageConsumer consumer = session.createConsumer(queue);
|
||||||
|
return consumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int numberOfMessages() throws Exception {
|
||||||
|
JMXConnector jmxConnector = JMXConnectorFactory.connect(new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi"));
|
||||||
|
MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection();
|
||||||
|
String beanId = "org.apache.activemq:BrokerName=dev,Type=Queue,Destination=myqueue";
|
||||||
|
List<?> object = (List<?>) mbeanServerConnection.invoke(new ObjectName(beanId), "browseMessages", null, null);
|
||||||
|
jmxConnector.close();
|
||||||
|
return object.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeQueue() throws Exception {
|
||||||
|
LOG.debug("Removing Destination: myqueue");
|
||||||
|
brokerService.getAdminView().removeQueue("myqueue");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void produceMessage() throws JMSException {
|
||||||
|
TextMessage textMessage = session.createTextMessage();
|
||||||
|
textMessage.setText("Sending message: " + count++);
|
||||||
|
LOG.debug("Sending message: " + textMessage);
|
||||||
|
producer.send(textMessage);
|
||||||
|
session.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
connection.close();
|
||||||
|
brokerService.stop();
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user