/*
* 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.artemis.tests.integration.stomp;
import javax.jms.BytesMessage;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.activemq.artemis.api.core.TransportConfiguration;
import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.protocol.mqtt.MQTTProtocolManagerFactory;
import org.apache.activemq.artemis.core.protocol.stomp.Stomp;
import org.apache.activemq.artemis.core.protocol.stomp.StompProtocolManagerFactory;
import org.apache.activemq.artemis.core.registry.JndiBindingRegistry;
import org.apache.activemq.artemis.core.remoting.impl.invm.InVMAcceptorFactory;
import org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnectorFactory;
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptorFactory;
import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
import org.apache.activemq.artemis.core.security.Role;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.ActiveMQServers;
import org.apache.activemq.artemis.api.core.RoutingType;
import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
import org.apache.activemq.artemis.jms.client.ActiveMQJMSConnectionFactory;
import org.apache.activemq.artemis.jms.server.JMSServerManager;
import org.apache.activemq.artemis.jms.server.config.JMSConfiguration;
import org.apache.activemq.artemis.jms.server.config.impl.JMSConfigurationImpl;
import org.apache.activemq.artemis.jms.server.config.impl.JMSQueueConfigurationImpl;
import org.apache.activemq.artemis.jms.server.config.impl.TopicConfigurationImpl;
import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl;
import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger;
import org.apache.activemq.artemis.tests.integration.stomp.util.ClientStompFrame;
import org.apache.activemq.artemis.tests.integration.stomp.util.StompClientConnection;
import org.apache.activemq.artemis.tests.unit.util.InVMNamingContext;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.junit.Before;
public abstract class StompTestBase extends ActiveMQTestBase {
protected String hostname = "127.0.0.1";
protected final int port = 61613;
private ConnectionFactory connectionFactory;
protected Connection connection;
protected Session session;
protected Queue queue;
protected Topic topic;
protected JMSServerManager server;
protected String defUser = "brianm";
protected String defPass = "wombats";
// Implementation methods
// -------------------------------------------------------------------------
public boolean isCompressLargeMessages() {
return false;
}
public boolean isSecurityEnabled() {
return false;
}
public boolean isPersistenceEnabled() {
return false;
}
public boolean isEnableStompMessageId() {
return false;
}
public Integer getStompMinLargeMessageSize() {
return null;
}
public List<String> getIncomingInterceptors() {
return null;
}
public List<String> getOutgoingInterceptors() {
return null;
}
@Override
@Before
public void setUp() throws Exception {
super.setUp();
server = createServer();
server.start();
connectionFactory = createConnectionFactory();
((ActiveMQConnectionFactory)connectionFactory).setCompressLargeMessage(isCompressLargeMessages());
if (isSecurityEnabled()) {
connection = connectionFactory.createConnection("brianm", "wombats");
} else {
connection = connectionFactory.createConnection();
}
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
queue = session.createQueue(getQueueName());
topic = session.createTopic(getTopicName());
connection.start();
}
/**
* @return
* @throws Exception
*/
protected JMSServerManager createServer() throws Exception {
Map<String, Object> params = new HashMap<>();
params.put(TransportConstants.PROTOCOLS_PROP_NAME, StompProtocolManagerFactory.STOMP_PROTOCOL_NAME + "," + MQTTProtocolManagerFactory.MQTT_PROTOCOL_NAME);
params.put(TransportConstants.PORT_PROP_NAME, TransportConstants.DEFAULT_STOMP_PORT);
params.put(TransportConstants.STOMP_CONSUMERS_CREDIT, "-1");
if (isEnableStompMessageId()) {
params.put(TransportConstants.STOMP_ENABLE_MESSAGE_ID, true);
}
if (getStompMinLargeMessageSize() != null) {
params.put(TransportConstants.STOMP_MIN_LARGE_MESSAGE_SIZE, 2048);
}
TransportConfiguration stompTransport = new TransportConfiguration(NettyAcceptorFactory.class.getName(), params);
Configuration config = createBasicConfig().setSecurityEnabled(isSecurityEnabled())
.setPersistenceEnabled(isPersistenceEnabled())
.addAcceptorConfiguration(stompTransport)
.addAcceptorConfiguration(new TransportConfiguration(InVMAcceptorFactory.class.getName()))
.setConnectionTtlCheckInterval(500);
if (getIncomingInterceptors() != null) {
config.setIncomingInterceptorClassNames(getIncomingInterceptors());
}
if (getOutgoingInterceptors() != null) {
config.setOutgoingInterceptorClassNames(getOutgoingInterceptors());
}
ActiveMQServer activeMQServer = addServer(ActiveMQServers.newActiveMQServer(config, defUser, defPass));
if (isSecurityEnabled()) {
ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) activeMQServer.getSecurityManager();
final String role = "testRole";
securityManager.getConfiguration().addRole(defUser, role);
config.getSecurityRoles().put("#", new HashSet<Role>() {
{
add(new Role(role, true, true, true, true, true, true, true, true, true, true));
}
});
}
JMSConfiguration jmsConfig = new JMSConfigurationImpl();
jmsConfig.getQueueConfigurations().add(new JMSQueueConfigurationImpl().setName(getQueueName()).setBindings(getQueueName()));
jmsConfig.getTopicConfigurations().add(new TopicConfigurationImpl().setName(getTopicName()).setBindings(getTopicName()));
server = new JMSServerManagerImpl(activeMQServer, jmsConfig);
server.setRegistry(new JndiBindingRegistry(new InVMNamingContext()));
return server;
}
protected ConnectionFactory createConnectionFactory() {
return new ActiveMQJMSConnectionFactory(false, new TransportConfiguration(InVMConnectorFactory.class.getName()));
}
protected String getQueueName() {
return "testQueue";
}
protected String getQueuePrefix() {
return "";
}
protected String getTopicName() {
return "testtopic";
}
protected String getTopicPrefix() {
return "";
}
public void sendJmsMessage(String msg) throws Exception {
sendJmsMessage(msg, queue);
}
public void sendJmsMessage(String msg, Destination destination) throws Exception {
MessageProducer producer = session.createProducer(destination);
TextMessage message = session.createTextMessage(msg);
producer.send(message);
}
public void sendJmsMessage(byte[] data, Destination destination) throws Exception {
sendJmsMessage(data, "foo", "xyz", destination);
}
public void sendJmsMessage(String msg, String propertyName, String propertyValue) throws Exception {
sendJmsMessage(msg.getBytes(StandardCharsets.UTF_8), propertyName, propertyValue, queue);
}
public void sendJmsMessage(byte[] data,
String propertyName,
String propertyValue,
Destination destination) throws Exception {
MessageProducer producer = session.createProducer(destination);
BytesMessage message = session.createBytesMessage();
message.setStringProperty(propertyName, propertyValue);
message.writeBytes(data);
producer.send(message);
}
public void abortTransaction(StompClientConnection conn, String txID) throws IOException, InterruptedException {
ClientStompFrame abortFrame = conn.createFrame(Stomp.Commands.ABORT)
.addHeader(Stomp.Headers.TRANSACTION, txID);
conn.sendFrame(abortFrame);
}
public void beginTransaction(StompClientConnection conn, String txID) throws IOException, InterruptedException {
ClientStompFrame beginFrame = conn.createFrame(Stomp.Commands.BEGIN)
.addHeader(Stomp.Headers.TRANSACTION, txID);
conn.sendFrame(beginFrame);
}
public void commitTransaction(StompClientConnection conn, String txID) throws IOException, InterruptedException {
commitTransaction(conn, txID, false);
}
public void commitTransaction(StompClientConnection conn,
String txID,
boolean receipt) throws IOException, InterruptedException {
ClientStompFrame beginFrame = conn.createFrame(Stomp.Commands.COMMIT)
.addHeader(Stomp.Headers.TRANSACTION, txID);
String uuid = UUID.randomUUID().toString();
if (receipt) {
beginFrame.addHeader(Stomp.Headers.RECEIPT_REQUESTED, uuid);
}
ClientStompFrame resp = conn.sendFrame(beginFrame);
if (receipt) {
assertEquals(uuid, resp.getHeader(Stomp.Headers.Response.RECEIPT_ID));
}
}
public void ack(StompClientConnection conn,
String subscriptionId,
ClientStompFrame messageIdFrame) throws IOException, InterruptedException {
String messageID = messageIdFrame.getHeader(Stomp.Headers.Message.MESSAGE_ID);
ClientStompFrame frame = conn.createFrame(Stomp.Commands.ACK)
.addHeader(Stomp.Headers.Message.MESSAGE_ID, messageID);
if (subscriptionId != null) {
frame.addHeader(Stomp.Headers.Ack.SUBSCRIPTION, subscriptionId);
}
ClientStompFrame response = conn.sendFrame(frame);
if (response != null) {
throw new IOException("failed to ack " + response);
}
}
public void ack(StompClientConnection conn,
String subscriptionId,
String mid,
String txID) throws IOException, InterruptedException {
ClientStompFrame frame = conn.createFrame(Stomp.Commands.ACK)
.addHeader(Stomp.Headers.Ack.SUBSCRIPTION, subscriptionId)
.addHeader(Stomp.Headers.Message.MESSAGE_ID, mid);
if (txID != null) {
frame.addHeader(Stomp.Headers.TRANSACTION, txID);
}
conn.sendFrame(frame);
}
public void nack(StompClientConnection conn, String subscriptionId, String messageId) throws IOException, InterruptedException {
ClientStompFrame frame = conn.createFrame(Stomp.Commands.NACK)
.addHeader(Stomp.Headers.Ack.SUBSCRIPTION, subscriptionId)
.addHeader(Stomp.Headers.Message.MESSAGE_ID, messageId);
conn.sendFrame(frame);
}
public ClientStompFrame subscribe(StompClientConnection conn,
String subscriptionId) throws IOException, InterruptedException {
return subscribe(conn, subscriptionId, Stomp.Headers.Subscribe.AckModeValues.AUTO);
}
public ClientStompFrame subscribe(StompClientConnection conn,
String subscriptionId,
String ack) throws IOException, InterruptedException {
return subscribe(conn, subscriptionId, ack, null);
}
public ClientStompFrame subscribe(StompClientConnection conn,
String subscriptionId,
String ack,
String durableId) throws IOException, InterruptedException {
return subscribe(conn, subscriptionId, ack, durableId, false);
}
public ClientStompFrame subscribe(StompClientConnection conn,
String subscriptionId,
String ack,
String durableId,
boolean receipt) throws IOException, InterruptedException {
return subscribe(conn, subscriptionId, ack, durableId, null, receipt);
}
public ClientStompFrame subscribe(StompClientConnection conn,
String subscriptionId,
String ack,
String durableId,
String selector) throws IOException, InterruptedException {
return subscribe(conn, subscriptionId, ack, durableId, selector, false);
}
public ClientStompFrame subscribe(StompClientConnection conn,
String subscriptionId,
String ack,
String durableId,
String selector,
boolean receipt) throws IOException, InterruptedException {
return subscribe(conn, subscriptionId, ack, durableId, selector, getQueuePrefix() + getQueueName(), receipt);
}
public void subscribeQueue(StompClientConnection conn, String subId, String destination) throws IOException, InterruptedException {
subscribe(conn, subId, Stomp.Headers.Subscribe.AckModeValues.AUTO, null, null, destination, false);
}
public ClientStompFrame subscribe(StompClientConnection conn,
String subscriptionId,
String ack,
String durableId,
String selector,
String destination,
boolean receipt) throws IOException, InterruptedException {
ClientStompFrame frame = conn.createFrame(Stomp.Commands.SUBSCRIBE)
.addHeader(Stomp.Headers.Subscribe.SUBSCRIPTION_TYPE, RoutingType.ANYCAST.toString())
.addHeader(Stomp.Headers.Subscribe.DESTINATION, destination);
if (subscriptionId != null) {
frame.addHeader(Stomp.Headers.Subscribe.ID, subscriptionId);
}
if (ack != null) {
frame.addHeader(Stomp.Headers.Subscribe.ACK_MODE, ack);
}
if (durableId != null) {
frame.addHeader(Stomp.Headers.Subscribe.DURABLE_SUBSCRIPTION_NAME, durableId);
}
if (selector != null) {
frame.addHeader(Stomp.Headers.Subscribe.SELECTOR, selector);
}
String uuid = UUID.randomUUID().toString();
if (receipt) {
frame.addHeader(Stomp.Headers.RECEIPT_REQUESTED, uuid);
}
frame = conn.sendFrame(frame);
if (receipt) {
assertEquals(uuid, frame.getHeader(Stomp.Headers.Response.RECEIPT_ID));
}
return frame;
}
public ClientStompFrame subscribeTopic(StompClientConnection conn,
String subscriptionId,
String ack,
String durableId) throws IOException, InterruptedException {
return subscribeTopic(conn, subscriptionId, ack, durableId, false);
}
public ClientStompFrame subscribeTopic(StompClientConnection conn,
String subscriptionId,
String ack,
String durableId,
boolean receipt) throws IOException, InterruptedException {
return subscribeTopic(conn, subscriptionId, ack, durableId, receipt, false);
}
public ClientStompFrame subscribeTopic(StompClientConnection conn,
String subscriptionId,
String ack,
String durableId,
boolean receipt,
boolean noLocal) throws IOException, InterruptedException {
ClientStompFrame frame = conn.createFrame(Stomp.Commands.SUBSCRIBE)
.addHeader(Stomp.Headers.Subscribe.SUBSCRIPTION_TYPE, RoutingType.MULTICAST.toString())
.addHeader(Stomp.Headers.Subscribe.DESTINATION, getTopicPrefix() + getTopicName());
if (subscriptionId != null) {
frame.addHeader(Stomp.Headers.Subscribe.ID, subscriptionId);
}
if (ack != null) {
frame.addHeader(Stomp.Headers.Subscribe.ACK_MODE, ack);
}
if (durableId != null) {
frame.addHeader(Stomp.Headers.Subscribe.DURABLE_SUBSCRIPTION_NAME, durableId);
}
String uuid = UUID.randomUUID().toString();
if (receipt) {
frame.addHeader(Stomp.Headers.RECEIPT_REQUESTED, uuid);
}
if (noLocal) {
frame.addHeader(Stomp.Headers.Subscribe.NO_LOCAL, "true");
}
frame = conn.sendFrame(frame);
if (receipt) {
assertNotNull("Requested receipt, but response is null", frame);
assertTrue(frame.getHeader(Stomp.Headers.Response.RECEIPT_ID).equals(uuid));
}
return frame;
}
public ClientStompFrame unsubscribe(StompClientConnection conn, String subscriptionId) throws IOException, InterruptedException {
return unsubscribe(conn, subscriptionId, null, false, false);
}
public ClientStompFrame unsubscribe(StompClientConnection conn,
String subscriptionId,
boolean receipt) throws IOException, InterruptedException {
return unsubscribe(conn, subscriptionId, null, receipt, false);
}
public ClientStompFrame unsubscribe(StompClientConnection conn,
String subscriptionId,
String destination,
boolean receipt,
boolean durable) throws IOException, InterruptedException {
ClientStompFrame frame = conn.createFrame(Stomp.Commands.UNSUBSCRIBE);
if (durable && subscriptionId != null) {
frame.addHeader(Stomp.Headers.Unsubscribe.DURABLE_SUBSCRIPTION_NAME, subscriptionId);
} else if (!durable && subscriptionId != null) {
frame.addHeader(Stomp.Headers.Unsubscribe.ID, subscriptionId);
}
if (destination != null) {
frame.addHeader(Stomp.Headers.Unsubscribe.DESTINATION, destination);
}
String uuid = UUID.randomUUID().toString();
if (receipt) {
frame.addHeader(Stomp.Headers.RECEIPT_REQUESTED, uuid);
}
frame = conn.sendFrame(frame);
if (receipt) {
assertEquals(Stomp.Responses.RECEIPT, frame.getCommand());
assertEquals(uuid, frame.getHeader(Stomp.Headers.Response.RECEIPT_ID));
}
return frame;
}
public ClientStompFrame send(StompClientConnection conn, String destination, String contentType, String body) throws IOException, InterruptedException {
return send(conn, destination, contentType, body, false);
}
public ClientStompFrame send(StompClientConnection conn, String destination, String contentType, String body, boolean receipt) throws IOException, InterruptedException {
return send(conn, destination, contentType, body, receipt, null);
}
public ClientStompFrame send(StompClientConnection conn, String destination, String contentType, String body, boolean receipt, RoutingType destinationType) throws IOException, InterruptedException {
return send(conn, destination, contentType, body, receipt, destinationType, null);
}
public ClientStompFrame send(StompClientConnection conn, String destination, String contentType, String body, boolean receipt, RoutingType destinationType, String txId) throws IOException, InterruptedException {
ClientStompFrame frame = conn.createFrame(Stomp.Commands.SEND)
.addHeader(Stomp.Headers.Send.DESTINATION, destination)
.setBody(body);
if (contentType != null) {
frame.addHeader(Stomp.Headers.CONTENT_TYPE, contentType);
}
if (destinationType != null) {
frame.addHeader(Stomp.Headers.Send.DESTINATION_TYPE, destinationType.toString());
}
if (txId != null) {
frame.addHeader(Stomp.Headers.TRANSACTION, txId);
}
String uuid = UUID.randomUUID().toString();
if (receipt) {
frame.addHeader(Stomp.Headers.RECEIPT_REQUESTED, uuid);
}
frame = conn.sendFrame(frame);
if (receipt) {
assertEquals(Stomp.Responses.RECEIPT, frame.getCommand());
assertEquals(uuid, frame.getHeader(Stomp.Headers.Response.RECEIPT_ID));
}
IntegrationTestLogger.LOGGER.info("Received: " + frame);
return frame;
}
}