This closes #1953
This commit is contained in:
commit
77172dece3
|
@ -278,8 +278,9 @@ public class MQTTPublishManager {
|
|||
log.warn("Unable to send message: " + message.getMessageID() + " Cause: " + e.getMessage(), e);
|
||||
}
|
||||
default:
|
||||
ActiveMQBuffer bufferDup = message.getReadOnlyBodyBuffer();
|
||||
payload = bufferDup.readBytes(bufferDup.writerIndex()).byteBuf();
|
||||
ActiveMQBuffer bodyBuffer = message.getReadOnlyBodyBuffer();
|
||||
payload = ByteBufAllocator.DEFAULT.buffer(bodyBuffer.writerIndex());
|
||||
payload.writeBytes(bodyBuffer.byteBuf());
|
||||
break;
|
||||
}
|
||||
session.getProtocolHandler().send(messageId, address, qos, isRetain, payload, deliveryCount);
|
||||
|
|
|
@ -103,6 +103,15 @@
|
|||
<groupId>org.jboss.logmanager</groupId>
|
||||
<artifactId>jboss-logmanager</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.fusesource.mqtt-client</groupId>
|
||||
<artifactId>mqtt-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.paho</groupId>
|
||||
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
|
||||
<version>RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -153,6 +162,22 @@
|
|||
<instance>${basedir}/target/expire</instance>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<phase>test-compile</phase>
|
||||
<id>create-mqtt</id>
|
||||
<goals>
|
||||
<goal>create</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<!-- this makes it easier in certain envs -->
|
||||
<configuration>${basedir}/target/classes/servers/mqtt</configuration>
|
||||
<allowAnonymous>true</allowAnonymous>
|
||||
<user>admin</user>
|
||||
<password>admin</password>
|
||||
<instance>${basedir}/target/mqtt</instance>
|
||||
<configuration>${basedir}/target/classes/servers/mqtt</configuration>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
</executions>
|
||||
<dependencies>
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
<?xml version='1.0'?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<configuration xmlns="urn:activemq"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
|
||||
|
||||
<core xmlns="urn:activemq:core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="urn:activemq:core ">
|
||||
|
||||
<name>0.0.0.0</name>
|
||||
|
||||
<persistence-enabled>true</persistence-enabled>
|
||||
|
||||
<!-- this could be ASYNCIO, MAPPED, NIO
|
||||
ASYNCIO: Linux Libaio
|
||||
MAPPED: mmap files
|
||||
NIO: Plain Java Files
|
||||
-->
|
||||
<journal-type>NIO</journal-type>
|
||||
|
||||
<paging-directory>./data/paging</paging-directory>
|
||||
|
||||
<bindings-directory>./data/bindings</bindings-directory>
|
||||
|
||||
<journal-directory>./data/journal</journal-directory>
|
||||
|
||||
<large-messages-directory>./data/large-messages</large-messages-directory>
|
||||
|
||||
<journal-datasync>true</journal-datasync>
|
||||
|
||||
<journal-min-files>2</journal-min-files>
|
||||
|
||||
<journal-pool-files>-1</journal-pool-files>
|
||||
|
||||
<journal-buffer-size>10485760</journal-buffer-size>
|
||||
|
||||
<!--
|
||||
You can specify the NIC you want to use to verify if the network
|
||||
<network-check-NIC>theNickName</network-check-NIC>
|
||||
-->
|
||||
|
||||
<!--
|
||||
Use this to use an HTTP server to validate the network
|
||||
<network-check-URL-list>http://www.apache.org</network-check-URL-list> -->
|
||||
|
||||
<!-- <network-check-period>10000</network-check-period> -->
|
||||
<!-- <network-check-timeout>1000</network-check-timeout> -->
|
||||
|
||||
<!-- this is a comma separated list, no spaces, just DNS or IPs
|
||||
it should accept IPV6
|
||||
|
||||
Warning: Make sure you understand your network topology as this is meant to validate if your network is valid.
|
||||
Using IPs that could eventually disappear or be partially visible may defeat the purpose.
|
||||
You can use a list of multiple IPs, and if any successful ping will make the server OK to continue running -->
|
||||
<!-- <network-check-list>10.0.0.1</network-check-list> -->
|
||||
|
||||
<!-- use this to customize the ping used for ipv4 addresses -->
|
||||
<!-- <network-check-ping-command>ping -c 1 -t %d %s</network-check-ping-command> -->
|
||||
|
||||
<!-- use this to customize the ping used for ipv6 addresses -->
|
||||
<!-- <network-check-ping6-command>ping6 -c 1 %2$s</network-check-ping6-command> -->
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
This value was determined through a calculation.
|
||||
Your system could perform 0.15 writes per millisecond
|
||||
on the current journal configuration.
|
||||
That translates as a sync write every 6488000 nanoseconds
|
||||
-->
|
||||
<journal-buffer-timeout>6488000</journal-buffer-timeout>
|
||||
|
||||
|
||||
<!-- how often we are looking for how many bytes are being used on the disk in ms -->
|
||||
<disk-scan-period>5000</disk-scan-period>
|
||||
|
||||
<!-- once the disk hits this limit the system will block, or close the connection in certain protocols
|
||||
that won't support flow control. -->
|
||||
<max-disk-usage>90</max-disk-usage>
|
||||
|
||||
<!-- the system will enter into page mode once you hit this limit.
|
||||
This is an estimate in bytes of how much the messages are using in memory
|
||||
|
||||
The system will use half of the available memory (-Xmx) by default for the global-max-size.
|
||||
You may specify a different value here if you need to customize it to your needs.
|
||||
|
||||
<global-max-size>100Mb</global-max-size>
|
||||
|
||||
-->
|
||||
<global-max-size>100Mb</global-max-size>
|
||||
|
||||
<acceptors>
|
||||
|
||||
<!-- useEpoll means: it will use Netty epoll if you are on a system (Linux) that supports it -->
|
||||
<!-- amqpCredits: The number of credits sent to AMQP producers -->
|
||||
<!-- amqpLowCredits: The server will send the # credits specified at amqpCredits at this low mark -->
|
||||
|
||||
<!-- Acceptor for every supported protocol -->
|
||||
<acceptor name="artemis">tcp://0.0.0.0:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE;useEpoll=true;amqpCredits=1000;amqpLowCredits=300</acceptor>
|
||||
|
||||
<!-- AMQP Acceptor. Listens on default AMQP port for AMQP traffic.-->
|
||||
<acceptor name="amqp">tcp://0.0.0.0:5672?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=AMQP;useEpoll=true;amqpCredits=1000;amqpMinCredits=300</acceptor>
|
||||
|
||||
<!-- STOMP Acceptor. -->
|
||||
<acceptor name="stomp">tcp://0.0.0.0:61613?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=STOMP;useEpoll=true</acceptor>
|
||||
|
||||
<!-- HornetQ Compatibility Acceptor. Enables HornetQ Core and STOMP for legacy HornetQ clients. -->
|
||||
<acceptor name="hornetq">tcp://0.0.0.0:5445?protocols=HORNETQ,STOMP;useEpoll=true</acceptor>
|
||||
|
||||
<!-- MQTT Acceptor -->
|
||||
<acceptor name="mqtt">tcp://0.0.0.0:1883?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=MQTT;useEpoll=true</acceptor>
|
||||
|
||||
</acceptors>
|
||||
|
||||
|
||||
<security-settings>
|
||||
<security-setting match="#">
|
||||
<permission type="createNonDurableQueue" roles="guest"/>
|
||||
<permission type="deleteNonDurableQueue" roles="guest"/>
|
||||
<permission type="createDurableQueue" roles="guest"/>
|
||||
<permission type="deleteDurableQueue" roles="guest"/>
|
||||
<permission type="createAddress" roles="guest"/>
|
||||
<permission type="deleteAddress" roles="guest"/>
|
||||
<permission type="consume" roles="guest"/>
|
||||
<permission type="browse" roles="guest"/>
|
||||
<permission type="send" roles="guest"/>
|
||||
<!-- we need this otherwise ./artemis data imp wouldn't work -->
|
||||
<permission type="manage" roles="guest"/>
|
||||
</security-setting>
|
||||
</security-settings>
|
||||
|
||||
<address-settings>
|
||||
<!-- if you define auto-create on certain queues, management has to be auto-create -->
|
||||
<address-setting match="activemq.management#">
|
||||
<dead-letter-address>DLQ</dead-letter-address>
|
||||
<expiry-address>ExpiryQueue</expiry-address>
|
||||
<redelivery-delay>0</redelivery-delay>
|
||||
<!-- with -1 only the global-max-size is in use for limiting -->
|
||||
<!-- <max-size-bytes>-1</max-size-bytes> -->
|
||||
<max-size-bytes>1M</max-size-bytes>
|
||||
<page-size-bytes>50000</page-size-bytes>
|
||||
<message-counter-history-day-limit>10</message-counter-history-day-limit>
|
||||
<address-full-policy>PAGE</address-full-policy>
|
||||
<auto-create-queues>true</auto-create-queues>
|
||||
<auto-create-addresses>true</auto-create-addresses>
|
||||
<auto-create-jms-queues>true</auto-create-jms-queues>
|
||||
<auto-create-jms-topics>true</auto-create-jms-topics>
|
||||
</address-setting>
|
||||
<!--default for catch all-->
|
||||
<address-setting match="#">
|
||||
<dead-letter-address>DLQ</dead-letter-address>
|
||||
<expiry-address>ExpiryQueue</expiry-address>
|
||||
<redelivery-delay>0</redelivery-delay>
|
||||
<!-- with -1 only the global-max-size is in use for limiting -->
|
||||
<!-- <max-size-bytes>-1</max-size-bytes> -->
|
||||
<page-size-bytes>50000</page-size-bytes>
|
||||
<message-counter-history-day-limit>10</message-counter-history-day-limit>
|
||||
<address-full-policy>PAGE</address-full-policy>
|
||||
<auto-create-queues>true</auto-create-queues>
|
||||
<auto-create-addresses>true</auto-create-addresses>
|
||||
<auto-create-jms-queues>true</auto-create-jms-queues>
|
||||
<auto-create-jms-topics>true</auto-create-jms-topics>
|
||||
</address-setting>
|
||||
</address-settings>
|
||||
|
||||
<addresses>
|
||||
<address name="DLQ">
|
||||
<anycast>
|
||||
<queue name="DLQ" />
|
||||
</anycast>
|
||||
</address>
|
||||
<address name="ExpiryQueue">
|
||||
<anycast>
|
||||
<queue name="ExpiryQueue" />
|
||||
</anycast>
|
||||
</address>
|
||||
|
||||
</addresses>
|
||||
|
||||
</core>
|
||||
</configuration>
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* 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.smoke.mqtt;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import org.apache.activemq.artemis.tests.smoke.common.SmokeTestBase;
|
||||
import org.apache.activemq.artemis.util.ServerUtil;
|
||||
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
|
||||
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
|
||||
import org.eclipse.paho.client.mqttv3.MqttCallback;
|
||||
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
|
||||
import org.eclipse.paho.client.mqttv3.MqttException;
|
||||
import org.eclipse.paho.client.mqttv3.MqttMessage;
|
||||
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class MQTTLeakTest extends SmokeTestBase {
|
||||
|
||||
public static final String SERVER_NAME_0 = "mqtt";
|
||||
|
||||
private static Process server0;
|
||||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
cleanupData(SERVER_NAME_0);
|
||||
disableCheckThread();
|
||||
}
|
||||
|
||||
@After
|
||||
@Override
|
||||
public void after() throws Exception {
|
||||
super.after();
|
||||
cleanupData(SERVER_NAME_0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMQTTLeak() throws Throwable {
|
||||
|
||||
try {
|
||||
server0 = startServer(SERVER_NAME_0, 0, 30000);
|
||||
MQTTRunner.run();
|
||||
} finally {
|
||||
|
||||
ServerUtil.killServer(server0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static class MQTTRunner implements MqttCallback {
|
||||
|
||||
private MqttAsyncClient mqttClient;
|
||||
private MqttConnectOptions connOpts;
|
||||
protected static MQTTRunner publisherClient;
|
||||
protected static MQTTRunner consumerClient;
|
||||
|
||||
private static String topicPaho1 = "State/PRN/";
|
||||
private static String topicPaho2 = "Soap/PRN/";
|
||||
public String name;
|
||||
|
||||
private static final Semaphore semaphore = new Semaphore(2);
|
||||
|
||||
public static void run() throws Exception {
|
||||
publisherClient = new MQTTRunner();
|
||||
publisherClient.connect();
|
||||
publisherClient.name = "Pub";
|
||||
consumerClient = new MQTTRunner();
|
||||
consumerClient.connect();
|
||||
consumerClient.name = "Consumer";
|
||||
byte[] content = buildContent();
|
||||
|
||||
for (int idx = 0; idx < 500; idx++) {
|
||||
if (idx % 100 == 0) {
|
||||
System.out.println("Sent " + idx + " messages");
|
||||
}
|
||||
MqttMessage msg = new MqttMessage(content);
|
||||
semaphore.acquire(2);
|
||||
publisherClient.mqttClient.publish(topicPaho1, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
// create a new Paho MqttClient
|
||||
MemoryPersistence persistence = new MemoryPersistence();
|
||||
// establish the client ID for the life of this DPI publisherClient
|
||||
String clientId = UUID.randomUUID().toString();
|
||||
try {
|
||||
mqttClient = new MqttAsyncClient("tcp://localhost:1883", clientId, persistence);
|
||||
// Create a set of connection options
|
||||
connOpts = new MqttConnectOptions();
|
||||
connOpts.setCleanSession(true);
|
||||
mqttClient.connect(connOpts);
|
||||
} catch (MqttException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// pause a moment to get connected (prevents the race condition)
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// subscribe
|
||||
try {
|
||||
String[] topicsPaho = new String[]{topicPaho1, topicPaho2};
|
||||
int[] qos = new int[]{0, 0};
|
||||
mqttClient.subscribe(topicsPaho, qos);
|
||||
} catch (MqttException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
mqttClient.setCallback(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionLost(Throwable throwable) {
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
@Override
|
||||
public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
|
||||
|
||||
count++;
|
||||
|
||||
if (count % 100 == 0) {
|
||||
System.out.println("Received " + count);
|
||||
}
|
||||
|
||||
semaphore.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliveryComplete(IMqttDeliveryToken token) {
|
||||
}
|
||||
|
||||
public static byte[] buildContent() {
|
||||
|
||||
ArrayList<String> stringval2 = buildContentArray();
|
||||
int size = 0;
|
||||
for (String value : stringval2) {
|
||||
size += value.length();
|
||||
}
|
||||
System.out.println();
|
||||
StringBuilder builder = new StringBuilder(size);
|
||||
for (String value : stringval2) {
|
||||
builder.append(value);
|
||||
}
|
||||
String msgContent = builder.toString();
|
||||
|
||||
return msgContent.getBytes();
|
||||
}
|
||||
|
||||
public static ArrayList<String> buildContentArray() {
|
||||
ArrayList<String> val = new ArrayList<>();
|
||||
String msgHdr = "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"><SOAP-ENV:Header/><SOAP-ENV:Body><ns5:ExchangeMessage xmlns:ns5=\"urn:dpcl:wsdl:2011-09-02\" xmlns:ns3=\"http://www.w3.org/2004/08/xop/include\" xmlns:ns6=\"urn:dpcl:wsdl:2010-01-19\" xmlns:xmime=\"http://www.w3.org/2005/05/xmlmime\" xmlns=\"\"><ExchangeMessageInput><data xmime:contentType=\"application/vnd.dpcl.update_transfer+xml\"><base64>";
|
||||
String msgChunk = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9InllcyI/Pgo8bnMyOlRyYW5zZmVyIHhtbG5zOm5zMj0idXJuOmRwY2w6dXBkYXRlOjIwMTEtMTAtMTkiPgogICAgPGltYWdlU2VnbWVudD4KICAgICAgICA8Ym9hcmQ+MjU5PC9ib2FyZD4KICAgICAgICA8Y2F0ZWdvcnk+MjwvY2F0ZWdvcnk+CiAgICAgICAgPHZlcnNpb24+Mjg1NDA5Mjg1PC92ZXJzaW9uPgogICAgICAgIDxpZD4yNjwvaWQ+CiAgICAgICAgPHNpemU+MjA5NzE1Mjwvc2l6ZT4KICAgICAgICA8Y2hlY2tzdW0+NTE0ODI3MGJmZTM2ZmYzNmIyZTNmMjc0NWJlNmYyMGY8L2NoZWNrc3VtPgogICAgICAgIDxkYXRhPm5OQUJ1WHQvWG0xYlhGeC9aallZbEJ1K2NrWU1ncHBTMnZpTVZoOUxjTENjTFlTL1Z6YUxlSWNnWmtlMjI5Z1dlS1p6czlSclBrdVlsSHYvaWNlSldJeTUxaGFpVUx3NTY0NWtTTUlhMEhjNnZoYTB5UC91OEVNUEcvck9LL1JhVXpuS0tRdXF5WVNDVlZ3TWROS25IWjZ5Sm91TkdMcVJ3a0MvVDZUdStrTWxKak9TcjV6MUNYWDdtZWdvSGpLdkFuU1AyOFJWY0F3MWVXTUtIY0pQU0Z0bFZXSkFYVXErZjFzbE9HWXlNSGhiN2haV0VnMWc4TlRlVUJ2NHJGL0RtUitKRjRmbjlWdkRJSkJYanJpeE5CNWFyc1RKOTR3dEF2YWxVM28vVzVnODltbURNNHp0VlVuaHZvSlRTSlZ6bXlqTGpJMWQ5OExVVTVWU3dqWE5KMjZ2d0F4R1ptVmwrVGlMU0JaeWNYak45NlYxVUZ6eldOMStPN2h5SHRMZnMvOE9kRjVMK1ArbjZXOXNqNVA3aDdGZUU4UFVHbGpLcXhxWmFGbFZ4aXJPRjYrUExGTHFFMzAzUzVodzJPeDFBQjA5Sjl4VThjVXNtUVI0dlJBS3B0Y3ZpbXkzb1VncmxWQTBwNG83cFdlYkduak1kT1N6ZGR2M01uMi9rMldlOVRHNzI3OEhkdTdLQlNtVW95VTJSM0l6TitITXhXeGQ4";
|
||||
|
||||
val.add(msgHdr);
|
||||
for (int idx = 0; idx < 300; idx++) {
|
||||
val.add(msgChunk);
|
||||
val.add(msgChunk);
|
||||
val.add(msgChunk);
|
||||
val.add(msgChunk);
|
||||
val.add(msgChunk);
|
||||
val.add(msgChunk);
|
||||
val.add(msgChunk);
|
||||
val.add(msgChunk);
|
||||
val.add(msgChunk);
|
||||
val.add(msgChunk);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue