/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed 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.fusesource.mqtt.codec;
import java.io.IOException;
import java.net.ProtocolException;
import static org.fusesource.mqtt.codec.MessageSupport.Message;
import org.fusesource.hawtbuf.DataByteArrayInputStream;
import org.fusesource.hawtbuf.DataByteArrayOutputStream;
import org.fusesource.hawtbuf.UTF8Buffer;
import org.fusesource.mqtt.client.QoS;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class CONNECT implements Message {
public static final byte TYPE = 1;
private static final UTF8Buffer V3_PROTOCOL_NAME = new UTF8Buffer("MQIsdp");
private static final UTF8Buffer V4_PROTOCOL_NAME = new UTF8Buffer("MQTT");
private short keepAlive = 30;
private UTF8Buffer clientId;
private UTF8Buffer willTopic;
private UTF8Buffer willMessage = new UTF8Buffer("");
private boolean willRetain;
private byte willQos;
private boolean cleanSession = true;
private UTF8Buffer userName;
private UTF8Buffer password;
private int version = 3;
public CONNECT(){
}
public CONNECT(CONNECT other) {
this.keepAlive = other.keepAlive;
this.clientId = other.clientId;
this.willTopic = other.willTopic;
this.willMessage = other.willMessage;
this.willRetain = other.willRetain;
this.willQos = other.willQos;
this.cleanSession = other.cleanSession;
this.userName = other.userName;
this.password = other.password;
this.version = other.version;
}
public byte messageType() {
return TYPE;
}
public CONNECT decode(MQTTFrame frame) throws ProtocolException {
assert(frame.buffers.length == 1);
DataByteArrayInputStream is = new DataByteArrayInputStream(frame.buffers[0]);
UTF8Buffer protocolName = MessageSupport.readUTF(is);
if (V4_PROTOCOL_NAME.equals(protocolName)) {
version = is.readByte() & 0xFF;
if( version < 4 ) {
throw new ProtocolException("Invalid CONNECT frame: protocol name/version mismatch");
}
} else if( V3_PROTOCOL_NAME.equals(protocolName) ) {
version = is.readByte() & 0xFF;
if( version != 3 ) {
throw new ProtocolException("Invalid CONNECT frame: protocol name/version mismatch");
}
} else {
throw new ProtocolException("Invalid CONNECT frame");
}
byte flags = is.readByte();
boolean username_flag = (flags & 0x80) > 0;
boolean password_flag = (flags & 0x40) > 0;
willRetain = (flags & 0x20) > 0;
willQos = (byte) ((flags & 0x18) >>> 3);
boolean will_flag = (flags & 0x04) > 0;
cleanSession = (flags & 0x02) > 0;
keepAlive = is.readShort();
clientId = MessageSupport.readUTF(is);
if( clientId.length == 0 ) {
clientId = null;
}
if(will_flag) {
willTopic = MessageSupport.readUTF(is);
willMessage = MessageSupport.readUTF(is);
}
if( username_flag ) {
userName = MessageSupport.readUTF(is);
}
if( password_flag ) {
password = MessageSupport.readUTF(is);
}
return this;
}
public MQTTFrame encode() {
try {
if( (clientId==null || clientId.length == 0) && !cleanSession ) {
throw new IllegalArgumentException("A clean session must be used when no clientId is specified");
}
DataByteArrayOutputStream os = new DataByteArrayOutputStream(500);
if(version==3) {
MessageSupport.writeUTF(os, V3_PROTOCOL_NAME);
os.writeByte(version);
} else if(version >= 4) {
MessageSupport.writeUTF(os, V4_PROTOCOL_NAME);
os.writeByte(version);
} else {
throw new IllegalArgumentException("Invalid version: "+version);
}
int flags = 0;
if(userName!=null) {
flags |= 0x80;
}
if(password!=null) {
flags |= 0x40;
}
if(willTopic!=null && willMessage!=null) {
flags |= 0x04;
if(willRetain) {
flags |= 0x20;
}
flags |= (willQos << 3) & 0x18;
}
if(cleanSession) {
flags |= 0x02;
}
os.writeByte(flags);
os.writeShort(keepAlive);
MessageSupport.writeUTF(os, clientId);
if(willTopic!=null && willMessage!=null) {
MessageSupport.writeUTF(os, willTopic);
MessageSupport.writeUTF(os, willMessage);
}
if(userName!=null) {
MessageSupport.writeUTF(os, userName);
}
if(password!=null) {
MessageSupport.writeUTF(os, password);
}
MQTTFrame frame = new MQTTFrame();
frame.commandType(TYPE);
return frame.buffer(os.toBuffer());
} catch (IOException e) {
throw new RuntimeException("The impossible happened");
}
}
public boolean cleanSession() {
return cleanSession;
}
public CONNECT cleanSession(boolean cleanSession) {
this.cleanSession = cleanSession;
return this;
}
public UTF8Buffer clientId() {
return clientId;
}
public CONNECT clientId(UTF8Buffer clientId) {
this.clientId = clientId;
return this;
}
public short keepAlive() {
return keepAlive;
}
public CONNECT keepAlive(short keepAlive) {
this.keepAlive = keepAlive;
return this;
}
public UTF8Buffer password() {
return password;
}
public CONNECT password(UTF8Buffer password) {
this.password = password;
return this;
}
public UTF8Buffer userName() {
return userName;
}
public CONNECT userName(UTF8Buffer userName) {
this.userName = userName;
return this;
}
public UTF8Buffer willMessage() {
return willMessage;
}
public CONNECT willMessage(UTF8Buffer willMessage) {
this.willMessage = willMessage;
return this;
}
public QoS willQos() {
return QoS.values()[willQos];
}
public CONNECT willQos(QoS willQos) {
this.willQos = (byte) willQos.ordinal();
return this;
}
public boolean willRetain() {
return willRetain;
}
public CONNECT willRetain(boolean willRetain) {
this.willRetain = willRetain;
return this;
}
public UTF8Buffer willTopic() {
return willTopic;
}
public CONNECT willTopic(UTF8Buffer willTopic) {
this.willTopic = willTopic;
return this;
}
public int version() {
return version;
}
public CONNECT version(int version) {
if(version==3) {
this.version = version;
} else if(version >= 4) {
this.version = version;
} else {
throw new IllegalArgumentException("Invalid version: "+version);
}
return this;
}
@Override
public String toString() {
return "CONNECT{" +
"cleanSession=" + cleanSession +
", keepAlive=" + keepAlive +
", clientId=" + clientId +
", willTopic=" + willTopic +
", willMessage=" + willMessage +
", willRetain=" + willRetain +
", willQos=" + willQos +
", userName=" + userName +
", password=" + password +
'}';
}
}