/** * 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 + '}'; } }