Support for temporary topic delete
This commit is contained in:
Timothy Bish 2015-03-03 15:55:28 -05:00
parent 2ec586f267
commit ad57cc6fcb
2 changed files with 89 additions and 33 deletions

View File

@ -19,6 +19,7 @@ package org.apache.activemq.transport.amqp;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -38,6 +39,7 @@ import org.apache.activemq.broker.region.RegionBroker;
import org.apache.activemq.broker.region.TopicRegion; import org.apache.activemq.broker.region.TopicRegion;
import org.apache.activemq.command.ActiveMQDestination; import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.command.ActiveMQMessage; import org.apache.activemq.command.ActiveMQMessage;
import org.apache.activemq.command.ActiveMQTempDestination;
import org.apache.activemq.command.ActiveMQTempQueue; import org.apache.activemq.command.ActiveMQTempQueue;
import org.apache.activemq.command.ActiveMQTempTopic; import org.apache.activemq.command.ActiveMQTempTopic;
import org.apache.activemq.command.Command; import org.apache.activemq.command.Command;
@ -517,12 +519,21 @@ class AmqpProtocolConverter implements IAmqpProtocolConverter {
static abstract class AmqpDeliveryListener { static abstract class AmqpDeliveryListener {
protected ActiveMQDestination destination;
protected List<Runnable> closeActions = new ArrayList<Runnable>();
abstract public void onDelivery(Delivery delivery) throws Exception; abstract public void onDelivery(Delivery delivery) throws Exception;
public void onDetach() throws Exception { public void onDetach() throws Exception {
} }
public void onClose() throws Exception { public void onClose() throws Exception {
for (Runnable action : closeActions) {
action.run();
}
closeActions.clear();
} }
public void drainCheck() { public void drainCheck() {
@ -531,6 +542,18 @@ class AmqpProtocolConverter implements IAmqpProtocolConverter {
abstract void doCommit() throws Exception; abstract void doCommit() throws Exception;
abstract void doRollback() throws Exception; abstract void doRollback() throws Exception;
public void addCloseAction(Runnable action) {
closeActions.add(action);
}
public ActiveMQDestination getDestination() {
return destination;
}
public void setDestination(ActiveMQDestination destination) {
this.destination = destination;
}
} }
private void onConnectionOpen() throws AmqpProtocolException { private void onConnectionOpen() throws AmqpProtocolException {
@ -683,14 +706,11 @@ class AmqpProtocolConverter implements IAmqpProtocolConverter {
class ProducerContext extends BaseProducerContext { class ProducerContext extends BaseProducerContext {
private final ProducerId producerId; private final ProducerId producerId;
private final LongSequenceGenerator messageIdGenerator = new LongSequenceGenerator(); private final LongSequenceGenerator messageIdGenerator = new LongSequenceGenerator();
private final ActiveMQDestination destination;
private boolean closed; private boolean closed;
private final boolean anonymous; private boolean anonymous;
public ProducerContext(ProducerId producerId, ActiveMQDestination destination, boolean anonymous) { public ProducerContext(ProducerId producerId) {
this.producerId = producerId; this.producerId = producerId;
this.destination = destination;
this.anonymous = anonymous;
} }
@Override @Override
@ -797,6 +817,8 @@ class AmqpProtocolConverter implements IAmqpProtocolConverter {
if (!closed) { if (!closed) {
sendToActiveMQ(new RemoveInfo(producerId), null); sendToActiveMQ(new RemoveInfo(producerId), null);
} }
super.onClose();
} }
public void close() { public void close() {
@ -914,6 +936,7 @@ class AmqpProtocolConverter implements IAmqpProtocolConverter {
// Client is producing to this receiver object // Client is producing to this receiver object
org.apache.qpid.proton.amqp.transport.Target remoteTarget = receiver.getRemoteTarget(); org.apache.qpid.proton.amqp.transport.Target remoteTarget = receiver.getRemoteTarget();
int flow = producerCredit; int flow = producerCredit;
try { try {
if (remoteTarget instanceof Coordinator) { if (remoteTarget instanceof Coordinator) {
pumpProtonToSocket(); pumpProtonToSocket();
@ -924,29 +947,35 @@ class AmqpProtocolConverter implements IAmqpProtocolConverter {
} else { } else {
Target target = (Target) remoteTarget; Target target = (Target) remoteTarget;
ProducerId producerId = new ProducerId(sessionContext.sessionId, sessionContext.nextProducerId++); ProducerId producerId = new ProducerId(sessionContext.sessionId, sessionContext.nextProducerId++);
final ProducerContext producerContext = new ProducerContext(producerId);
ActiveMQDestination destination = null; ActiveMQDestination destination = null;
boolean anonymous = false;
String targetNodeName = target.getAddress(); String targetNodeName = target.getAddress();
if ((targetNodeName == null || targetNodeName.length() == 0) && !target.getDynamic()) { if ((targetNodeName == null || targetNodeName.length() == 0) && !target.getDynamic()) {
anonymous = true; producerContext.anonymous = true;
} else if (target.getDynamic()) { } else if (target.getDynamic()) {
destination = createTemporaryDestination(receiver, target.getCapabilities()); destination = createTemporaryDestination(receiver, target.getCapabilities());
Target actualTarget = new Target(); Target actualTarget = new Target();
actualTarget.setAddress(destination.getQualifiedName()); actualTarget.setAddress(destination.getQualifiedName());
actualTarget.setDynamic(true); actualTarget.setDynamic(true);
receiver.setTarget(actualTarget); receiver.setTarget(actualTarget);
producerContext.addCloseAction(new Runnable() {
@Override
public void run() {
deleteTemporaryDestination((ActiveMQTempDestination) producerContext.getDestination());
}
});
} else { } else {
destination = createDestination(remoteTarget); destination = createDestination(remoteTarget);
} }
final ProducerContext producerContext = new ProducerContext(producerId, destination, anonymous);
receiver.setContext(producerContext); receiver.setContext(producerContext);
receiver.flow(flow); receiver.flow(flow);
ProducerInfo producerInfo = new ProducerInfo(producerId); ProducerInfo producerInfo = new ProducerInfo(producerId);
producerInfo.setDestination(destination); producerInfo.setDestination(destination);
producerContext.setDestination(destination);
sendToActiveMQ(producerInfo, new ResponseHandler() { sendToActiveMQ(producerInfo, new ResponseHandler() {
@Override @Override
public void onResponse(IAmqpProtocolConverter converter, Response response) throws IOException { public void onResponse(IAmqpProtocolConverter converter, Response response) throws IOException {
@ -1005,7 +1034,6 @@ class AmqpProtocolConverter implements IAmqpProtocolConverter {
private boolean closed; private boolean closed;
public ConsumerInfo info; public ConsumerInfo info;
private boolean endOfBrowse = false; private boolean endOfBrowse = false;
public ActiveMQDestination destination;
public int credit; public int credit;
public int consumerPrefetch = 0; public int consumerPrefetch = 0;
private long lastDeliveredSequenceId; private long lastDeliveredSequenceId;
@ -1068,28 +1096,32 @@ class AmqpProtocolConverter implements IAmqpProtocolConverter {
@Override @Override
public void onClose() throws Exception { public void onClose() throws Exception {
if (!closed) { try {
closed = true; if (!closed) {
sender.setContext(null); closed = true;
subscriptionsByConsumerId.remove(consumerId); sender.setContext(null);
subscriptionsByConsumerId.remove(consumerId);
AmqpSessionContext session = (AmqpSessionContext) sender.getSession().getContext(); AmqpSessionContext session = (AmqpSessionContext) sender.getSession().getContext();
if (session != null) { if (session != null) {
session.consumers.remove(info.getConsumerId()); session.consumers.remove(info.getConsumerId());
} }
RemoveInfo removeCommand = new RemoveInfo(consumerId); RemoveInfo removeCommand = new RemoveInfo(consumerId);
removeCommand.setLastDeliveredSequenceId(lastDeliveredSequenceId); removeCommand.setLastDeliveredSequenceId(lastDeliveredSequenceId);
sendToActiveMQ(removeCommand, null); sendToActiveMQ(removeCommand, null);
if (info.isDurable()) { if (info.isDurable()) {
RemoveSubscriptionInfo rsi = new RemoveSubscriptionInfo(); RemoveSubscriptionInfo rsi = new RemoveSubscriptionInfo();
rsi.setConnectionId(connectionId); rsi.setConnectionId(connectionId);
rsi.setSubscriptionName(sender.getName()); rsi.setSubscriptionName(sender.getName());
rsi.setClientId(connectionInfo.getClientId()); rsi.setClientId(connectionInfo.getClientId());
sendToActiveMQ(rsi, null); sendToActiveMQ(rsi, null);
}
} }
} finally {
super.onClose();
} }
} }
@ -1415,6 +1447,13 @@ class AmqpProtocolConverter implements IAmqpProtocolConverter {
source.setAddress(destination.getQualifiedName()); source.setAddress(destination.getQualifiedName());
source.setDynamic(true); source.setDynamic(true);
sender.setSource(source); sender.setSource(source);
consumerContext.addCloseAction(new Runnable() {
@Override
public void run() {
deleteTemporaryDestination((ActiveMQTempDestination) consumerContext.getDestination());
}
});
} else { } else {
destination = createDestination(source); destination = createDestination(source);
} }
@ -1425,7 +1464,7 @@ class AmqpProtocolConverter implements IAmqpProtocolConverter {
consumerInfo.setSelector(selector); consumerInfo.setSelector(selector);
consumerInfo.setNoRangeAcks(true); consumerInfo.setNoRangeAcks(true);
consumerInfo.setDestination(destination); consumerInfo.setDestination(destination);
consumerContext.destination = destination; consumerContext.setDestination(destination);
int senderCredit = sender.getRemoteCredit(); int senderCredit = sender.getRemoteCredit();
if (prefetch != 0) { if (prefetch != 0) {
// use the value configured on the transport connector // use the value configured on the transport connector
@ -1551,6 +1590,24 @@ class AmqpProtocolConverter implements IAmqpProtocolConverter {
return rc; return rc;
} }
private void deleteTemporaryDestination(ActiveMQTempDestination destination) {
DestinationInfo info = new DestinationInfo();
info.setConnectionId(connectionId);
info.setOperationType(DestinationInfo.REMOVE_OPERATION_TYPE);
info.setDestination(destination);
sendToActiveMQ(info, new ResponseHandler() {
@Override
public void onResponse(IAmqpProtocolConverter converter, Response response) throws IOException {
if (response.isException()) {
Throwable exception = ((ExceptionResponse) response).getException();
LOG.debug("Error during temp destination removeal: {}", exception.getMessage());
}
}
});
}
private boolean contains(Symbol[] symbols, Symbol key) { private boolean contains(Symbol[] symbols, Symbol key) {
if (symbols == null) { if (symbols == null) {
return false; return false;

View File

@ -1067,7 +1067,6 @@ public class JMSClientTest extends JMSClientTestSupport {
} }
} }
@Ignore("Broker cannot currently tell if it should delete a temp destination")
@Test(timeout=30000) @Test(timeout=30000)
public void testDeleteTemporaryQueue() throws Exception { public void testDeleteTemporaryQueue() throws Exception {
ActiveMQAdmin.enableJMSFrameTracing(); ActiveMQAdmin.enableJMSFrameTracing();
@ -1112,7 +1111,7 @@ public class JMSClientTest extends JMSClientTestSupport {
} }
} }
@Ignore("Broker cannot currently tell if it should delete a temp destination") @Ignore("Legacy QPid client does not support creation of TemporaryTopics correctly")
@Test(timeout=30000) @Test(timeout=30000)
public void testDeleteTemporaryTopic() throws Exception { public void testDeleteTemporaryTopic() throws Exception {
ActiveMQAdmin.enableJMSFrameTracing(); ActiveMQAdmin.enableJMSFrameTracing();