/**
* 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.core.protocol.mqtt;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.mqtt.MqttMessage;
import io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader;
import io.netty.handler.codec.mqtt.MqttMessageType;
import io.netty.handler.codec.mqtt.MqttPublishVariableHeader;
import io.netty.handler.codec.mqtt.MqttSubscribeMessage;
import io.netty.handler.codec.mqtt.MqttTopicSubscription;
import org.apache.activemq.artemis.api.core.ICoreMessage;
import org.apache.activemq.artemis.api.core.Message;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.config.WildcardConfiguration;
import org.apache.activemq.artemis.core.message.impl.CoreMessage;
/**
* A Utility Class for creating Server Side objects and converting MQTT concepts to/from Artemis.
*/
public class MQTTUtil {
// TODO These settings should be configurable.
public static final int DEFAULT_SERVER_MESSAGE_BUFFER_SIZE = 512;
public static final boolean DURABLE_MESSAGES = true;
public static final boolean SESSION_AUTO_COMMIT_SENDS = true;
public static final boolean SESSION_AUTO_COMMIT_ACKS = true;
public static final boolean SESSION_PREACKNOWLEDGE = false;
public static final boolean SESSION_XA = false;
public static final boolean SESSION_AUTO_CREATE_QUEUE = false;
public static final int MAX_MESSAGE_SIZE = 268435455;
public static final String MQTT_RETAIN_ADDRESS_PREFIX = "$sys.mqtt.retain.";
public static final String MQTT_QOS_LEVEL_KEY = "mqtt.qos.level";
public static final String MQTT_MESSAGE_ID_KEY = "mqtt.message.id";
public static final String MQTT_MESSAGE_TYPE_KEY = "mqtt.message.type";
public static final String MQTT_MESSAGE_RETAIN_KEY = "mqtt.message.retain";
public static final String MANAGEMENT_QUEUE_PREFIX = "$sys.mqtt.queue.qos2.";
public static final int DEFAULT_KEEP_ALIVE_FREQUENCY = 5000;
public static String convertMQTTAddressFilterToCore(String filter, WildcardConfiguration wildcardConfiguration) {
return MQTT_WILDCARD.convert(filter, wildcardConfiguration);
}
public static class MQTTWildcardConfiguration extends WildcardConfiguration {
public MQTTWildcardConfiguration() {
setDelimiter('/');
setSingleWord('+');
setAnyWords('#');
}
}
public static final WildcardConfiguration MQTT_WILDCARD = new MQTTWildcardConfiguration();
private static final MQTTLogger logger = MQTTLogger.LOGGER;
public static String convertCoreAddressFilterToMQTT(String filter, WildcardConfiguration wildcardConfiguration) {
if (filter.startsWith(MQTT_RETAIN_ADDRESS_PREFIX)) {
filter = filter.substring(MQTT_RETAIN_ADDRESS_PREFIX.length(), filter.length());
}
return wildcardConfiguration.convert(filter, MQTT_WILDCARD);
}
public static String convertMQTTAddressFilterToCoreRetain(String filter, WildcardConfiguration wildcardConfiguration) {
return MQTT_RETAIN_ADDRESS_PREFIX + MQTT_WILDCARD.convert(filter, wildcardConfiguration);
}
private static ICoreMessage createServerMessage(MQTTSession session,
SimpleString address,
boolean retain,
int qos) {
long id = session.getServer().getStorageManager().generateID();
CoreMessage message = new CoreMessage(id, DEFAULT_SERVER_MESSAGE_BUFFER_SIZE);
message.setAddress(address);
message.putBooleanProperty(new SimpleString(MQTT_MESSAGE_RETAIN_KEY), retain);
message.putIntProperty(new SimpleString(MQTT_QOS_LEVEL_KEY), qos);
message.setType(Message.BYTES_TYPE);
return message;
}
public static Message createServerMessageFromByteBuf(MQTTSession session,
String topic,
boolean retain,
int qos,
ByteBuf payload) {
String coreAddress = convertMQTTAddressFilterToCore(topic, session.getWildcardConfiguration());
ICoreMessage message = createServerMessage(session, new SimpleString(coreAddress), retain, qos);
message.getBodyBuffer().writeBytes(payload, 0, payload.readableBytes());
return message;
}
public static Message createPubRelMessage(MQTTSession session, SimpleString address, int messageId) {
Message message = createServerMessage(session, address, false, 1);
message.putIntProperty(new SimpleString(MQTTUtil.MQTT_MESSAGE_ID_KEY), messageId);
message.putIntProperty(new SimpleString(MQTTUtil.MQTT_MESSAGE_TYPE_KEY), MqttMessageType.PUBREL.value());
return message;
}
public static void logMessage(MQTTSessionState state, MqttMessage message, boolean inbound) {
if (logger.isTraceEnabled()) {
StringBuilder log = new StringBuilder("MQTT(");
if (state != null) {
log.append(state.getClientId());
}
if (inbound) {
log.append("): IN << ");
} else {
log.append("): OUT >> ");
}
if (message.fixedHeader() != null) {
log.append(message.fixedHeader().messageType().toString());
if (message.variableHeader() instanceof MqttPublishVariableHeader) {
log.append("(" + ((MqttPublishVariableHeader) message.variableHeader()).messageId() + ") " + message.fixedHeader().qosLevel());
} else if (message.variableHeader() instanceof MqttMessageIdVariableHeader) {
log.append("(" + ((MqttMessageIdVariableHeader) message.variableHeader()).messageId() + ")");
}
if (message.fixedHeader().messageType() == MqttMessageType.SUBSCRIBE) {
for (MqttTopicSubscription sub : ((MqttSubscribeMessage) message).payload().topicSubscriptions()) {
log.append("\n\t" + sub.topicName() + " : " + sub.qualityOfService());
}
}
logger.trace(log.toString());
}
}
}
}