mirror of https://github.com/apache/activemq.git
Initial work on making the stomp frame and destination conversion in and out of activemq pluggable. The only implementation is the 4.0 style, but adding others is doable now.
Configuration is done on a per-TransportFactory basis right now, this might need some tweaking. Will discuss on list git-svn-id: https://svn.apache.org/repos/asf/incubator/activemq/trunk@453497 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
cc1a9be9c8
commit
f8485e3b36
|
@ -0,0 +1,109 @@
|
|||
package org.apache.activemq.transport.stomp;
|
||||
|
||||
import org.apache.activemq.command.ActiveMQMessage;
|
||||
import org.apache.activemq.command.ActiveMQDestination;
|
||||
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.Destination;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Implementations of this interface are used to map back and forth from Stomp to ActiveMQ.
|
||||
* There are several standard mappings which are semantically the same, the inner class,
|
||||
* Helper, provides functions to copy those properties from one to the other
|
||||
*/
|
||||
public interface FrameTranslator
|
||||
{
|
||||
public ActiveMQMessage convertFrame(StompFrame frame) throws JMSException, ProtocolException;
|
||||
|
||||
public StompFrame convertMessage(ActiveMQMessage message) throws IOException, JMSException;
|
||||
|
||||
public String convertDestination(Destination d);
|
||||
|
||||
public ActiveMQDestination convertDestination(String name) throws ProtocolException;
|
||||
|
||||
/**
|
||||
* Helper class which holds commonly needed functions used when implementing
|
||||
* FrameTranslators
|
||||
*/
|
||||
public final static class Helper
|
||||
{
|
||||
public static void copyStandardHeadersFromMessageToFrame(ActiveMQMessage message,
|
||||
StompFrame command,
|
||||
FrameTranslator ft)
|
||||
throws IOException
|
||||
{
|
||||
final Map headers = command.getHeaders();
|
||||
headers.put(Stomp.Headers.Message.DESTINATION, ft.convertDestination(message.getDestination()));
|
||||
headers.put(Stomp.Headers.Message.MESSAGE_ID, message.getJMSMessageID());
|
||||
|
||||
if (message.getJMSCorrelationID() != null) {
|
||||
headers.put(Stomp.Headers.Message.CORRELATION_ID, message.getJMSCorrelationID());
|
||||
}
|
||||
headers.put(Stomp.Headers.Message.EXPIRATION_TIME, ""+message.getJMSExpiration());
|
||||
|
||||
if (message.getJMSRedelivered()) {
|
||||
headers.put(Stomp.Headers.Message.REDELIVERED, "true");
|
||||
}
|
||||
headers.put(Stomp.Headers.Message.PRORITY, ""+message.getJMSPriority());
|
||||
|
||||
if (message.getJMSReplyTo() != null) {
|
||||
headers.put(Stomp.Headers.Message.REPLY_TO, ft.convertDestination(message.getJMSReplyTo()));
|
||||
}
|
||||
headers.put(Stomp.Headers.Message.TIMESTAMP, ""+message.getJMSTimestamp());
|
||||
|
||||
if (message.getJMSType() != null) {
|
||||
headers.put(Stomp.Headers.Message.TYPE, message.getJMSType());
|
||||
}
|
||||
|
||||
// now lets add all the message headers
|
||||
final Map properties = message.getProperties();
|
||||
if (properties != null) {
|
||||
headers.putAll(properties);
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyStandardHeadersFromFrameToMessage(StompFrame command,
|
||||
ActiveMQMessage msg,
|
||||
FrameTranslator ft)
|
||||
throws ProtocolException, JMSException
|
||||
{
|
||||
final Map headers = new HashMap(command.getHeaders());
|
||||
final String destination = (String) headers.remove(Stomp.Headers.Send.DESTINATION);
|
||||
msg.setDestination( ft.convertDestination(destination));
|
||||
|
||||
// the standard JMS headers
|
||||
msg.setJMSCorrelationID((String) headers.remove(Stomp.Headers.Send.CORRELATION_ID));
|
||||
|
||||
Object o = headers.remove(Stomp.Headers.Send.EXPIRATION_TIME);
|
||||
if (o != null) {
|
||||
msg.setJMSExpiration(Long.parseLong((String) o));
|
||||
}
|
||||
|
||||
o = headers.remove(Stomp.Headers.Send.PRIORITY);
|
||||
if (o != null) {
|
||||
msg.setJMSPriority(Integer.parseInt((String) o));
|
||||
}
|
||||
|
||||
o = headers.remove(Stomp.Headers.Send.TYPE);
|
||||
if (o != null) {
|
||||
msg.setJMSType((String) o);
|
||||
}
|
||||
|
||||
o = headers.remove(Stomp.Headers.Send.REPLY_TO);
|
||||
if (o != null) {
|
||||
msg.setJMSReplyTo(ft.convertDestination((String) o));
|
||||
}
|
||||
|
||||
o = headers.remove(Stomp.Headers.Send.PERSISTENT);
|
||||
if (o != null) {
|
||||
msg.setPersistent("true".equals(o));
|
||||
}
|
||||
|
||||
// now the general headers
|
||||
msg.setProperties(headers);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package org.apache.activemq.transport.stomp;
|
||||
|
||||
import org.apache.activemq.command.ActiveMQMessage;
|
||||
import org.apache.activemq.command.ActiveMQBytesMessage;
|
||||
import org.apache.activemq.command.ActiveMQTextMessage;
|
||||
import org.apache.activemq.command.ActiveMQDestination;
|
||||
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.Destination;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Implements ActiveMQ 4.0 translations
|
||||
*/
|
||||
public class LegacyFrameTranslator implements FrameTranslator
|
||||
{
|
||||
public ActiveMQMessage convertFrame(StompFrame command) throws JMSException, ProtocolException {
|
||||
final Map headers = command.getHeaders();
|
||||
final ActiveMQMessage msg;
|
||||
if (headers.containsKey(Stomp.Headers.CONTENT_LENGTH)) {
|
||||
headers.remove(Stomp.Headers.CONTENT_LENGTH);
|
||||
ActiveMQBytesMessage bm = new ActiveMQBytesMessage();
|
||||
bm.writeBytes(command.getContent());
|
||||
msg = bm;
|
||||
} else {
|
||||
ActiveMQTextMessage text = new ActiveMQTextMessage();
|
||||
try {
|
||||
text.setText(new String(command.getContent(), "UTF-8"));
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throw new ProtocolException("Text could not bet set: " + e, false, e);
|
||||
}
|
||||
msg = text;
|
||||
}
|
||||
FrameTranslator.Helper.copyStandardHeadersFromFrameToMessage(command, msg, this);
|
||||
return msg;
|
||||
}
|
||||
|
||||
public StompFrame convertMessage(ActiveMQMessage message) throws IOException, JMSException {
|
||||
StompFrame command = new StompFrame();
|
||||
command.setAction(Stomp.Responses.MESSAGE);
|
||||
Map headers = new HashMap(25);
|
||||
command.setHeaders(headers);
|
||||
|
||||
FrameTranslator.Helper.copyStandardHeadersFromMessageToFrame(message, command, this);
|
||||
|
||||
if( message.getDataStructureType() == ActiveMQTextMessage.DATA_STRUCTURE_TYPE ) {
|
||||
|
||||
ActiveMQTextMessage msg = (ActiveMQTextMessage)message.copy();
|
||||
command.setContent(msg.getText().getBytes("UTF-8"));
|
||||
|
||||
} else if( message.getDataStructureType() == ActiveMQBytesMessage.DATA_STRUCTURE_TYPE ) {
|
||||
|
||||
ActiveMQBytesMessage msg = (ActiveMQBytesMessage)message.copy();
|
||||
byte[] data = new byte[(int)msg.getBodyLength()];
|
||||
msg.readBytes(data);
|
||||
|
||||
headers.put(Stomp.Headers.CONTENT_LENGTH, ""+data.length);
|
||||
command.setContent(data);
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
public String convertDestination(Destination d) {
|
||||
if (d == null) {
|
||||
return null;
|
||||
}
|
||||
ActiveMQDestination amq_d = (ActiveMQDestination) d;
|
||||
String p_name = amq_d.getPhysicalName();
|
||||
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
if (amq_d.isQueue()) {
|
||||
buffer.append("/queue/");
|
||||
}
|
||||
if (amq_d.isTopic()) {
|
||||
buffer.append("/topic/");
|
||||
}
|
||||
buffer.append(p_name);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public ActiveMQDestination convertDestination(String name) throws ProtocolException {
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
else if (name.startsWith("/queue/")) {
|
||||
String q_name = name.substring("/queue/".length(), name.length());
|
||||
return ActiveMQDestination.createDestination(q_name, ActiveMQDestination.QUEUE_TYPE);
|
||||
}
|
||||
else if (name.startsWith("/topic/")) {
|
||||
String t_name = name.substring("/topic/".length(), name.length());
|
||||
return ActiveMQDestination.createDestination(t_name, ActiveMQDestination.TOPIC_TYPE);
|
||||
}
|
||||
else {
|
||||
throw new ProtocolException("Illegal destination name: [" + name + "] -- ActiveMQ STOMP destinations " +
|
||||
"must begine with /queue/ or /topic/");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -74,11 +74,18 @@ public class ProtocolConverter {
|
|||
private final ConcurrentHashMap resposeHandlers = new ConcurrentHashMap();
|
||||
private final ConcurrentHashMap subscriptionsByConsumerId = new ConcurrentHashMap();
|
||||
private final Map transactions = new ConcurrentHashMap();
|
||||
private StompTransportFilter transportFilter;
|
||||
private final StompTransportFilter transportFilter;
|
||||
|
||||
private final Object commnadIdMutex = new Object();
|
||||
private int lastCommandId;
|
||||
private final AtomicBoolean connected = new AtomicBoolean(false);
|
||||
private final FrameTranslator frameTranslator;
|
||||
|
||||
public ProtocolConverter(StompTransportFilter stompTransportFilter, FrameTranslator translator)
|
||||
{
|
||||
this.transportFilter = stompTransportFilter;
|
||||
this.frameTranslator = translator;
|
||||
}
|
||||
|
||||
protected int generateCommandId() {
|
||||
synchronized(commnadIdMutex){
|
||||
|
@ -274,10 +281,8 @@ public class ProtocolConverter {
|
|||
throw new ProtocolException("Must specify the transaction you are committing");
|
||||
}
|
||||
|
||||
TransactionId activemqTx=null;
|
||||
if (stompTx!=null) {
|
||||
activemqTx = (TransactionId) transactions.remove(stompTx);
|
||||
if (activemqTx == null)
|
||||
TransactionId activemqTx = (TransactionId) transactions.remove(stompTx);
|
||||
if (activemqTx == null) {
|
||||
throw new ProtocolException("Invalid transaction id: "+stompTx);
|
||||
}
|
||||
|
||||
|
@ -298,10 +303,8 @@ public class ProtocolConverter {
|
|||
throw new ProtocolException("Must specify the transaction you are committing");
|
||||
}
|
||||
|
||||
TransactionId activemqTx=null;
|
||||
if (stompTx!=null) {
|
||||
activemqTx = (TransactionId) transactions.remove(stompTx);
|
||||
if (activemqTx == null)
|
||||
TransactionId activemqTx = (TransactionId) transactions.remove(stompTx);
|
||||
if (activemqTx == null) {
|
||||
throw new ProtocolException("Invalid transaction id: "+stompTx);
|
||||
}
|
||||
|
||||
|
@ -321,7 +324,7 @@ public class ProtocolConverter {
|
|||
String subscriptionId = (String)headers.get(Stomp.Headers.Subscribe.ID);
|
||||
String destination = (String)headers.get(Stomp.Headers.Subscribe.DESTINATION);
|
||||
|
||||
ActiveMQDestination actual_dest = convertDestination(destination);
|
||||
ActiveMQDestination actual_dest = frameTranslator.convertDestination(destination);
|
||||
ConsumerId id = new ConsumerId(sessionId, consumerIdGenerator.getNextSequenceId());
|
||||
ConsumerInfo consumerInfo = new ConsumerInfo(id);
|
||||
consumerInfo.setPrefetchSize(1000);
|
||||
|
@ -332,7 +335,7 @@ public class ProtocolConverter {
|
|||
|
||||
IntrospectionSupport.setProperties(consumerInfo, headers, "activemq.");
|
||||
|
||||
consumerInfo.setDestination(convertDestination(destination));
|
||||
consumerInfo.setDestination(frameTranslator.convertDestination(destination));
|
||||
|
||||
StompSubscription stompSubscription = new StompSubscription(this, subscriptionId, consumerInfo);
|
||||
stompSubscription.setDestination(actual_dest);
|
||||
|
@ -356,7 +359,7 @@ public class ProtocolConverter {
|
|||
ActiveMQDestination destination=null;
|
||||
Object o = headers.get(Stomp.Headers.Unsubscribe.DESTINATION);
|
||||
if( o!=null )
|
||||
destination =convertDestination((String) o);
|
||||
destination = frameTranslator.convertDestination((String) o);
|
||||
|
||||
String subscriptionId = (String)headers.get(Stomp.Headers.Unsubscribe.ID);
|
||||
|
||||
|
@ -454,7 +457,7 @@ public class ProtocolConverter {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convert a ActiveMQ command
|
||||
* Dispatch a ActiveMQ command
|
||||
* @param command
|
||||
* @throws IOException
|
||||
*/
|
||||
|
@ -472,164 +475,24 @@ public class ProtocolConverter {
|
|||
|
||||
MessageDispatch md = (MessageDispatch)command;
|
||||
StompSubscription sub = (StompSubscription) subscriptionsByConsumerId.get(md.getConsumerId());
|
||||
if (sub != null)
|
||||
if (sub != null) {
|
||||
sub.onMessageDispatch(md);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public ActiveMQMessage convertMessage(StompFrame command) throws IOException, JMSException {
|
||||
Map headers = command.getHeaders();
|
||||
|
||||
// now the body
|
||||
ActiveMQMessage msg;
|
||||
if (headers.containsKey(Stomp.Headers.CONTENT_LENGTH)) {
|
||||
headers.remove(Stomp.Headers.CONTENT_LENGTH);
|
||||
ActiveMQBytesMessage bm = new ActiveMQBytesMessage();
|
||||
bm.writeBytes(command.getContent());
|
||||
msg = bm;
|
||||
} else {
|
||||
ActiveMQTextMessage text = new ActiveMQTextMessage();
|
||||
try {
|
||||
text.setText(new String(command.getContent(), "UTF-8"));
|
||||
} catch (Throwable e) {
|
||||
throw new ProtocolException("Text could not bet set: "+e, false, e);
|
||||
}
|
||||
msg = text;
|
||||
}
|
||||
|
||||
String destination = (String) headers.remove(Stomp.Headers.Send.DESTINATION);
|
||||
msg.setDestination(convertDestination(destination));
|
||||
|
||||
// the standard JMS headers
|
||||
msg.setJMSCorrelationID((String) headers.remove(Stomp.Headers.Send.CORRELATION_ID));
|
||||
|
||||
Object o = headers.remove(Stomp.Headers.Send.EXPIRATION_TIME);
|
||||
if (o != null) {
|
||||
msg.setJMSExpiration(Long.parseLong((String) o));
|
||||
}
|
||||
|
||||
o = headers.remove(Stomp.Headers.Send.PRIORITY);
|
||||
if (o != null) {
|
||||
msg.setJMSPriority(Integer.parseInt((String)o));
|
||||
}
|
||||
|
||||
o = headers.remove(Stomp.Headers.Send.TYPE);
|
||||
if (o != null) {
|
||||
msg.setJMSType((String) o);
|
||||
}
|
||||
|
||||
o = headers.remove(Stomp.Headers.Send.REPLY_TO);
|
||||
if( o!=null ) {
|
||||
msg.setJMSReplyTo(convertDestination((String)o));
|
||||
}
|
||||
|
||||
o = headers.remove(Stomp.Headers.Send.PERSISTENT);
|
||||
if (o != null) {
|
||||
msg.setPersistent("true".equals(o));
|
||||
}
|
||||
|
||||
// now the general headers
|
||||
msg.setProperties(headers);
|
||||
ActiveMQMessage msg = frameTranslator.convertFrame(command);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public StompFrame convertMessage(ActiveMQMessage message) throws IOException, JMSException {
|
||||
|
||||
StompFrame command = new StompFrame();
|
||||
command.setAction(Stomp.Responses.MESSAGE);
|
||||
|
||||
HashMap headers = new HashMap();
|
||||
command.setHeaders(headers);
|
||||
|
||||
headers.put(Stomp.Headers.Message.DESTINATION, convertDestination(message.getDestination()));
|
||||
headers.put(Stomp.Headers.Message.MESSAGE_ID, message.getJMSMessageID());
|
||||
if (message.getJMSCorrelationID() != null) {
|
||||
headers.put(Stomp.Headers.Message.CORRELATION_ID, message.getJMSCorrelationID());
|
||||
}
|
||||
headers.put(Stomp.Headers.Message.EXPIRATION_TIME, ""+message.getJMSExpiration());
|
||||
if (message.getJMSRedelivered()) {
|
||||
headers.put(Stomp.Headers.Message.REDELIVERED, "true");
|
||||
}
|
||||
headers.put(Stomp.Headers.Message.PRORITY, ""+message.getJMSPriority());
|
||||
if (message.getJMSReplyTo() != null) {
|
||||
headers.put(Stomp.Headers.Message.REPLY_TO, convertDestination(message.getJMSReplyTo()));
|
||||
}
|
||||
headers.put(Stomp.Headers.Message.TIMESTAMP, ""+message.getJMSTimestamp());
|
||||
if (message.getJMSType() != null) {
|
||||
headers.put(Stomp.Headers.Message.TYPE, message.getJMSType());
|
||||
}
|
||||
|
||||
// now lets add all the message headers
|
||||
Map properties = message.getProperties();
|
||||
if (properties != null) {
|
||||
headers.putAll(properties);
|
||||
}
|
||||
|
||||
if( message.getDataStructureType() == ActiveMQTextMessage.DATA_STRUCTURE_TYPE ) {
|
||||
|
||||
ActiveMQTextMessage msg = (ActiveMQTextMessage)message.copy();
|
||||
command.setContent(msg.getText().getBytes("UTF-8"));
|
||||
|
||||
} else if( message.getDataStructureType() == ActiveMQBytesMessage.DATA_STRUCTURE_TYPE ) {
|
||||
|
||||
ActiveMQBytesMessage msg = (ActiveMQBytesMessage)message.copy();
|
||||
byte[] data = new byte[(int)msg.getBodyLength()];
|
||||
msg.readBytes(data);
|
||||
|
||||
headers.put(Stomp.Headers.CONTENT_LENGTH, ""+data.length);
|
||||
command.setContent(data);
|
||||
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
protected ActiveMQDestination convertDestination(String name) throws ProtocolException {
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
else if (name.startsWith("/queue/")) {
|
||||
String q_name = name.substring("/queue/".length(), name.length());
|
||||
return ActiveMQDestination.createDestination(q_name, ActiveMQDestination.QUEUE_TYPE);
|
||||
}
|
||||
else if (name.startsWith("/topic/")) {
|
||||
String t_name = name.substring("/topic/".length(), name.length());
|
||||
return ActiveMQDestination.createDestination(t_name, ActiveMQDestination.TOPIC_TYPE);
|
||||
}
|
||||
else {
|
||||
throw new ProtocolException("Illegal destination name: [" + name + "] -- ActiveMQ STOMP destinations " + "must begine with /queue/ or /topic/");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected String convertDestination(Destination d) {
|
||||
if (d == null) {
|
||||
return null;
|
||||
}
|
||||
ActiveMQDestination amq_d = (ActiveMQDestination) d;
|
||||
String p_name = amq_d.getPhysicalName();
|
||||
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
if (amq_d.isQueue()) {
|
||||
buffer.append("/queue/");
|
||||
}
|
||||
if (amq_d.isTopic()) {
|
||||
buffer.append("/topic/");
|
||||
}
|
||||
buffer.append(p_name);
|
||||
|
||||
return buffer.toString();
|
||||
return frameTranslator.convertMessage(message);
|
||||
}
|
||||
|
||||
public StompTransportFilter getTransportFilter() {
|
||||
return transportFilter;
|
||||
}
|
||||
|
||||
public void setTransportFilter(StompTransportFilter transportFilter) {
|
||||
this.transportFilter = transportFilter;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ public class StompTransportFactory extends TcpTransportFactory {
|
|||
}
|
||||
|
||||
public Transport compositeConfigure(Transport transport, WireFormat format, Map options) {
|
||||
transport = new StompTransportFilter(transport);
|
||||
transport = new StompTransportFilter(transport, new LegacyFrameTranslator());
|
||||
return super.compositeConfigure(transport, format, options);
|
||||
}
|
||||
}
|
|
@ -37,14 +37,17 @@ import org.apache.activemq.util.IOExceptionSupport;
|
|||
*/
|
||||
public class StompTransportFilter extends TransportFilter {
|
||||
|
||||
ProtocolConverter protocolConverter = new ProtocolConverter();
|
||||
private final ProtocolConverter protocolConverter;
|
||||
|
||||
private final Object sendToActiveMQMutex = new Object();
|
||||
private final Object sendToStompMutex = new Object();
|
||||
|
||||
public StompTransportFilter(Transport next) {
|
||||
private final FrameTranslator frameTranslator;
|
||||
|
||||
public StompTransportFilter(Transport next, FrameTranslator translator) {
|
||||
super(next);
|
||||
protocolConverter.setTransportFilter(this);
|
||||
this.frameTranslator = translator;
|
||||
this.protocolConverter = new ProtocolConverter(this, translator);
|
||||
}
|
||||
|
||||
public void oneway(Command command) throws IOException {
|
||||
|
@ -77,4 +80,8 @@ public class StompTransportFilter extends TransportFilter {
|
|||
}
|
||||
}
|
||||
|
||||
public FrameTranslator getFrameTranslator()
|
||||
{
|
||||
return frameTranslator;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,10 +49,7 @@ public class StompSubscriptionRemoveTest extends TestCase {
|
|||
private Socket stompSocket;
|
||||
private ByteArrayOutputStream inputBuffer;
|
||||
|
||||
/**
|
||||
* @param args
|
||||
* @throws Exception
|
||||
*/
|
||||
|
||||
public void testRemoveSubscriber() throws Exception {
|
||||
BrokerService broker = new BrokerService();
|
||||
broker.setPersistent(false);
|
||||
|
|
Loading…
Reference in New Issue