Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bcf969e56f | ||
|
|
b3a95c0310 | ||
|
|
ae863ab6e4 | ||
|
|
688ca4e31d | ||
|
|
724b6dddd6 | ||
|
|
bf3b30eb2c | ||
|
|
4f0545dfc1 | ||
|
|
c1b9a81e12 | ||
|
|
029b6ed691 | ||
|
|
4fdefca33d | ||
|
|
e90ca145be | ||
|
|
bf3d3c308d | ||
|
|
bee2f3c6ef | ||
|
|
d4e13e4578 | ||
|
|
66486b019f | ||
|
|
6510204fe6 | ||
|
|
b4b736c0c4 | ||
|
|
c4e8c5811c | ||
|
|
804b2fc610 | ||
|
|
0a7487a144 | ||
|
|
4ae9de0628 | ||
|
|
04d0f717d8 | ||
|
|
b3ddda78a1 | ||
|
|
066e74be5f | ||
|
|
f9c2cf8e31 | ||
|
|
d3fc625299 | ||
|
|
905763b6e3 | ||
|
|
877b7bc217 | ||
|
|
d8b7f6a8de | ||
|
|
579d3b529f | ||
|
|
065a79adc3 | ||
|
|
5ed45ef414 | ||
|
|
ba671b3370 | ||
|
|
e975f20a6b | ||
|
|
70bdebd4a0 | ||
|
|
409078801e |
@@ -7,7 +7,6 @@
|
||||
| [android-chat](https://github.com/wildfirechat/android-chat) | 野火IM Android SDK源码和App源码 | 可以很方便地进行二次开发,或集成到现有应用当中 |
|
||||
| [ios-chat](https://github.com/wildfirechat/ios-chat) | 野火IM iOS SDK源码和App源码 | 可以很方便地进行二次开发,或集成到现有应用当中 |
|
||||
| [pc-chat](https://github.com/wildfirechat/pc-chat) | 基于[Electron](https://electronjs.org/)开发的PC平台应用 | |
|
||||
| [proto](https://github.com/wildfirechat/proto) | 野火IM的协议栈实现 | |
|
||||
| [server](https://github.com/wildfirechat/server) | IM server | |
|
||||
| [app server](https://github.com/wildfirechat/app_server) | 应用服务端 | |
|
||||
| [robot_server](https://github.com/wildfirechat/robot_server) | 机器人服务端 | |
|
||||
@@ -28,13 +27,10 @@
|
||||
2. imndx 邮箱: imndx@wildfirechat.cn 微信:wfchat
|
||||
|
||||
### 问题交流
|
||||
1. 微信交流群(请先加我们好友,我们再邀请您进群)
|
||||
|
||||
 
|
||||
|
||||
2. 如果大家发现bug,请在GitHub提issue
|
||||
3. 其他问题,请到[野火IM论坛](http://bbs.wildfirechat.cn/)进行交流学习
|
||||
4. 微信公众号
|
||||
1. 如果大家发现bug,请在GitHub提issue
|
||||
2. 其他问题,请到[野火IM论坛](http://bbs.wildfirechat.cn/)进行交流学习
|
||||
3. 微信公众号
|
||||
|
||||
<img src="http://static.wildfirechat.cn/wx_wfc_qrcode.jpg" width = 50% height = 50% />
|
||||
|
||||
@@ -45,6 +41,11 @@
|
||||
|
||||

|
||||
|
||||
#### 加入野火官方体验交流群
|
||||
使用野火IM移动客户端扫码下面二维码(不是使用微信扫码),和野火IM的爱好者一起畅谈野火IM的问题和经验:
|
||||
|
||||

|
||||
|
||||
#### 应用截图
|
||||

|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
|
||||
<!-- # 本配置文件为debug使用,修改这里不会在Release包中生效。Release中包含的配置文件在 ${Porject_Path}/distribution/src/main/resources目录下-->
|
||||
|
||||
|
||||
<hazelcast
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.hazelcast.com/schema/config
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
#
|
||||
#
|
||||
# 本配置文件为debug使用,修改这里不会在Release包中生效。Release中包含的配置文件在 ${Porject_Path}/distribution/src/main/resources目录下
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
|
||||
#*********************************************************************
|
||||
# Server configuration
|
||||
#*********************************************************************
|
||||
#服务器的接入IP。给客户端提供是${server.ip}和${http_port}。
|
||||
#客户端会从${http_port}端口获取到长链接端口。
|
||||
#客户端会从${http_port}端口获取到长链接端口。这个地址一定要改成客户端可以访问到的IP地址
|
||||
# (如果您部署云服务器上或者具有独立公网出口的服务器上,请改为对应的公网IP;如果您部署在在内网环境下,在内网使用,这个地方改成内网地址)
|
||||
server.ip 0.0.0.0
|
||||
|
||||
##原生客户端长链接端口
|
||||
@@ -34,6 +43,7 @@ http.admin.secret_key 123456
|
||||
|
||||
##服务器API接口参数是否检查时间。当设置为false时,所有的请求会检查时间的有效性;当设置为true时,可以在http.admin.secret_key保持不变的情况下,使用固定的服务API签名
|
||||
##nonce = "76616", timestamp = "1558350862502", sign = "b98f9b0717f59febccf1440067a7f50d9b31bdde"
|
||||
##这个地方上线前要改为false
|
||||
http.admin.no_check_time true
|
||||
|
||||
##客户端协议栈密钥,需要与客户端协议栈文件libemqq.cc文件中的aes_key值保持一致,16进制byte用逗号分割。这个密钥对安全性帮助不大,客户不要修改
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
alter table `t_user_session` modify column `_voip_token` varchar(240) DEFAULT '';
|
||||
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
alter table t_friend add column `_blacked` tinyint DEFAULT 0;
|
||||
update t_friend set `_blacked` = 1 where `_state` = 2;
|
||||
@@ -0,0 +1,3 @@
|
||||
alter table `t_user_session` modify column `_voip_token` varchar(240) DEFAULT '';
|
||||
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
alter table t_friend add column `_blacked` tinyint DEFAULT 0 COMMENT "0, normal; 1, blacked";
|
||||
update t_friend set `_blacked` = 1 where `_state` = 2;
|
||||
Executable → Regular
+2
-8
@@ -5,7 +5,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<version>0.26</version>
|
||||
<version>0.31</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>moquette-broker</artifactId>
|
||||
@@ -31,12 +31,6 @@
|
||||
<version>1.7.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<artifactId>client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
@@ -167,7 +161,7 @@
|
||||
<dependency>
|
||||
<groupId>org.eclipse.paho</groupId>
|
||||
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
|
||||
<version>1.0.2</version>
|
||||
<version>1.2.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ public class FriendRelationGetAction extends AdminAction {
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
InputGetFriendList inputGetFriendList = getRequestBody(request.getNettyRequest(), InputGetFriendList.class);
|
||||
List<FriendData> dataList = messagesStore.getFriendList(inputGetFriendList.getUserId(), 0);
|
||||
List<FriendData> dataList = messagesStore.getFriendList(inputGetFriendList.getUserId(), null, 0);
|
||||
List<String> list = new ArrayList<>();
|
||||
for (FriendData data : dataList) {
|
||||
if (data.getState() == inputGetFriendList.getStatus()) {
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
|
||||
import cn.wildfirechat.common.APIPath;
|
||||
import cn.wildfirechat.common.ErrorCode;
|
||||
import cn.wildfirechat.pojos.InputAddGroupMember;
|
||||
import cn.wildfirechat.pojos.InputSetGroupManager;
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import io.moquette.persistence.RPCCenter;
|
||||
import io.moquette.persistence.TargetEntry;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import win.liyufan.im.IMTopic;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@Route(APIPath.Group_Set_Manager)
|
||||
@HttpMethod("POST")
|
||||
public class SetGroupManagerAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
InputSetGroupManager inputAddGroupMember = getRequestBody(request.getNettyRequest(), InputSetGroupManager.class);
|
||||
if (inputAddGroupMember.isValide()) {
|
||||
RPCCenter.getInstance().sendRequest(inputAddGroupMember.getOperator(), null, IMTopic.SetGroupManagerTopic, inputAddGroupMember.toProtoGroupRequest().toByteArray(), inputAddGroupMember.getOperator(), TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
|
||||
@Override
|
||||
public void onSuccess(byte[] result) {
|
||||
ByteBuf byteBuf = Unpooled.buffer();
|
||||
byteBuf.writeBytes(result);
|
||||
ErrorCode errorCode = ErrorCode.fromCode(byteBuf.readByte());
|
||||
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
sendResponse(response, null, null);
|
||||
} else {
|
||||
sendResponse(response, errorCode, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorCode errorCode) {
|
||||
sendResponse(response, errorCode, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout() {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor getResponseExecutor() {
|
||||
return command -> {
|
||||
ctx.executor().execute(command);
|
||||
};
|
||||
}
|
||||
}, true);
|
||||
return false;
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -15,14 +15,16 @@ public class FriendData implements Serializable {
|
||||
private String friendUid;
|
||||
private String alias;
|
||||
private int state;
|
||||
private int blacked;
|
||||
private long timestamp;
|
||||
|
||||
|
||||
public FriendData(String userId, String friendUid, String alias, int state, long timestamp) {
|
||||
public FriendData(String userId, String friendUid, String alias, int state, int blacked, long timestamp) {
|
||||
this.userId = userId;
|
||||
this.friendUid = friendUid;
|
||||
this.alias = alias;
|
||||
this.state = state;
|
||||
this.blacked = blacked;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
@@ -61,6 +63,14 @@ public class FriendData implements Serializable {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public int getBlacked() {
|
||||
return blacked;
|
||||
}
|
||||
|
||||
public void setBlacked(int blacked) {
|
||||
this.blacked = blacked;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ public final class BrokerConstants {
|
||||
public static final String MONGODB_Data_Expire_Days = "mongodb.data_expire_days";
|
||||
|
||||
public static final String MESSAGE_ROAMING = "message.roaming";
|
||||
public static final String MESSAGE_Remote_History_Message = "message.reomte_history_message";
|
||||
public static final String MESSAGE_Remote_History_Message = "message.remote_history_message";
|
||||
|
||||
|
||||
private BrokerConstants() {
|
||||
|
||||
@@ -22,11 +22,11 @@ import java.util.List;
|
||||
public class FriendPullHandler extends IMHandler<WFCMessage.Version> {
|
||||
@Override
|
||||
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, boolean isAdmin, WFCMessage.Version request, Qos1PublishHandler.IMCallback callback) {
|
||||
List<FriendData> friendDatas = m_messagesStore.getFriendList(fromUser, request.getVersion());
|
||||
List<FriendData> friendDatas = m_messagesStore.getFriendList(fromUser, clientID, request.getVersion());
|
||||
WFCMessage.GetFriendsResult.Builder builder = WFCMessage.GetFriendsResult.newBuilder();
|
||||
for (FriendData data : friendDatas
|
||||
) {
|
||||
WFCMessage.Friend.Builder builder1 = WFCMessage.Friend.newBuilder().setState(data.getState()).setUid(data.getFriendUid()).setUpdateDt(data.getTimestamp());
|
||||
WFCMessage.Friend.Builder builder1 = WFCMessage.Friend.newBuilder().setState(data.getState()).setBlacked(data.getBlacked()).setUid(data.getFriendUid()).setUpdateDt(data.getTimestamp());
|
||||
if (!StringUtil.isNullOrEmpty(data.getAlias())) {
|
||||
builder1.setAlias(data.getAlias());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package io.moquette.imhandler;
|
||||
|
||||
import cn.wildfirechat.common.ErrorCode;
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.qiniu.util.Auth;
|
||||
import com.xiaoleilu.loServer.action.UploadFileAction;
|
||||
import io.moquette.server.config.MediaServerConfig;
|
||||
import io.moquette.spi.impl.Qos1PublishHandler;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
@Handler("GMUT")
|
||||
public class GetMediaUploadTokenHandler extends IMHandler<WFCMessage.GetUploadTokenRequest> {
|
||||
@Override
|
||||
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, boolean isAdmin, WFCMessage.GetUploadTokenRequest request, Qos1PublishHandler.IMCallback callback) {
|
||||
int type = request.getMediaType();
|
||||
|
||||
String token;
|
||||
|
||||
WFCMessage.GetUploadTokenResult.Builder resultBuilder = WFCMessage.GetUploadTokenResult.newBuilder();
|
||||
if (MediaServerConfig.USER_QINIU) {
|
||||
Auth auth = Auth.create(MediaServerConfig.QINIU_ACCESS_KEY, MediaServerConfig.QINIU_SECRET_KEY);
|
||||
|
||||
|
||||
String bucketName;
|
||||
String bucketDomain;
|
||||
switch (type) {
|
||||
case 0:
|
||||
bucketName = MediaServerConfig.QINIU_BUCKET_GENERAL_NAME;
|
||||
bucketDomain = MediaServerConfig.QINIU_BUCKET_GENERAL_DOMAIN;
|
||||
break;
|
||||
case 1:
|
||||
bucketName = MediaServerConfig.QINIU_BUCKET_IMAGE_NAME;
|
||||
bucketDomain = MediaServerConfig.QINIU_BUCKET_IMAGE_DOMAIN;
|
||||
break;
|
||||
case 2:
|
||||
bucketName = MediaServerConfig.QINIU_BUCKET_VOICE_NAME;
|
||||
bucketDomain = MediaServerConfig.QINIU_BUCKET_VOICE_DOMAIN;
|
||||
break;
|
||||
case 3:
|
||||
bucketName = MediaServerConfig.QINIU_BUCKET_VIDEO_NAME;
|
||||
bucketDomain = MediaServerConfig.QINIU_BUCKET_VIDEO_DOMAIN;
|
||||
break;
|
||||
case 4:
|
||||
bucketName = MediaServerConfig.QINIU_BUCKET_FILE_NAME;
|
||||
bucketDomain = MediaServerConfig.QINIU_BUCKET_FILE_DOMAIN;
|
||||
break;
|
||||
case 5:
|
||||
bucketName = MediaServerConfig.QINIU_BUCKET_PORTRAIT_NAME;
|
||||
bucketDomain = MediaServerConfig.QINIU_BUCKET_PORTRAIT_DOMAIN;
|
||||
break;
|
||||
case 6:
|
||||
bucketName = MediaServerConfig.QINIU_BUCKET_FAVORITE_NAME;
|
||||
bucketDomain = MediaServerConfig.QINIU_BUCKET_FAVORITE_DOMAIN;
|
||||
break;
|
||||
default:
|
||||
bucketName = MediaServerConfig.QINIU_BUCKET_GENERAL_NAME;
|
||||
bucketDomain = MediaServerConfig.QINIU_BUCKET_GENERAL_DOMAIN;
|
||||
break;
|
||||
}
|
||||
|
||||
token = auth.uploadToken(bucketName);
|
||||
resultBuilder.setDomain(bucketDomain)
|
||||
.setServer(MediaServerConfig.QINIU_SERVER_URL);
|
||||
resultBuilder.setPort(80);
|
||||
} else {
|
||||
token = UploadFileAction.getToken(type);
|
||||
resultBuilder.setDomain("http://" + MediaServerConfig.SERVER_IP + ":" + MediaServerConfig.HTTP_SERVER_PORT)
|
||||
.setServer(MediaServerConfig.SERVER_IP);
|
||||
resultBuilder.setPort(MediaServerConfig.HTTP_SERVER_PORT);
|
||||
}
|
||||
|
||||
resultBuilder.setToken(token);
|
||||
|
||||
byte[] data = resultBuilder.buildPartial().toByteArray();
|
||||
ackPayload.ensureWritable(data.length).writeBytes(data);
|
||||
return ErrorCode.ERROR_CODE_SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import com.xiaoleilu.loServer.model.FriendData;
|
||||
import io.moquette.spi.ClientSession;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sun.misc.BASE64Encoder;
|
||||
import win.liyufan.im.DBUtil;
|
||||
import win.liyufan.im.MessageBundle;
|
||||
import win.liyufan.im.MessageShardingUtil;
|
||||
@@ -57,8 +56,6 @@ public class DatabaseStore {
|
||||
|
||||
statement.setString(1, userId);
|
||||
|
||||
statement.executeQuery();
|
||||
|
||||
rs = statement.executeQuery();
|
||||
while (rs.next()) {
|
||||
int index = 1;
|
||||
@@ -173,13 +170,17 @@ public class DatabaseStore {
|
||||
if (buzzy) {
|
||||
sql += " where (`_display_name` like ? or `_name` = ? or `_mobile` = ?) ";
|
||||
} else {
|
||||
sql += " where (`_display_name` = ? or `_name` = ? or `_mobile` = ?) ";
|
||||
sql += " where (`_name` = ? or `_mobile` = ?) ";
|
||||
}
|
||||
|
||||
sql += " and _type <> 2"; //can search normal user(0) and robot(1), can not search things
|
||||
|
||||
sql += " limit 20";
|
||||
|
||||
if (buzzy) {
|
||||
sql += " limit 20";
|
||||
} else {
|
||||
sql += " limit 1";
|
||||
}
|
||||
|
||||
if (page > 0) {
|
||||
sql += "offset = '" + page * 20 + "'";
|
||||
}
|
||||
@@ -189,9 +190,8 @@ public class DatabaseStore {
|
||||
int index = 1;
|
||||
if (buzzy) {
|
||||
statement.setString(index++, "%" + keyword + "%");
|
||||
} else {
|
||||
statement.setString(index++, keyword);
|
||||
}
|
||||
|
||||
statement.setString(index++, keyword);
|
||||
statement.setString(index++, keyword);
|
||||
|
||||
@@ -350,7 +350,7 @@ public class DatabaseStore {
|
||||
|
||||
try {
|
||||
connection = DBUtil.getConnection();
|
||||
String sql = "select `_uid`, `_friend_uid`, `_alias`, `_state`, `_dt` from t_friend";
|
||||
String sql = "select `_uid`, `_friend_uid`, `_alias`, `_state`, `_blacked`, `_dt` from t_friend";
|
||||
statement = connection.prepareStatement(sql);
|
||||
|
||||
int index;
|
||||
@@ -375,6 +375,9 @@ public class DatabaseStore {
|
||||
int intvalue = rs.getInt(index++);
|
||||
builder.setState(intvalue);
|
||||
|
||||
intvalue = rs.getInt(index++);
|
||||
builder.setBlacked(intvalue);
|
||||
|
||||
long longvalue = rs.getLong(index++);
|
||||
builder.setTimestamp(longvalue);
|
||||
|
||||
@@ -1628,8 +1631,7 @@ public class DatabaseStore {
|
||||
|
||||
try {
|
||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||||
BASE64Encoder base64en = new BASE64Encoder();
|
||||
String passwdMd5 = base64en.encode(md5.digest(password.getBytes("utf-8")));
|
||||
String passwdMd5 = Base64.getEncoder().encodeToString(md5.digest(password.getBytes("utf-8")));
|
||||
statement.setString(index, passwdMd5);
|
||||
} catch (Exception e) {
|
||||
statement.setString(index, "");
|
||||
@@ -2140,7 +2142,7 @@ public class DatabaseStore {
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
connection = DBUtil.getConnection();
|
||||
String sql = "select `_friend_uid`, `_alias`, `_state`, `_dt` from t_friend where `_uid` = ?";
|
||||
String sql = "select `_friend_uid`, `_alias`, `_state`, `_blacked`, `_dt` from t_friend where `_uid` = ?";
|
||||
statement = connection.prepareStatement(sql);
|
||||
|
||||
|
||||
@@ -2154,9 +2156,10 @@ public class DatabaseStore {
|
||||
String uid = rs.getString(1);
|
||||
String alias = rs.getString(2);
|
||||
int state = rs.getInt(3);
|
||||
long timestamp = rs.getLong(4);
|
||||
int blacked = rs.getInt(4);
|
||||
long timestamp = rs.getLong(5);
|
||||
|
||||
FriendData data = new FriendData(userId, uid, alias, state, timestamp);
|
||||
FriendData data = new FriendData(userId, uid, alias, state, blacked, timestamp);
|
||||
out.add(data);
|
||||
}
|
||||
return out;
|
||||
@@ -2306,10 +2309,11 @@ public class DatabaseStore {
|
||||
PreparedStatement statement = null;
|
||||
try {
|
||||
connection = DBUtil.getConnection();
|
||||
String sql = "insert into t_friend (`_uid`, `_friend_uid`, `_alias`, `_state`, `_dt`) values(?, ?, ?, ?, ?)" +
|
||||
String sql = "insert into t_friend (`_uid`, `_friend_uid`, `_alias`, `_state`, `_blacked`, `_dt`) values(?, ?, ?, ?, ?, ?)" +
|
||||
" ON DUPLICATE KEY UPDATE " +
|
||||
"`_alias` = ?," +
|
||||
"`_state` = ?," +
|
||||
"`_blacked` = ?," +
|
||||
"`_dt` = ?";
|
||||
|
||||
|
||||
@@ -2319,9 +2323,11 @@ public class DatabaseStore {
|
||||
statement.setString(index++, request.getFriendUid());
|
||||
statement.setString(index++, request.getAlias());
|
||||
statement.setInt(index++, request.getState());
|
||||
statement.setInt(index++, request.getBlacked());
|
||||
statement.setLong(index++, request.getTimestamp());
|
||||
statement.setString(index++, request.getAlias());
|
||||
statement.setInt(index++, request.getState());
|
||||
statement.setInt(index++, request.getBlacked());
|
||||
statement.setLong(index++, request.getTimestamp());
|
||||
int count = statement.executeUpdate();
|
||||
LOG.info("Update rows {}", count);
|
||||
|
||||
@@ -30,9 +30,9 @@ import io.moquette.spi.IMatchingCondition;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.moquette.spi.security.Tokenor;
|
||||
import io.moquette.spi.impl.subscriptions.Topic;
|
||||
import io.netty.handler.codec.mqtt.MqttVersion;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sun.misc.BASE64Encoder;
|
||||
import win.liyufan.im.*;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
@@ -349,7 +349,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
}
|
||||
|
||||
size += bundle.getMessage().getSerializedSize();
|
||||
if (size >= 1 * 1024 * 1024) { //3M
|
||||
if (size >= 512 * 1024) { //3M
|
||||
break;
|
||||
}
|
||||
builder.addMessage(bundle.getMessage());
|
||||
@@ -1039,25 +1039,31 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
|
||||
//check the new owner is in member list? is that necessary?
|
||||
long updateDt = System.currentTimeMillis();
|
||||
groupInfo = groupInfo.toBuilder().setOwner(newOwner).setUpdateDt(updateDt).build();
|
||||
|
||||
groupInfo = groupInfo.toBuilder().setOwner(newOwner).setUpdateDt(updateDt).setMemberUpdateDt(updateDt).build();
|
||||
mIMap.set(groupId, groupInfo);
|
||||
|
||||
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
|
||||
Collection<WFCMessage.GroupMember> members = groupMembers.get(groupId);
|
||||
if (members == null || members.size() == 0) {
|
||||
members = loadGroupMemberFromDB(hzInstance, groupId);
|
||||
}
|
||||
int modifyMemeberCount = 0;
|
||||
for (WFCMessage.GroupMember member : members) {
|
||||
if (modifyMemeberCount == 2) {
|
||||
break;
|
||||
}
|
||||
if (newOwner.equals(member.getMemberId())) {
|
||||
groupMembers.remove(groupId, member);
|
||||
member = member.toBuilder().setType(GroupMemberType_Owner).setUpdateDt(updateDt).build();
|
||||
databaseStore.persistGroupMember(groupId, Arrays.asList(member));
|
||||
groupMembers.put(groupId, member);
|
||||
modifyMemeberCount++;
|
||||
} else if(member.getType() == GroupMemberType_Owner) {
|
||||
groupMembers.remove(groupId, member);
|
||||
member = member.toBuilder().setType(GroupMemberType_Normal).setUpdateDt(updateDt).build();
|
||||
databaseStore.persistGroupMember(groupId, Arrays.asList(member));
|
||||
groupMembers.put(groupId, member);
|
||||
modifyMemeberCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1556,7 +1562,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
|
||||
for (FriendData friendData : friendDatas) {
|
||||
if (friendData.getFriendUid().equals(userId)) {
|
||||
if (friendData.getState() == 2) {
|
||||
if (friendData.getBlacked() == 1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -1592,8 +1598,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
String pwd_md5 = rs.getString(2);
|
||||
try {
|
||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||||
BASE64Encoder base64en = new BASE64Encoder();
|
||||
String passwdMd5 = base64en.encode(md5.digest(password.getBytes("utf-8")));
|
||||
String passwdMd5 = Base64.getEncoder().encodeToString(md5.digest(password.getBytes("utf-8")));
|
||||
if (passwdMd5.equals(pwd_md5)) {
|
||||
LOG.info("login success userName={}, userId={}", name, uid);
|
||||
userIdRet.add(uid);
|
||||
@@ -1648,7 +1653,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FriendData> getFriendList(String userId, long version) {
|
||||
public List<FriendData> getFriendList(String userId, String clientId, long version) {
|
||||
List<FriendData> out = new ArrayList<FriendData>();
|
||||
|
||||
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
|
||||
@@ -1658,9 +1663,26 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
friends = loadFriend(friendsMap, userId);
|
||||
}
|
||||
|
||||
boolean needFriendMigrate = false;
|
||||
|
||||
if (!StringUtil.isNullOrEmpty(clientId)) {
|
||||
MemorySessionStore.Session session = m_Server.getStore().sessionsStore().getSession(clientId);
|
||||
if(session != null && session.getMqttVersion() != null && session.getMqttVersion().protocolLevel() < MqttVersion.Wildfire_2.protocolLevel()) {
|
||||
needFriendMigrate = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (FriendData friend : friends) {
|
||||
if (friend.getTimestamp() > version) {
|
||||
out.add(friend);
|
||||
if (needFriendMigrate) {
|
||||
if (friend.getBlacked() > 0) {
|
||||
friend.setState(2);
|
||||
}
|
||||
out.add(friend);
|
||||
} else {
|
||||
out.add(friend);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1715,6 +1737,24 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
return ErrorCode.ERROR_CODE_FRIEND_ALREADY_REQUEST;
|
||||
}
|
||||
}
|
||||
|
||||
MultiMap<String, FriendData> friendsMap = hzInstance.getMultiMap(USER_FRIENDS);
|
||||
|
||||
FriendData friendData1 = null;
|
||||
Collection<FriendData> friendDatas = friendsMap.get(userId);
|
||||
if (friendDatas == null || friendDatas.size() == 0) {
|
||||
friendDatas = loadFriend(friendsMap, userId);
|
||||
}
|
||||
for (FriendData fd : friendDatas) {
|
||||
if (fd.getFriendUid().equals(request.getTargetUid())) {
|
||||
friendData1 = fd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (friendData1 != null && friendData1.getBlacked() > 0) {
|
||||
return ErrorCode.ERROR_CODE_IN_BLACK_LIST;
|
||||
}
|
||||
|
||||
WFCMessage.FriendRequest newRequest = WFCMessage.FriendRequest
|
||||
.newBuilder()
|
||||
.setFromUid(userId)
|
||||
@@ -1766,7 +1806,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
}
|
||||
}
|
||||
if (friendData1 == null) {
|
||||
friendData1 = new FriendData(userId, request.getTargetUid(), "", request.getStatus(), System.currentTimeMillis());
|
||||
friendData1 = new FriendData(userId, request.getTargetUid(), "", request.getStatus(), 0, System.currentTimeMillis());
|
||||
} else {
|
||||
friendData1.setState(request.getStatus());
|
||||
friendData1.setTimestamp(System.currentTimeMillis());
|
||||
@@ -1788,7 +1828,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
}
|
||||
}
|
||||
if (friendData2 == null) {
|
||||
friendData2 = new FriendData(request.getTargetUid(), userId, "", request.getStatus(), friendData1.getTimestamp());
|
||||
friendData2 = new FriendData(request.getTargetUid(), userId, "", request.getStatus(), 0, friendData1.getTimestamp());
|
||||
} else {
|
||||
friendsMap.remove(request.getTargetUid(), friendData2);
|
||||
friendData2.setState(request.getStatus());
|
||||
@@ -1829,10 +1869,10 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
databaseStore.persistOrUpdateFriendRequest(existRequest);
|
||||
MultiMap<String, FriendData> friendsMap = hzInstance.getMultiMap(USER_FRIENDS);
|
||||
|
||||
FriendData friendData1 = new FriendData(userId, request.getTargetUid(), "", 0, System.currentTimeMillis());
|
||||
FriendData friendData1 = new FriendData(userId, request.getTargetUid(), "", 0, 0, System.currentTimeMillis());
|
||||
databaseStore.persistOrUpdateFriendData(friendData1);
|
||||
|
||||
FriendData friendData2 = new FriendData(request.getTargetUid(), userId, "", 0, friendData1.getTimestamp());
|
||||
FriendData friendData2 = new FriendData(request.getTargetUid(), userId, "", 0, 0, friendData1.getTimestamp());
|
||||
databaseStore.persistOrUpdateFriendData(friendData2);
|
||||
|
||||
requestMap.remove(userId);
|
||||
@@ -1854,6 +1894,12 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
|
||||
@Override
|
||||
public ErrorCode blackUserRequest(String fromUser, String targetUserId, int state, long[] heads) {
|
||||
if (state == 2) {
|
||||
state = 1;
|
||||
} else {
|
||||
state = 0;
|
||||
}
|
||||
|
||||
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
|
||||
MultiMap<String, FriendData> friendsMap = hzInstance.getMultiMap(USER_FRIENDS);
|
||||
|
||||
@@ -1871,9 +1917,9 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
}
|
||||
|
||||
if (friendData == null) {
|
||||
friendData = new FriendData(fromUser, targetUserId, "", state, System.currentTimeMillis());
|
||||
friendData = new FriendData(fromUser, targetUserId, "", 1, state, System.currentTimeMillis());
|
||||
}
|
||||
friendData.setState(state);
|
||||
friendData.setBlacked(state);
|
||||
friendData.setTimestamp(System.currentTimeMillis());
|
||||
|
||||
|
||||
@@ -2121,7 +2167,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
public boolean getUserPushHiddenDetail(String userId) {
|
||||
Boolean hidden = userPushHiddenDetail.get(userId);
|
||||
if (hidden == null) {
|
||||
WFCMessage.UserSettingEntry entry = getUserSetting(userId, UserSettingScope.kUserSettingGlobalSilent, null);
|
||||
WFCMessage.UserSettingEntry entry = getUserSetting(userId, UserSettingScope.kUserSettingHiddenNotificationDetail, null);
|
||||
if (entry == null || !entry.getValue().equals("1")) {
|
||||
hidden = false;
|
||||
} else {
|
||||
@@ -2320,6 +2366,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
words) {
|
||||
databaseStore.deleteSensitiveWord(word);
|
||||
}
|
||||
lastUpdateSensitiveTime = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -406,6 +406,7 @@ public class Server {
|
||||
}
|
||||
|
||||
public void stopServer() {
|
||||
System.out.println("Server will flush data to db before shutting down, please wait 5 seconds!");
|
||||
LOG.info("Unbinding server from the configured ports");
|
||||
m_acceptor.close();
|
||||
LOG.trace("Stopping MQTT protocol processor");
|
||||
|
||||
@@ -153,7 +153,7 @@ public interface IMessagesStore {
|
||||
ErrorCode verifyToken(String userId, String token, List<String> serverIPs, List<Integer> ports);
|
||||
ErrorCode login(String name, String password, List<String> userIdRet);
|
||||
|
||||
List<FriendData> getFriendList(String userId, long version);
|
||||
List<FriendData> getFriendList(String userId, String clientId, long version);
|
||||
List<WFCMessage.FriendRequest> getFriendRequestList(String userId, long version);
|
||||
|
||||
ErrorCode saveAddFriendRequest(String userId, WFCMessage.AddFriendRequest request, long[] head);
|
||||
|
||||
@@ -142,9 +142,8 @@ public class ProtocolProcessor {
|
||||
String clientId = payload.clientIdentifier();
|
||||
LOG.info("Processing CONNECT message. CId={}, username={}", clientId, payload.userName());
|
||||
|
||||
if (msg.variableHeader().version() != MqttVersion.MQTT_3_1.protocolLevel()
|
||||
&& msg.variableHeader().version() != MqttVersion.MQTT_3_1_1.protocolLevel()
|
||||
&& msg.variableHeader().version() != MqttVersion.Wildfire_1.protocolLevel()) {
|
||||
if (msg.variableHeader().version() < MqttVersion.MQTT_3_1_1.protocolLevel() ||
|
||||
msg.variableHeader().version() >= MqttVersion.Wildfire_Max.protocolLevel()) {
|
||||
MqttConnAckMessage badProto = connAck(CONNECTION_REFUSED_UNACCEPTABLE_PROTOCOL_VERSION);
|
||||
|
||||
LOG.error("MQTT protocol version is not valid. CId={}", clientId);
|
||||
|
||||
@@ -178,7 +178,7 @@ public class Qos1PublishHandler extends QosPublishHandler {
|
||||
} else {
|
||||
MemorySessionStore.Session session = m_sessionStore.getSession(clientID);
|
||||
if (session != null && session.getUsername().equals(fromUser)) {
|
||||
if (data.length > 7*1024 && session.getMqttVersion() == MqttVersion.Wildfire_1) {
|
||||
if (data.length > 7*1024 && session.getMqttVersion().protocolLevel() >= MqttVersion.Wildfire_1.protocolLevel()) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
GZIPOutputStream gzip;
|
||||
try {
|
||||
|
||||
@@ -43,7 +43,7 @@ final class MqttCodecUtil {
|
||||
return clientId != null && clientId.length() >= MIN_CLIENT_ID_LENGTH &&
|
||||
clientId.length() <= MAX_CLIENT_ID_LENGTH;
|
||||
}
|
||||
if (mqttVersion == MqttVersion.MQTT_3_1_1 || mqttVersion == MqttVersion.Wildfire_1) {
|
||||
if (mqttVersion.protocolLevel() >= MqttVersion.MQTT_3_1_1.protocolLevel()) {
|
||||
// In 3.1.3.1 Client Identifier of MQTT 3.1.1 specification, The Server MAY allow ClientId’s
|
||||
// that contain more than 23 encoded bytes. And, The Server MAY allow zero-length ClientId.
|
||||
return clientId != null;
|
||||
|
||||
@@ -223,7 +223,7 @@ public final class MqttDecoder extends ReplayingDecoder<DecoderState> {
|
||||
final int willQos = (b1 & 0x18) >> 3;
|
||||
final boolean willFlag = (b1 & 0x04) == 0x04;
|
||||
final boolean cleanSession = (b1 & 0x02) == 0x02;
|
||||
if (mqttVersion == MqttVersion.MQTT_3_1_1 || mqttVersion == MqttVersion.Wildfire_1) {
|
||||
if (mqttVersion.protocolLevel() >= MqttVersion.MQTT_3_1_1.protocolLevel()) {
|
||||
final boolean zeroReservedFlag = (b1 & 0x01) == 0x0;
|
||||
if (!zeroReservedFlag) {
|
||||
// MQTT v3.1.1: The Server MUST validate that the reserved flag in the CONNECT Control Packet is
|
||||
|
||||
@@ -25,7 +25,9 @@ import io.netty.util.internal.ObjectUtil;
|
||||
public enum MqttVersion {
|
||||
MQTT_3_1("MQIsdp", (byte) 3),
|
||||
MQTT_3_1_1("MQTT", (byte) 4),
|
||||
Wildfire_1("MQTT", (byte) 5);
|
||||
Wildfire_1("MQTT", (byte) 5),
|
||||
Wildfire_2("MQTT", (byte) 6),
|
||||
Wildfire_Max("MQTT", (byte) 7);
|
||||
|
||||
private final String name;
|
||||
private final byte level;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<?xml version ="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- # 本配置文件为debug使用,修改这里不会在Release包中生效。Release中包含的配置文件在 ${Porject_Path}/distribution/src/main/resources目录下-->
|
||||
|
||||
<c3p0-config>
|
||||
<!-- This is default config! -->
|
||||
<default-config>
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<relativePath>../</relativePath>
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<version>0.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>client</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.7.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.14</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.fusesource.hawtbuf/hawtbuf -->
|
||||
<dependency>
|
||||
<groupId>org.fusesource.hawtbuf</groupId>
|
||||
<artifactId>hawtbuf</artifactId>
|
||||
<version>1.11</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.fusesource.hawtdispatch/hawtdispatch-transport -->
|
||||
<dependency>
|
||||
<groupId>org.fusesource.hawtdispatch</groupId>
|
||||
<artifactId>hawtdispatch-transport</artifactId>
|
||||
<version>1.22</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>2.5.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.6</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpcore</artifactId>
|
||||
<version>4.4.10</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,424 +0,0 @@
|
||||
package cn.wildfirechat.client;
|
||||
|
||||
import cn.wildfirechat.proto.ProtoConstants;
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.xiaoleilu.loServer.pojos.InputRoute;
|
||||
import io.moquette.spi.impl.security.AES;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.fusesource.hawtbuf.Buffer;
|
||||
import org.fusesource.hawtbuf.UTF8Buffer;
|
||||
import org.fusesource.mqtt.client.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.wildfirechat.client.IMClient.ConnectionStatus.ConnectionStatus_Connected;
|
||||
import static cn.wildfirechat.client.IMClient.ConnectionStatus.ConnectionStatus_Connecting;
|
||||
import static cn.wildfirechat.client.IMClient.ConnectionStatus.ConnectionStatus_Unconnected;
|
||||
|
||||
|
||||
public class IMClient implements Listener {
|
||||
private final String userId;
|
||||
private final String token;
|
||||
private final String clientId;
|
||||
private final String host;
|
||||
private final int port;
|
||||
private ConnectionStatusCallback connectionStatusCallback;
|
||||
private ReceiveMessageCallback receiveMessageCallback;
|
||||
private ConnectionStatus connectionStatus;
|
||||
|
||||
protected String mqttServerIp;
|
||||
protected long mqttServerPort;
|
||||
private static byte[] commonSecret= {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F};
|
||||
protected String privateSecret;
|
||||
|
||||
private long messageHead;
|
||||
|
||||
private transient MQTT mqtt = new MQTT();
|
||||
private transient CallbackConnection connection = null;
|
||||
|
||||
public interface ReceiveMessageCallback {
|
||||
void onReceiveMessages(List<WFCMessage.Message> messageList, boolean hasMore);
|
||||
void onRecallMessage(long messageUid);
|
||||
}
|
||||
|
||||
public interface ConnectionStatusCallback {
|
||||
void onConnectionStatusChanged(ConnectionStatus newStatus);
|
||||
}
|
||||
|
||||
public interface SendMessageCallback {
|
||||
void onSuccess(long messageUid, long timestamp);
|
||||
void onFailure(int errorCode);
|
||||
}
|
||||
|
||||
public enum ConnectionStatus {
|
||||
ConnectionStatus_Unconnected,
|
||||
ConnectionStatus_Connecting,
|
||||
ConnectionStatus_Connected,
|
||||
}
|
||||
|
||||
public IMClient(String userId, String token, String clientId, String host, int port) {
|
||||
this.userId = userId;
|
||||
|
||||
byte[] data = Base64.getDecoder().decode(token);
|
||||
data = AES.AESDecrypt(data, commonSecret, false);
|
||||
String s = new String(data);
|
||||
String[] ss = s.split("\\|");
|
||||
|
||||
this.token = ss[0];
|
||||
this.privateSecret = ss[1];
|
||||
this.clientId = clientId;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
AES.init(commonSecret);
|
||||
}
|
||||
|
||||
|
||||
public void connect() {
|
||||
if(route(userId, token)) {
|
||||
try {
|
||||
mqtt.setHost("tcp://" + mqttServerIp + ":" + mqttServerPort);
|
||||
mqtt.setVersion("3.1.1");
|
||||
mqtt.setKeepAlive((short)180);
|
||||
|
||||
mqtt.setClientId(clientId);
|
||||
mqtt.setConnectAttemptsMax(100);
|
||||
mqtt.setReconnectAttemptsMax(100);
|
||||
|
||||
mqtt.setUserName(userId);
|
||||
byte[] password = AES.AESEncrypt(token, privateSecret);
|
||||
mqtt.setPassword(new UTF8Buffer(password));
|
||||
|
||||
|
||||
connection = mqtt.callbackConnection();
|
||||
connection.listener(this);
|
||||
|
||||
//connecting
|
||||
connectionStatus = ConnectionStatus_Connecting;
|
||||
if(connectionStatusCallback != null) {
|
||||
connectionStatusCallback.onConnectionStatusChanged(connectionStatus);
|
||||
}
|
||||
|
||||
connection.connect(new Callback<byte[]>() {
|
||||
@Override
|
||||
public void onSuccess(byte[] value) {
|
||||
if (value != null) {
|
||||
try {
|
||||
WFCMessage.ConnectAckPayload ackPayload = WFCMessage.ConnectAckPayload.parseFrom(value);
|
||||
System.out.println(ackPayload.getMsgHead());
|
||||
messageHead = ackPayload.getMsgHead();
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
System.out.println("on connect success");
|
||||
//connected
|
||||
connectionStatus = ConnectionStatus_Connected;
|
||||
if(connectionStatusCallback != null) {
|
||||
connectionStatusCallback.onConnectionStatusChanged(connectionStatus);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable value) {
|
||||
System.out.println("on connect failure");
|
||||
connectionStatus = ConnectionStatus_Unconnected;
|
||||
if(connectionStatusCallback != null) {
|
||||
connectionStatusCallback.onConnectionStatusChanged(connectionStatus);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void disconnect(boolean clearSession, final Callback<Void> onComplete) {
|
||||
this.connection.disconnect(clearSession, onComplete);
|
||||
}
|
||||
public void sendMessage(WFCMessage.Conversation conversation, WFCMessage.MessageContent messageContent, final SendMessageCallback callback) {
|
||||
WFCMessage.Message message = WFCMessage.Message.newBuilder().setConversation(conversation).setContent(messageContent).setFromUser(userId).build();
|
||||
byte[] data = message.toByteArray();
|
||||
data = AES.AESEncrypt(data, privateSecret);
|
||||
connection.publish("MS", data, QoS.AT_LEAST_ONCE, false, new Callback<byte[]>() {
|
||||
@Override
|
||||
public void onSuccess(byte[] value) {
|
||||
if (value[0] == 0) {
|
||||
byte[] data = new byte[value.length-1];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = value[i+1];
|
||||
}
|
||||
|
||||
data = AES.AESDecrypt(data, privateSecret, true);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data, 0,16);
|
||||
|
||||
long messageUid = buffer.getLong();
|
||||
long timestamp = buffer.getLong();
|
||||
callback.onSuccess(messageUid, timestamp);
|
||||
} else {
|
||||
callback.onFailure(value[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable value) {
|
||||
callback.onFailure(-1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected boolean route(String userId, String token) {
|
||||
HttpPost httpPost;
|
||||
try{
|
||||
HttpClient httpClient = HttpClientBuilder.create().build();
|
||||
httpPost = new HttpPost("http://" + host + ":" + port + "/route");
|
||||
InputRoute inputRoute = new InputRoute();
|
||||
inputRoute.setUserId(userId);
|
||||
inputRoute.setClientId(clientId);
|
||||
inputRoute.setToken(token);
|
||||
|
||||
WFCMessage.RouteRequest routeRequest = WFCMessage.RouteRequest.newBuilder().setPlatform(0).build();
|
||||
|
||||
WFCMessage.IMHttpWrapper request = WFCMessage.IMHttpWrapper.newBuilder().setClientId(clientId).setToken(token).setRequest("ROUTE").setData(ByteString.copyFrom(routeRequest.toByteArray())).build();
|
||||
byte[] data = AES.AESEncrypt(request.toByteArray(), privateSecret);
|
||||
data = Base64.getEncoder().encode(data);
|
||||
|
||||
StringEntity entity = new StringEntity(new String(data), Charset.forName("UTF-8"));
|
||||
entity.setContentEncoding("UTF-8");
|
||||
entity.setContentType("application/json");
|
||||
httpPost.setEntity(entity);
|
||||
|
||||
byte[] cidByte = AES.AESEncrypt(clientId.getBytes(), commonSecret);
|
||||
cidByte = Base64.getEncoder().encode(cidByte);
|
||||
String cid = new String(cidByte);
|
||||
httpPost.setHeader("cid", cid);
|
||||
|
||||
byte[] uidByte = AES.AESEncrypt(userId.getBytes(), commonSecret);
|
||||
uidByte = Base64.getEncoder().encode(uidByte);
|
||||
String uid = new String(uidByte);
|
||||
httpPost.setHeader("uid", uid);
|
||||
|
||||
|
||||
HttpResponse response = httpClient.execute(httpPost);
|
||||
if(response != null){
|
||||
if (response.getStatusLine().getStatusCode() != 200) {
|
||||
System.out.println("Http response error {" + response.getStatusLine().getStatusCode() + "}");
|
||||
return false;
|
||||
}
|
||||
HttpEntity resEntity = response.getEntity();
|
||||
if(resEntity != null){
|
||||
try {
|
||||
byte[] bytes = new byte[resEntity.getContent().available()];
|
||||
resEntity.getContent().read(bytes);
|
||||
if (bytes[0] == 0) {
|
||||
byte[] bytes1 = new byte[bytes.length -1];
|
||||
for (int i = 0; i < bytes1.length; i++) {
|
||||
bytes1[i] = bytes[i+1];
|
||||
}
|
||||
byte[] rawData = AES.AESDecrypt(bytes1, privateSecret, true);
|
||||
WFCMessage.RouteResponse routeResponse = WFCMessage.RouteResponse.parseFrom(rawData);
|
||||
mqttServerIp = routeResponse.getHost();
|
||||
mqttServerPort = routeResponse.getLongPort();
|
||||
routeResponse.getShortPort();
|
||||
return true;
|
||||
} else {
|
||||
System.out.println("the route failure:" + bytes[0]);
|
||||
return false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
System.out.println("Http response nil");
|
||||
}
|
||||
}catch(Exception ex){
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected() {
|
||||
System.out.println("onConnected");
|
||||
connectionStatus = ConnectionStatus_Connecting;
|
||||
if(connectionStatusCallback != null) {
|
||||
connectionStatusCallback.onConnectionStatusChanged(connectionStatus);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected() {
|
||||
System.out.println("onDisconnected");
|
||||
connectionStatus = ConnectionStatus_Unconnected;
|
||||
if(connectionStatusCallback != null) {
|
||||
connectionStatusCallback.onConnectionStatusChanged(connectionStatus);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPublish(UTF8Buffer topic, Buffer body, Runnable ack) {
|
||||
System.out.println("onPublish" + topic.toString());
|
||||
ack.run();
|
||||
if (topic.toString().equals("MN")) {
|
||||
try {
|
||||
WFCMessage.NotifyMessage notifyMessage = WFCMessage.NotifyMessage.parseFrom(body.toByteArray());
|
||||
WFCMessage.PullMessageRequest request = WFCMessage.PullMessageRequest.newBuilder().setId(messageHead).setType(notifyMessage.getType()).build();
|
||||
byte[] data = request.toByteArray();
|
||||
data = AES.AESEncrypt(data, privateSecret);
|
||||
connection.publish("MP", data, QoS.AT_LEAST_ONCE, false, new Callback<byte[]>(){
|
||||
@Override
|
||||
public void onSuccess(byte[] value) {
|
||||
if (value == null || value.length == 0) {
|
||||
System.out.println("Invalide data");
|
||||
}
|
||||
|
||||
if (value[0] != 0) {
|
||||
System.out.println("Pull message error with errorCode:" + value[0]);
|
||||
}
|
||||
|
||||
byte[] data = new byte[value.length - 1];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = value[i+1];
|
||||
}
|
||||
|
||||
try {
|
||||
data = AES.AESDecrypt(data, privateSecret, true);
|
||||
try {
|
||||
WFCMessage.PullMessageResult result = WFCMessage.PullMessageResult.parseFrom(data);
|
||||
if (receiveMessageCallback != null && result.getMessageList().size() > 0) {
|
||||
List<WFCMessage.Message> messages = result.getMessageList();
|
||||
List<WFCMessage.Message> out = new ArrayList<>();
|
||||
for (WFCMessage.Message msg : messages) {
|
||||
if (msg.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Private && msg.getConversation().getTarget().equals(userId)) {
|
||||
msg = msg.toBuilder().setConversation(msg.getConversation().toBuilder().setTarget(msg.getFromUser())).build();
|
||||
}
|
||||
out.add(msg);
|
||||
}
|
||||
receiveMessageCallback.onReceiveMessages(out, false);
|
||||
}
|
||||
messageHead = result.getHead();
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
System.out.println("onSuccess");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable value) {
|
||||
System.out.println("onFailure");
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable value) {
|
||||
System.out.println("onDisconnected" + value.toString());
|
||||
if(connectionStatusCallback != null) {
|
||||
connectionStatusCallback.onConnectionStatusChanged(ConnectionStatus_Unconnected);
|
||||
}
|
||||
}
|
||||
|
||||
public void setConnectionStatusCallback(ConnectionStatusCallback connectionStatusCallback) {
|
||||
this.connectionStatusCallback = connectionStatusCallback;
|
||||
}
|
||||
|
||||
public void setReceiveMessageCallback(ReceiveMessageCallback receiveMessageCallback) {
|
||||
this.receiveMessageCallback = receiveMessageCallback;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public ConnectionStatus getConnectionStatus() {
|
||||
return connectionStatus;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
//token与userid和clientid是绑定的,使用时一定要传入正确的userid和clientid,不然会认为token非法
|
||||
//clientId唯一代表一个设备,只能有一个登录。如果使用同一个clientId登录多次,会出现不可预料问题。
|
||||
IMClient client = new IMClient("KNK_K_00", "6I7UTfKSQT7VhsUJfU6UCvDeCyblLfcXPfajAFxgFGOcqvfFVQCvYy8A5vfAGUj2LSYgoM5W02MHh6jtG/otQs6ft8zLT4Efz315UlDLfrz+OSQYfwAuGFw9m0/x4ckrgiooprsKFxPMaNcCDact0qvp1dBhbeGUipdWVhI33b4=", "5B6DEB72-8A3D-45E2-A88A-47DC4E692B00", "192.168.0.193", 80);
|
||||
|
||||
client.setReceiveMessageCallback(new ReceiveMessageCallback() {
|
||||
@Override
|
||||
public void onReceiveMessages(List<WFCMessage.Message> messageList, boolean hasMore) {
|
||||
System.out.println("recieved messages");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecallMessage(long messageUid) {
|
||||
System.out.println("recalled messages");
|
||||
}
|
||||
});
|
||||
|
||||
client.setConnectionStatusCallback((ConnectionStatus newStatus) -> {
|
||||
if (newStatus == ConnectionStatus_Connected) {
|
||||
WFCMessage.Conversation conversation = WFCMessage.Conversation.newBuilder().setType(0).setTarget("IMININnn").setLine(0).build();
|
||||
WFCMessage.MessageContent messageContent = WFCMessage.MessageContent.newBuilder().setSearchableContent("helloworld").setType(1).build();
|
||||
client.sendMessage(conversation, messageContent, new SendMessageCallback() {
|
||||
@Override
|
||||
public void onSuccess(long messageUid, long timestamp) {
|
||||
System.out.println("send success");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(int errorCode) {
|
||||
System.out.println("send failure");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
client.connect();
|
||||
|
||||
try {
|
||||
Thread.sleep(1000000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
/**
|
||||
* 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.client;
|
||||
|
||||
import org.fusesource.hawtbuf.Buffer;
|
||||
import org.fusesource.hawtbuf.UTF8Buffer;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static org.fusesource.hawtbuf.Buffer.utf8;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A blocking Connection interface to MQTT.
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class BlockingConnection {
|
||||
|
||||
private final FutureConnection next;
|
||||
|
||||
public BlockingConnection(FutureConnection next) {
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return next.isConnected();
|
||||
}
|
||||
|
||||
public void connect() throws Exception {
|
||||
this.next.connect().await();
|
||||
}
|
||||
|
||||
public void disconnect() throws Exception {
|
||||
this.next.disconnect().await();
|
||||
}
|
||||
|
||||
public void kill() throws Exception {
|
||||
this.next.kill().await();
|
||||
}
|
||||
|
||||
public byte[] subscribe(final Topic[] topics) throws Exception {
|
||||
return this.next.subscribe(topics).await();
|
||||
}
|
||||
|
||||
public void unsubscribe(final String[] topics) throws Exception {
|
||||
this.next.unsubscribe(topics).await();
|
||||
}
|
||||
|
||||
public void unsubscribe(final UTF8Buffer[] topics) throws Exception {
|
||||
this.next.unsubscribe(topics).await();
|
||||
}
|
||||
|
||||
public void publish(final UTF8Buffer topic, final Buffer payload, final QoS qos, final boolean retain) throws Exception {
|
||||
this.next.publish(topic, payload, qos, retain).await();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object clone() throws CloneNotSupportedException {
|
||||
return super.clone(); //To change body of overridden methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public void publish(final String topic, final byte[] payload, final QoS qos, final boolean retain) throws Exception {
|
||||
publish(utf8(topic), new Buffer(payload), qos, retain);
|
||||
}
|
||||
|
||||
public Message receive() throws Exception {
|
||||
return this.next.receive().await();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null if the receive times out.
|
||||
*/
|
||||
public Message receive(long amount, TimeUnit unit) throws Exception {
|
||||
Future<Message> receive = this.next.receive();
|
||||
try {
|
||||
Message message = receive.await(amount, unit);
|
||||
if( message!=null ) {
|
||||
message.blocking = true;
|
||||
}
|
||||
return message;
|
||||
} catch (TimeoutException e) {
|
||||
// Put it back on the queue..
|
||||
receive.then(new Callback<Message>() {
|
||||
public void onSuccess(final Message value) {
|
||||
next.putBackMessage(value);
|
||||
}
|
||||
public void onFailure(Throwable value) {
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setReceiveBuffer(final long receiveBuffer) throws InterruptedException {
|
||||
final CountDownLatch done = new CountDownLatch(1);
|
||||
next.getDispatchQueue().execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
next.setReceiveBuffer(receiveBuffer);
|
||||
} finally {
|
||||
done.countDown();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
done.await();
|
||||
}
|
||||
|
||||
public long getReceiveBuffer() throws InterruptedException {
|
||||
final CountDownLatch done = new CountDownLatch(1);
|
||||
final AtomicLong result = new AtomicLong();
|
||||
next.getDispatchQueue().execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
result.set(next.getReceiveBuffer());
|
||||
} finally {
|
||||
done.countDown();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
done.await();
|
||||
return result.get();
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
next.resume();
|
||||
}
|
||||
|
||||
public void suspend() {
|
||||
next.suspend();
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* 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.client;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Function Result that carries one value.
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public interface Callback<T> {
|
||||
public void onSuccess(T value);
|
||||
public void onFailure(Throwable value);
|
||||
}
|
||||
@@ -1,966 +0,0 @@
|
||||
/**
|
||||
* 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.client;
|
||||
|
||||
import org.fusesource.hawtbuf.Buffer;
|
||||
import org.fusesource.hawtbuf.HexSupport;
|
||||
import org.fusesource.hawtbuf.UTF8Buffer;
|
||||
import org.fusesource.hawtdispatch.Dispatch;
|
||||
import org.fusesource.hawtdispatch.DispatchQueue;
|
||||
import org.fusesource.hawtdispatch.Task;
|
||||
import org.fusesource.hawtdispatch.transport.DefaultTransportListener;
|
||||
import org.fusesource.hawtdispatch.transport.HeartBeatMonitor;
|
||||
import org.fusesource.hawtdispatch.transport.SslTransport;
|
||||
import org.fusesource.hawtdispatch.transport.TcpTransport;
|
||||
import org.fusesource.hawtdispatch.transport.Transport;
|
||||
import org.fusesource.mqtt.codec.CONNACK;
|
||||
import org.fusesource.mqtt.codec.DISCONNECT;
|
||||
import org.fusesource.mqtt.codec.MQTTFrame;
|
||||
import org.fusesource.mqtt.codec.MQTTProtocolCodec;
|
||||
import org.fusesource.mqtt.codec.MessageSupport.Acked;
|
||||
import org.fusesource.mqtt.codec.PINGREQ;
|
||||
import org.fusesource.mqtt.codec.PINGRESP;
|
||||
import org.fusesource.mqtt.codec.PUBACK;
|
||||
import org.fusesource.mqtt.codec.PUBCOMP;
|
||||
import org.fusesource.mqtt.codec.PUBLISH;
|
||||
import org.fusesource.mqtt.codec.PUBREC;
|
||||
import org.fusesource.mqtt.codec.PUBREL;
|
||||
import org.fusesource.mqtt.codec.SUBACK;
|
||||
import org.fusesource.mqtt.codec.SUBSCRIBE;
|
||||
import org.fusesource.mqtt.codec.UNSUBACK;
|
||||
import org.fusesource.mqtt.codec.UNSUBSCRIBE;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.fusesource.hawtbuf.Buffer.utf8;
|
||||
import static org.fusesource.hawtdispatch.Dispatch.createQueue;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A callback based non/blocking Connection interface to MQTT.
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class CallbackConnection {
|
||||
|
||||
private static class Request {
|
||||
private final MQTTFrame frame;
|
||||
private final short id;
|
||||
private final Callback cb;
|
||||
|
||||
Request(int id, MQTTFrame frame, Callback cb) {
|
||||
this.id = (short) id;
|
||||
this.cb = cb;
|
||||
this.frame = frame;
|
||||
}
|
||||
}
|
||||
|
||||
private static final ExtendedListener DEFAULT_LISTENER = new ExtendedListener(){
|
||||
public void onConnected() {
|
||||
}
|
||||
public void onDisconnected() {
|
||||
}
|
||||
public void onPublish(UTF8Buffer utf8Buffer, Buffer buffer, Runnable runnable) {
|
||||
this.onFailure(createListenerNotSetError());
|
||||
}
|
||||
public void onPublish(UTF8Buffer topic, Buffer body, Callback<Callback<byte[]>> ack) {
|
||||
this.onFailure(createListenerNotSetError());
|
||||
}
|
||||
public void onFailure(Throwable value) {
|
||||
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), value);
|
||||
}
|
||||
};
|
||||
|
||||
private final DispatchQueue queue;
|
||||
private final MQTT mqtt;
|
||||
private Transport transport;
|
||||
private ExtendedListener listener = DEFAULT_LISTENER;
|
||||
private Runnable refiller;
|
||||
private Map<Short, Request> requests = new ConcurrentHashMap<Short, Request>();
|
||||
private LinkedList<Request> overflow = new LinkedList<Request>();
|
||||
private final HashMap<Short, Callback<byte[]>> processed = new HashMap<Short, Callback<byte[]>>();
|
||||
private Throwable failure;
|
||||
private boolean disconnected = false;
|
||||
private HeartBeatMonitor heartBeatMonitor;
|
||||
private long pingedAt;
|
||||
private long reconnects = 0;
|
||||
private final AtomicInteger suspendCount = new AtomicInteger(0);
|
||||
private final AtomicInteger suspendChanges = new AtomicInteger(0);
|
||||
|
||||
private final HashMap<UTF8Buffer, QoS> activeSubs = new HashMap<UTF8Buffer, QoS>();
|
||||
|
||||
|
||||
public CallbackConnection(MQTT mqtt) {
|
||||
this.mqtt = mqtt;
|
||||
if(this.mqtt.dispatchQueue == null) {
|
||||
this.queue = createQueue("mqtt client");
|
||||
} else {
|
||||
this.queue = this.mqtt.dispatchQueue;
|
||||
}
|
||||
}
|
||||
|
||||
public void connect(final Callback<byte[]> cb) {
|
||||
assert cb !=null : "Callback should not be null.";
|
||||
|
||||
if( transport!=null ) {
|
||||
cb.onFailure(new IllegalStateException("Already connected"));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
createTransport(new LoginHandler(cb, true));
|
||||
} catch (Throwable e) {
|
||||
// This error happens when the MQTT config is invalid, reattempting
|
||||
// wont fix this case.
|
||||
cb.onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
void reconnect() {
|
||||
try {
|
||||
// And reconnect.
|
||||
createTransport(new LoginHandler(new Callback<byte[]>() {
|
||||
public void onSuccess(byte[] value) {
|
||||
|
||||
mqtt.tracer.debug("Restoring MQTT connection state");
|
||||
// Setup a new overflow so that the replay can be sent out before the original overflow list.
|
||||
LinkedList<Request> originalOverflow = overflow;
|
||||
Map<Short, Request> originalRequests = requests;
|
||||
overflow = new LinkedList<Request>();
|
||||
requests = new ConcurrentHashMap<Short, Request>();
|
||||
|
||||
// Restore any active subscriptions.
|
||||
if (!activeSubs.isEmpty()) {
|
||||
ArrayList<Topic> topics = new ArrayList<Topic>(activeSubs.size());
|
||||
for (Map.Entry<UTF8Buffer, QoS> entry : activeSubs.entrySet()) {
|
||||
topics.add(new Topic(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
send(new SUBSCRIBE().topics(topics.toArray(new Topic[topics.size()])), null);
|
||||
}
|
||||
|
||||
// Replay any un-acked requests..
|
||||
for (Map.Entry<Short, Request> entry : originalRequests.entrySet()) {
|
||||
MQTTFrame frame = entry.getValue().frame;
|
||||
frame.dup(true); // set the dup flag as these frames were previously transmitted.
|
||||
send(entry.getValue());
|
||||
}
|
||||
|
||||
// Replay the original overflow
|
||||
for (Request request : originalOverflow) {
|
||||
// Stuff in the overflow never got sent out.. so no need to set the dup flag
|
||||
send(request);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void onFailure(Throwable value) {
|
||||
handleFatalFailure(value);
|
||||
}
|
||||
}, false));
|
||||
} catch (Throwable e) {
|
||||
handleFatalFailure(e);
|
||||
}
|
||||
}
|
||||
void handleSessionFailure(Throwable error) {
|
||||
// Socket failure, should we try to reconnect?
|
||||
if( !disconnected && (mqtt.reconnectAttemptsMax<0 || reconnects < mqtt.reconnectAttemptsMax ) ) {
|
||||
|
||||
mqtt.tracer.debug("Reconnecting transport");
|
||||
// Cleanup the previous transport.
|
||||
if(heartBeatMonitor!=null) {
|
||||
heartBeatMonitor.stop();
|
||||
heartBeatMonitor = null;
|
||||
}
|
||||
final Transport t = transport;
|
||||
transport = null;
|
||||
|
||||
if(t!=null) {
|
||||
t.stop(new Task() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onDisconnected();
|
||||
reconnect();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
reconnect();
|
||||
}
|
||||
|
||||
} else {
|
||||
// nope.
|
||||
handleFatalFailure(error);
|
||||
}
|
||||
}
|
||||
|
||||
void reconnect(final Callback<Transport> onConnect) {
|
||||
long reconnectDelay = mqtt.reconnectDelay;
|
||||
if( reconnectDelay> 0 && mqtt.reconnectBackOffMultiplier > 1.0 ) {
|
||||
reconnectDelay = (long) Math.pow(mqtt.reconnectDelay*reconnects, mqtt.reconnectBackOffMultiplier);
|
||||
}
|
||||
reconnectDelay = Math.min(reconnectDelay, mqtt.reconnectDelayMax);
|
||||
reconnects += 1;
|
||||
queue.executeAfter(reconnectDelay, TimeUnit.MILLISECONDS, new Task() {
|
||||
@Override
|
||||
public void run() {
|
||||
if(disconnected) {
|
||||
onConnect.onFailure(createDisconnectedError());
|
||||
} else {
|
||||
try {
|
||||
createTransport(onConnect);
|
||||
} catch (Exception e) {
|
||||
onConnect.onFailure(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and start a transport to the MQTT server. Passes it to the onConnect
|
||||
* once the transport is connected.
|
||||
*
|
||||
* @param onConnect
|
||||
* @throws Exception
|
||||
*/
|
||||
void createTransport(final Callback<Transport> onConnect) throws Exception {
|
||||
mqtt.tracer.debug("Connecting");
|
||||
String scheme = mqtt.host.getScheme();
|
||||
|
||||
final Transport transport;
|
||||
if( "tcp".equals(scheme) ) {
|
||||
transport = new TcpTransport();
|
||||
} else if( SslTransport.protocol(scheme)!=null ) {
|
||||
SslTransport ssl = new SslTransport();
|
||||
if( mqtt.sslContext == null ) {
|
||||
mqtt.sslContext = SSLContext.getDefault();
|
||||
}
|
||||
ssl.setSSLContext(mqtt.sslContext);
|
||||
transport = ssl;
|
||||
} else {
|
||||
throw new Exception("Unsupported URI scheme '"+scheme+"'");
|
||||
}
|
||||
|
||||
if( mqtt.blockingExecutor == null ) {
|
||||
mqtt.blockingExecutor = MQTT.getBlockingThreadPool();
|
||||
}
|
||||
transport.setBlockingExecutor(mqtt.blockingExecutor);
|
||||
transport.setDispatchQueue(queue);
|
||||
transport.setProtocolCodec(new MQTTProtocolCodec());
|
||||
|
||||
if( transport instanceof TcpTransport ) {
|
||||
TcpTransport tcp = (TcpTransport)transport;
|
||||
tcp.setMaxReadRate(mqtt.maxReadRate);
|
||||
tcp.setMaxWriteRate(mqtt.maxWriteRate);
|
||||
tcp.setReceiveBufferSize(mqtt.receiveBufferSize);
|
||||
tcp.setSendBufferSize(mqtt.sendBufferSize);
|
||||
tcp.setTrafficClass(mqtt.trafficClass);
|
||||
tcp.setUseLocalHost(mqtt.useLocalHost);
|
||||
tcp.connecting(mqtt.host, mqtt.localAddress);
|
||||
}
|
||||
|
||||
transport.setTransportListener(new DefaultTransportListener(){
|
||||
@Override
|
||||
public void onTransportConnected() {
|
||||
mqtt.tracer.debug("Transport connected");
|
||||
if(disconnected) {
|
||||
onFailure(createDisconnectedError());
|
||||
} else {
|
||||
onConnect.onSuccess(transport);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransportFailure(final IOException error) {
|
||||
mqtt.tracer.debug("Transport failure: %s", error);
|
||||
onFailure(error);
|
||||
}
|
||||
|
||||
private void onFailure(final Throwable error) {
|
||||
if(!transport.isClosed()) {
|
||||
transport.stop(new Task() {
|
||||
@Override
|
||||
public void run() {
|
||||
onConnect.onFailure(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
transport.start(NOOP);
|
||||
}
|
||||
|
||||
class LoginHandler implements Callback<Transport> {
|
||||
private final Callback<byte[]> cb;
|
||||
private final boolean initialConnect;
|
||||
|
||||
LoginHandler(Callback<byte[]> cb, boolean initialConnect) {
|
||||
this.cb = cb;
|
||||
this.initialConnect = initialConnect;
|
||||
}
|
||||
|
||||
public void onSuccess(final Transport transport) {
|
||||
transport.setTransportListener(new DefaultTransportListener() {
|
||||
@Override
|
||||
public void onTransportFailure(IOException error) {
|
||||
mqtt.tracer.debug("Transport failure: %s", error);
|
||||
transport.stop(NOOP);
|
||||
onFailure(error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransportCommand(Object command) {
|
||||
MQTTFrame response = (MQTTFrame) command;
|
||||
mqtt.tracer.onReceive(response);
|
||||
try {
|
||||
switch (response.messageType()) {
|
||||
case CONNACK.TYPE:
|
||||
CONNACK connack = new CONNACK().decode(response);
|
||||
switch (connack.code()) {
|
||||
case CONNECTION_ACCEPTED:
|
||||
mqtt.tracer.debug("MQTT login accepted");
|
||||
onSessionEstablished(transport);
|
||||
listener.onConnected();
|
||||
cb.onSuccess(connack.payload);
|
||||
queue.execute(new Task() {
|
||||
@Override
|
||||
public void run() {
|
||||
drainOverflow();
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
mqtt.tracer.debug("MQTT login rejected");
|
||||
// Bad creds or something. No point in reconnecting.
|
||||
transport.stop(NOOP);
|
||||
cb.onFailure(new MQTTException("Could not connect: " + connack.code(), connack));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mqtt.tracer.debug("Received unexpected MQTT frame: %d", response.messageType());
|
||||
// Naughty MQTT server? No point in reconnecting.
|
||||
transport.stop(NOOP);
|
||||
cb.onFailure(new IOException("Could not connect. Received unexpected command: " + response.messageType()));
|
||||
|
||||
}
|
||||
} catch (ProtocolException e) {
|
||||
mqtt.tracer.debug("Protocol error: %s", e);
|
||||
transport.stop(NOOP);
|
||||
cb.onFailure(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
transport.resumeRead();
|
||||
if( mqtt.connect.clientId() == null ) {
|
||||
String id = hex(transport.getLocalAddress())+Long.toHexString(System.currentTimeMillis()/1000);
|
||||
if(id.length() > 23) {
|
||||
id = id.substring(0,23);
|
||||
}
|
||||
mqtt.connect.clientId(utf8(id));
|
||||
}
|
||||
MQTTFrame encoded = mqtt.connect.encode();
|
||||
boolean accepted = transport.offer(encoded);
|
||||
mqtt.tracer.onSend(encoded);
|
||||
mqtt.tracer.debug("Logging in");
|
||||
assert accepted: "First frame should always be accepted by the transport";
|
||||
}
|
||||
|
||||
private boolean tryReconnect() {
|
||||
if(initialConnect) {
|
||||
return mqtt.connectAttemptsMax<0 || reconnects < mqtt.connectAttemptsMax;
|
||||
}
|
||||
|
||||
return mqtt.reconnectAttemptsMax<0 || reconnects < mqtt.reconnectAttemptsMax;
|
||||
}
|
||||
|
||||
public void onFailure(Throwable value) {
|
||||
// Socket failure, should we try to reconnect?
|
||||
if( !disconnected && tryReconnect() ) {
|
||||
reconnect(this);
|
||||
} else {
|
||||
// nope.
|
||||
cb.onFailure(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean onRefillCalled =false;
|
||||
public void onSessionEstablished(Transport transport) {
|
||||
this.transport = transport;
|
||||
if( suspendCount.get() > 0 ) {
|
||||
this.transport.suspendRead();
|
||||
}
|
||||
this.transport.setTransportListener(new DefaultTransportListener() {
|
||||
@Override
|
||||
public void onTransportCommand(Object command) {
|
||||
MQTTFrame frame = (MQTTFrame) command;
|
||||
mqtt.tracer.onReceive(frame);
|
||||
processFrame(frame);
|
||||
}
|
||||
@Override
|
||||
public void onRefill() {
|
||||
onRefillCalled =true;
|
||||
drainOverflow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransportFailure(IOException error) {
|
||||
handleSessionFailure(error);
|
||||
}
|
||||
});
|
||||
pingedAt = 0;
|
||||
if(mqtt.getKeepAlive()>0) {
|
||||
heartBeatMonitor = new HeartBeatMonitor();
|
||||
heartBeatMonitor.setWriteInterval((mqtt.getKeepAlive() * 1000) / 2);
|
||||
heartBeatMonitor.setTransport(this.transport);
|
||||
heartBeatMonitor.suspendRead(); // to match the suspended state of the transport.
|
||||
heartBeatMonitor.setOnKeepAlive(new Task() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Don't care if the offer is rejected, just means we have data outbound.
|
||||
if(!disconnected && pingedAt==0) {
|
||||
MQTTFrame encoded = new PINGREQ().encode();
|
||||
if(CallbackConnection.this.transport.offer(encoded)) {
|
||||
mqtt.tracer.onSend(encoded);
|
||||
final long now = System.currentTimeMillis();
|
||||
final long suspends = suspendChanges.get();
|
||||
pingedAt = now;
|
||||
queue.executeAfter(CallbackConnection.this.mqtt.getKeepAlive(), TimeUnit.SECONDS, new Task() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (now == pingedAt) {
|
||||
// if the connection remained suspend we will never get the ping response..
|
||||
// Looks like the user has forgoton to resume the connection
|
||||
if (suspends == suspendChanges.get() && suspendCount.get() > 0) {
|
||||
// Since the connection has been suspended, we can't really
|
||||
// check to see if we are getting the ping responses.
|
||||
mqtt.tracer.debug("The connection has remained suspended for an extended period of time so it cannot do proper keep alive processing. Did you forget to resume the connection?");
|
||||
} else {
|
||||
mqtt.tracer.debug("Ping timeout");
|
||||
handleSessionFailure(new ProtocolException("Ping timeout").fillInStackTrace());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
heartBeatMonitor.start();
|
||||
}
|
||||
}
|
||||
|
||||
public Transport transport() {
|
||||
return transport;
|
||||
}
|
||||
|
||||
public DispatchQueue getDispatchQueue() {
|
||||
return queue;
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
suspendChanges.incrementAndGet();
|
||||
if( suspendCount.decrementAndGet() == 0 && this.transport!=null ) {
|
||||
this.transport.resumeRead();
|
||||
if(this.heartBeatMonitor!=null){
|
||||
this.heartBeatMonitor.resumeRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void suspend() {
|
||||
suspendChanges.incrementAndGet();
|
||||
if( suspendCount.incrementAndGet() == 1 && this.transport!=null ) {
|
||||
this.transport.suspendRead();
|
||||
if(this.heartBeatMonitor!=null){
|
||||
this.heartBeatMonitor.suspendRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CallbackConnection refiller(Runnable refiller) {
|
||||
queue.assertExecuting();
|
||||
this.refiller = refiller;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CallbackConnection listener(final Listener original) {
|
||||
if( original instanceof ExtendedListener ) {
|
||||
this.listener = (ExtendedListener) original;
|
||||
} else {
|
||||
this.listener = new ExtendedListener() {
|
||||
public void onPublish(UTF8Buffer topic, Buffer body, final Callback<Callback<byte[]>> ack) {
|
||||
original.onPublish(topic, body, new Runnable() {
|
||||
public void run() {
|
||||
ack.onSuccess(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
public void onPublish(UTF8Buffer topic, Buffer body, Runnable ack) {
|
||||
original.onPublish(topic, body, ack);
|
||||
}
|
||||
|
||||
public void onConnected() {
|
||||
original.onConnected();
|
||||
}
|
||||
|
||||
public void onDisconnected() {
|
||||
original.onDisconnected();
|
||||
}
|
||||
|
||||
public void onFailure(Throwable value) {
|
||||
original.onFailure(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean full() {
|
||||
queue.assertExecuting();
|
||||
return this.transport.full();
|
||||
}
|
||||
|
||||
public Throwable failure() {
|
||||
queue.assertExecuting();
|
||||
return failure;
|
||||
}
|
||||
|
||||
public void disconnect(boolean clearSession, final Callback<Void> onComplete) {
|
||||
if( disconnected ) {
|
||||
if(onComplete!=null){
|
||||
onComplete.onSuccess(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
disconnected = true;
|
||||
final short requestId = getNextMessageId();
|
||||
final Runnable stop = new Runnable() {
|
||||
private boolean executed = false;
|
||||
public void run() {
|
||||
if(!executed) {
|
||||
executed = true;
|
||||
requests.remove(requestId);
|
||||
|
||||
if(heartBeatMonitor!=null) {
|
||||
heartBeatMonitor.stop();
|
||||
heartBeatMonitor = null;
|
||||
}
|
||||
transport.stop(new Task() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onDisconnected();
|
||||
if (onComplete != null) {
|
||||
onComplete.onSuccess(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Callback<Void> cb = new Callback<Void>() {
|
||||
public void onSuccess(Void v) {
|
||||
// To make sure DISCONNECT has been flushed out to the socket
|
||||
onRefillCalled = false;
|
||||
refiller = new Runnable() {
|
||||
public void run() {
|
||||
if(onRefillCalled) {
|
||||
stop.run();
|
||||
}
|
||||
}
|
||||
};
|
||||
if(transport != null){
|
||||
transport.flush();
|
||||
}
|
||||
}
|
||||
public void onFailure(Throwable value) {
|
||||
stop.run();
|
||||
}
|
||||
};
|
||||
|
||||
// Pop the frame into a request so it we get notified
|
||||
// of any failures so we continue to stop the transport.
|
||||
if(transport!=null) {
|
||||
MQTTFrame frame = new DISCONNECT().encode().dup(clearSession);
|
||||
send(new Request(getNextMessageId(), frame, cb));
|
||||
} else {
|
||||
cb.onSuccess(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kills the connection without a graceful disconnect.
|
||||
* @param onComplete
|
||||
*/
|
||||
public void kill(final Callback<Void> onComplete) {
|
||||
if( disconnected ) {
|
||||
if(onComplete!=null){
|
||||
onComplete.onSuccess(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
disconnected = true;
|
||||
if(heartBeatMonitor!=null) {
|
||||
heartBeatMonitor.stop();
|
||||
heartBeatMonitor = null;
|
||||
}
|
||||
transport.stop(new Task() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onDisconnected();
|
||||
if (onComplete != null) {
|
||||
onComplete.onSuccess(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void publish(String topic, byte[] payload, QoS qos, boolean retain, Callback<byte[]> cb) {
|
||||
publish(utf8(topic), new Buffer(payload), qos, retain, cb);
|
||||
}
|
||||
|
||||
public void publish(UTF8Buffer topic, Buffer payload, QoS qos, boolean retain, Callback<byte[]> cb) {
|
||||
queue.assertExecuting();
|
||||
if( disconnected ) {
|
||||
cb.onFailure(createDisconnectedError());
|
||||
return;
|
||||
}
|
||||
PUBLISH command = new PUBLISH().qos(qos).retain(retain);
|
||||
command.topicName(topic).payload(payload);
|
||||
send(command, cb);
|
||||
}
|
||||
|
||||
public void subscribe(final Topic[] topics, Callback<byte[]> cb) {
|
||||
if(topics==null) {
|
||||
throw new IllegalArgumentException("topics must not be null");
|
||||
}
|
||||
queue.assertExecuting();
|
||||
if( disconnected ) {
|
||||
cb.onFailure(createDisconnectedError());
|
||||
return;
|
||||
}
|
||||
if( listener == DEFAULT_LISTENER ) {
|
||||
cb.onFailure(createListenerNotSetError());
|
||||
} else {
|
||||
send(new SUBSCRIBE().topics(topics), new ProxyCallback<byte[]>(cb){
|
||||
@Override
|
||||
public void onSuccess(byte[] value) {
|
||||
for (Topic topic : topics) {
|
||||
activeSubs.put(topic.name(), topic.qos());
|
||||
}
|
||||
if(next!=null) {
|
||||
next.onSuccess(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void unsubscribe(final UTF8Buffer[] topics, Callback<Void> cb) {
|
||||
queue.assertExecuting();
|
||||
if( disconnected ) {
|
||||
cb.onFailure(createDisconnectedError());
|
||||
return;
|
||||
}
|
||||
send(new UNSUBSCRIBE().topics(topics), new ProxyCallback(cb){
|
||||
@Override
|
||||
public void onSuccess(Object value) {
|
||||
for (UTF8Buffer topic : topics) {
|
||||
activeSubs.remove(topic);
|
||||
}
|
||||
if(next!=null) {
|
||||
next.onSuccess(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void send(Acked command, Callback cb) {
|
||||
short id = 0;
|
||||
if(command.qos() != QoS.AT_MOST_ONCE) {
|
||||
id = getNextMessageId();
|
||||
command.messageId(id);
|
||||
}
|
||||
send(new Request(id, command.encode(), cb));
|
||||
}
|
||||
|
||||
private void send(Request request) {
|
||||
if( failure !=null ) {
|
||||
if( request.cb!=null ) {
|
||||
request.cb.onFailure(failure);
|
||||
}
|
||||
} else {
|
||||
// Put the request in the map before sending it over the wire.
|
||||
if(request.id!=0) {
|
||||
this.requests.put(request.id, request);
|
||||
}
|
||||
|
||||
if( overflow.isEmpty() && transport!=null && transport.offer(request.frame) ) {
|
||||
mqtt.tracer.onSend(request.frame);
|
||||
if(request.id==0) {
|
||||
if( request.cb!=null ) {
|
||||
((Callback<Void>)request.cb).onSuccess(null);
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
// Remove it from the request.
|
||||
this.requests.remove(request.id);
|
||||
overflow.addLast(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private short nextMessageId = 1;
|
||||
private short getNextMessageId() {
|
||||
short rc = nextMessageId;
|
||||
nextMessageId++;
|
||||
if(nextMessageId==0) {
|
||||
nextMessageId=1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
private void drainOverflow() {
|
||||
queue.assertExecuting();
|
||||
if( overflow.isEmpty() || transport==null ){
|
||||
return;
|
||||
}
|
||||
Request request;
|
||||
while((request=overflow.peek())!=null) {
|
||||
if( this.transport.offer(request.frame) ) {
|
||||
mqtt.tracer.onSend(request.frame);
|
||||
overflow.removeFirst();
|
||||
if(request.id==0) {
|
||||
if( request.cb!=null ) {
|
||||
((Callback<Void>)request.cb).onSuccess(null);
|
||||
}
|
||||
} else {
|
||||
this.requests.put(request.id, request);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( overflow.isEmpty() ) {
|
||||
if( refiller!=null ) {
|
||||
try {
|
||||
refiller.run();
|
||||
} catch (Throwable e) {
|
||||
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void completeRequest(short id, byte originalType, Object arg) {
|
||||
Request request = requests.remove(id);
|
||||
if( request!=null ) {
|
||||
assert originalType==request.frame.messageType();
|
||||
if(request.cb!=null) {
|
||||
if( arg==null ) {
|
||||
((Callback<Void>)request.cb).onSuccess(null);
|
||||
} else {
|
||||
((Callback<Object>)request.cb).onSuccess(arg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
handleFatalFailure(new ProtocolException("Command from server contained an invalid message id: " + id));
|
||||
}
|
||||
}
|
||||
|
||||
private void processFrame(MQTTFrame frame) {
|
||||
try {
|
||||
switch(frame.messageType()) {
|
||||
case PUBLISH.TYPE: {
|
||||
PUBLISH publish = new PUBLISH().decode(frame);
|
||||
toReceiver(publish);
|
||||
break;
|
||||
}
|
||||
case PUBREL.TYPE:{
|
||||
PUBREL ack = new PUBREL().decode(frame);
|
||||
Callback<byte[]> onRel = processed.remove(ack.messageId());
|
||||
PUBCOMP response = new PUBCOMP();
|
||||
response.messageId(ack.messageId());
|
||||
send(new Request(0, response.encode(), null));
|
||||
if( onRel!=null ) {
|
||||
onRel.onSuccess(null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PUBACK.TYPE:{
|
||||
PUBACK ack = new PUBACK().decode(frame);
|
||||
completeRequest(ack.messageId(), PUBLISH.TYPE, ack.payload);
|
||||
break;
|
||||
}
|
||||
case PUBREC.TYPE:{
|
||||
PUBREC ack = new PUBREC().decode(frame);
|
||||
PUBREL response = new PUBREL();
|
||||
response.messageId(ack.messageId());
|
||||
send(new Request(0, response.encode(), null));
|
||||
break;
|
||||
}
|
||||
case PUBCOMP.TYPE:{
|
||||
PUBCOMP ack = new PUBCOMP().decode(frame);
|
||||
completeRequest(ack.messageId(), PUBLISH.TYPE, null);
|
||||
break;
|
||||
}
|
||||
case SUBACK.TYPE: {
|
||||
SUBACK ack = new SUBACK().decode(frame);
|
||||
completeRequest(ack.messageId(), SUBSCRIBE.TYPE, ack.grantedQos());
|
||||
break;
|
||||
}
|
||||
case UNSUBACK.TYPE: {
|
||||
UNSUBACK ack = new UNSUBACK().decode(frame);
|
||||
completeRequest(ack.messageId(), UNSUBSCRIBE.TYPE, null);
|
||||
break;
|
||||
}
|
||||
case PINGRESP.TYPE: {
|
||||
pingedAt = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ProtocolException("Unexpected MQTT command type: "+frame.messageType());
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
handleFatalFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
static public final Task NOOP = Dispatch.NOOP;
|
||||
|
||||
private void toReceiver(final PUBLISH publish) {
|
||||
if( listener !=null ) {
|
||||
try {
|
||||
Callback<Callback<byte[]>> cb = null;
|
||||
switch( publish.qos() ) {
|
||||
case AT_LEAST_ONCE:
|
||||
cb = new Callback<Callback<byte[]>>() {
|
||||
public void onSuccess(Callback<byte[]> value) {
|
||||
PUBACK response = new PUBACK();
|
||||
response.messageId(publish.messageId());
|
||||
send(new Request(0, response.encode(), null));
|
||||
if( value !=null ) {
|
||||
value.onSuccess(null);
|
||||
}
|
||||
}
|
||||
public void onFailure(Throwable value) {
|
||||
}
|
||||
};
|
||||
break;
|
||||
case EXACTLY_ONCE:
|
||||
cb = new Callback<Callback<byte[]>>() {
|
||||
public void onSuccess(Callback<byte[]> value) {
|
||||
PUBREC response = new PUBREC();
|
||||
response.messageId(publish.messageId());
|
||||
processed.put(publish.messageId(), value);
|
||||
send(new Request(0, response.encode(), null));
|
||||
}
|
||||
|
||||
public void onFailure(Throwable value) {
|
||||
}
|
||||
};
|
||||
// Looks like a dup delivery.. filter it out.
|
||||
if( processed.get(publish.messageId())!=null ) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case AT_MOST_ONCE:
|
||||
cb = new Callback<Callback<byte[]>>() {
|
||||
public void onSuccess(Callback<byte[]> value) {
|
||||
if (value != null) {
|
||||
value.onSuccess(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void onFailure(Throwable value) {
|
||||
}
|
||||
};
|
||||
}
|
||||
listener.onPublish(publish.topicName(), publish.payload(), cb);
|
||||
} catch (Throwable e) {
|
||||
handleFatalFailure(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleFatalFailure(Throwable error) {
|
||||
if( failure == null ) {
|
||||
failure = error;
|
||||
|
||||
mqtt.tracer.debug("Fatal connection failure: %s", error);
|
||||
// Fail incomplete requests.
|
||||
ArrayList<Request> values = new ArrayList(requests.values());
|
||||
requests.clear();
|
||||
for (Request value : values) {
|
||||
if( value.cb!= null ) {
|
||||
value.cb.onFailure(failure);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<Request> overflowEntries = new ArrayList<Request>(overflow);
|
||||
overflow.clear();
|
||||
for (Request entry : overflowEntries) {
|
||||
if( entry.cb !=null ) {
|
||||
entry.cb.onFailure(failure);
|
||||
}
|
||||
}
|
||||
|
||||
if( listener !=null && !disconnected ) {
|
||||
try {
|
||||
listener.onFailure(failure);
|
||||
} catch (Exception e) {
|
||||
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IllegalStateException createListenerNotSetError() {
|
||||
return (IllegalStateException) new IllegalStateException("No connection listener set to handle message received from the server.").fillInStackTrace();
|
||||
}
|
||||
|
||||
private static IllegalStateException createDisconnectedError() {
|
||||
return (IllegalStateException) new IllegalStateException("Disconnected").fillInStackTrace();
|
||||
}
|
||||
|
||||
static private String hex(SocketAddress address) {
|
||||
if( address instanceof InetSocketAddress ) {
|
||||
InetSocketAddress isa = (InetSocketAddress)address;
|
||||
return HexSupport.toHexFromBuffer(new Buffer(isa.getAddress().getAddress()))+Integer.toHexString(isa.getPort());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2016, Red Hat Inc. All rights reserved.
|
||||
*
|
||||
* http://redhat.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.client;
|
||||
|
||||
import org.fusesource.hawtbuf.Buffer;
|
||||
import org.fusesource.hawtbuf.UTF8Buffer;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public interface ExtendedListener extends Listener {
|
||||
public void onPublish(UTF8Buffer topic, Buffer body, Callback<Callback<byte[]>> ack);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/**
|
||||
* 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.client;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* <p>A simplified Future function results interface.</p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public interface Future<T> {
|
||||
T await() throws Exception;
|
||||
T await(long amount, TimeUnit unit) throws Exception;
|
||||
void then(Callback<T> callback);
|
||||
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
/**
|
||||
* 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.client;
|
||||
|
||||
import org.fusesource.hawtbuf.Buffer;
|
||||
import org.fusesource.hawtbuf.UTF8Buffer;
|
||||
import org.fusesource.hawtdispatch.DispatchQueue;
|
||||
import org.fusesource.hawtdispatch.Task;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import static org.fusesource.hawtbuf.Buffer.utf8;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A Future based optionally-blocking Connection interface to MQTT.
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class FutureConnection {
|
||||
|
||||
private final CallbackConnection next;
|
||||
|
||||
private final LinkedList<Promise<Message>> receiveFutures = new LinkedList<Promise<Message>>();
|
||||
private final LinkedList<Message> receivedFrames = new LinkedList<Message>();
|
||||
|
||||
private volatile boolean connected;
|
||||
|
||||
private long receiveBuffer = 1024*1024;
|
||||
private long receiveBufferRemaining = receiveBuffer;
|
||||
private boolean receiveBufferFull = false;
|
||||
|
||||
public FutureConnection(CallbackConnection next) {
|
||||
this.next = next;
|
||||
this.next.listener(new ExtendedListener() {
|
||||
|
||||
public void onConnected() {
|
||||
connected = true;
|
||||
}
|
||||
|
||||
public void onDisconnected() {
|
||||
connected = false;
|
||||
}
|
||||
|
||||
|
||||
public void onPublish(UTF8Buffer topic, final Buffer payload, final Callback<Callback<byte[]>> onComplete) {
|
||||
getDispatchQueue().assertExecuting();
|
||||
receiveBufferRemaining -= payload.length();
|
||||
if( !receiveBufferFull && receiveBufferRemaining <=0 ) {
|
||||
receiveBufferFull = true;
|
||||
suspend();
|
||||
}
|
||||
deliverMessage(new Message(getDispatchQueue(), topic, payload, new Callback<Callback<byte[]>>() {
|
||||
public void onSuccess(Callback<byte[]> value) {
|
||||
processed();
|
||||
onComplete.onSuccess(value);
|
||||
}
|
||||
public void onFailure(Throwable value) {
|
||||
processed();
|
||||
onComplete.onFailure(value);
|
||||
}
|
||||
|
||||
private void processed() {
|
||||
getDispatchQueue().assertExecuting();
|
||||
receiveBufferRemaining += payload.length();
|
||||
if( receiveBufferFull && receiveBufferRemaining >0 ) {
|
||||
receiveBufferFull = false;
|
||||
resume();
|
||||
}
|
||||
}
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
public void onPublish(UTF8Buffer topic, Buffer payload, final Runnable onComplete) {
|
||||
onPublish(topic, payload, new Callback<Callback<byte[]>>() {
|
||||
public void onSuccess(Callback<byte[]> value) {
|
||||
onComplete.run();
|
||||
}
|
||||
public void onFailure(Throwable value) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onFailure(Throwable value) {
|
||||
getDispatchQueue().assertExecuting();
|
||||
ArrayList<Promise<?>> tmp = new ArrayList<Promise<?>>(receiveFutures);
|
||||
receiveFutures.clear();
|
||||
for (Promise<?> future : tmp) {
|
||||
future.onFailure(value);
|
||||
}
|
||||
connected = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void deliverMessage(Message msg) {
|
||||
if( receiveFutures.isEmpty() ) {
|
||||
receivedFrames.add(msg);
|
||||
} else {
|
||||
receiveFutures.removeFirst().onSuccess(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void putBackMessage(Message msg) {
|
||||
if( receiveFutures.isEmpty() ) {
|
||||
receivedFrames.addFirst(msg);
|
||||
} else {
|
||||
receiveFutures.removeFirst().onSuccess(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
public DispatchQueue getDispatchQueue() {
|
||||
return this.next.getDispatchQueue();
|
||||
}
|
||||
|
||||
|
||||
public Future<byte[]> connect() {
|
||||
final Promise<byte[]> future = new Promise<byte[]>();
|
||||
next.getDispatchQueue().execute(new Task() {
|
||||
public void run() {
|
||||
next.connect(future);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
public Future<Void> disconnect() {
|
||||
final Promise<Void> future = new Promise<Void>();
|
||||
next.getDispatchQueue().execute(new Task() {
|
||||
public void run() {
|
||||
next.disconnect(false, future);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
public Future<Void> kill() {
|
||||
final Promise<Void> future = new Promise<Void>();
|
||||
next.getDispatchQueue().execute(new Task() {
|
||||
public void run() {
|
||||
next.kill(future);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
public Future<byte[]> subscribe(final Topic[] topics) {
|
||||
final Promise<byte[]> future = new Promise<byte[]>();
|
||||
next.getDispatchQueue().execute(new Task() {
|
||||
public void run() {
|
||||
next.subscribe(topics, future);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
public Future<Void> unsubscribe(final String[] topics) {
|
||||
UTF8Buffer[] buffers = new UTF8Buffer[topics.length];
|
||||
for (int i = 0; i < buffers.length; i++) {
|
||||
buffers[i] = new UTF8Buffer(topics[i]);
|
||||
}
|
||||
return unsubscribe(buffers);
|
||||
}
|
||||
|
||||
public Future<Void> unsubscribe(final UTF8Buffer[] topics) {
|
||||
final Promise<Void> future = new Promise<Void>();
|
||||
next.getDispatchQueue().execute(new Task() {
|
||||
public void run() {
|
||||
next.unsubscribe(topics, future);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
public Future<byte[]> publish(final String topic, final byte[] payload, final QoS qos, final boolean retain) {
|
||||
return publish(utf8(topic), new Buffer(payload), qos, retain);
|
||||
}
|
||||
|
||||
public Future<byte[]> publish(final UTF8Buffer topic, final Buffer payload, final QoS qos, final boolean retain) {
|
||||
final Promise<byte[]> future = new Promise<byte[]>();
|
||||
next.getDispatchQueue().execute(new Task() {
|
||||
public void run() {
|
||||
next.publish(topic, payload, qos, retain, future);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
public Future<Message> receive() {
|
||||
final Promise<Message> future = new Promise<Message>();
|
||||
getDispatchQueue().execute(new Task(){
|
||||
public void run() {
|
||||
if( next.failure()!=null ) {
|
||||
future.onFailure(next.failure());
|
||||
} else {
|
||||
if( receivedFrames.isEmpty() ) {
|
||||
receiveFutures.add(future);
|
||||
} else {
|
||||
future.onSuccess(receivedFrames.removeFirst());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
public long getReceiveBuffer() {
|
||||
getDispatchQueue().assertExecuting();
|
||||
return receiveBuffer;
|
||||
}
|
||||
|
||||
public void setReceiveBuffer(long receiveBuffer) {
|
||||
getDispatchQueue().assertExecuting();
|
||||
long prev = this.receiveBuffer;
|
||||
this.receiveBuffer = receiveBuffer;
|
||||
long diff = prev-receiveBuffer;
|
||||
|
||||
receiveBufferRemaining -= diff;
|
||||
if( !receiveBufferFull && receiveBufferRemaining <=0 ) {
|
||||
receiveBufferFull = true;
|
||||
suspend();
|
||||
} else if( receiveBufferFull && receiveBufferRemaining >0 ) {
|
||||
receiveBufferFull = false;
|
||||
resume();
|
||||
}
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
next.resume();
|
||||
}
|
||||
|
||||
public void suspend() {
|
||||
next.suspend();
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* 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.client;
|
||||
|
||||
import org.fusesource.hawtbuf.Buffer;
|
||||
import org.fusesource.hawtbuf.UTF8Buffer;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
@Deprecated
|
||||
public interface Listener {
|
||||
public void onConnected();
|
||||
public void onDisconnected();
|
||||
public void onPublish(UTF8Buffer topic, Buffer body, Runnable ack);
|
||||
public void onFailure(Throwable value);
|
||||
}
|
||||
@@ -1,392 +0,0 @@
|
||||
/**
|
||||
* 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.client;
|
||||
|
||||
import org.fusesource.hawtbuf.UTF8Buffer;
|
||||
import org.fusesource.hawtdispatch.DispatchQueue;
|
||||
import org.fusesource.hawtdispatch.transport.*;
|
||||
import org.fusesource.mqtt.codec.CONNECT;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static org.fusesource.hawtbuf.Buffer.utf8;
|
||||
import static org.fusesource.hawtdispatch.Dispatch.createQueue;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class MQTT {
|
||||
|
||||
private static final long KEEP_ALIVE = Long.parseLong(System.getProperty("mqtt.thread.keep_alive", Integer.toString(1000)));
|
||||
private static final long STACK_SIZE = Long.parseLong(System.getProperty("mqtt.thread.stack_size", Integer.toString(1024*512)));
|
||||
private static ThreadPoolExecutor blockingThreadPool;
|
||||
|
||||
|
||||
public synchronized static ThreadPoolExecutor getBlockingThreadPool() {
|
||||
if( blockingThreadPool == null ) {
|
||||
blockingThreadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, KEEP_ALIVE, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(), new ThreadFactory() {
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread rc = new Thread(null, r, "MQTT Task", STACK_SIZE);
|
||||
rc.setDaemon(true);
|
||||
return rc;
|
||||
}
|
||||
}) {
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
// we don't ever shutdown since we are shared..
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Runnable> shutdownNow() {
|
||||
// we don't ever shutdown since we are shared..
|
||||
return Collections.emptyList();
|
||||
}
|
||||
};
|
||||
}
|
||||
return blockingThreadPool;
|
||||
}
|
||||
public synchronized static void setBlockingThreadPool(ThreadPoolExecutor pool) {
|
||||
blockingThreadPool = pool;
|
||||
}
|
||||
|
||||
private static final URI DEFAULT_HOST = createDefaultHost();
|
||||
private static URI createDefaultHost() {
|
||||
try {
|
||||
return new URI("tcp://127.0.0.1:1883");
|
||||
} catch (URISyntaxException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected URI host = DEFAULT_HOST;
|
||||
protected URI localAddress;
|
||||
protected SSLContext sslContext;
|
||||
protected DispatchQueue dispatchQueue;
|
||||
protected Executor blockingExecutor;
|
||||
protected int maxReadRate;
|
||||
protected int maxWriteRate;
|
||||
protected int trafficClass = TcpTransport.IPTOS_THROUGHPUT;
|
||||
protected int receiveBufferSize = 1024*64;
|
||||
protected int sendBufferSize = 1024*64;
|
||||
protected boolean useLocalHost = true;
|
||||
protected CONNECT connect = new CONNECT();
|
||||
|
||||
protected long reconnectDelay = 10;
|
||||
protected long reconnectDelayMax = 30*1000;
|
||||
protected double reconnectBackOffMultiplier = 2.0f;
|
||||
protected long reconnectAttemptsMax = -1;
|
||||
protected long connectAttemptsMax = -1;
|
||||
protected Tracer tracer = new Tracer();
|
||||
|
||||
public MQTT() {
|
||||
}
|
||||
public MQTT(MQTT other) {
|
||||
this.host = other.host;
|
||||
this.localAddress = other.localAddress;
|
||||
this.sslContext = other.sslContext;
|
||||
this.dispatchQueue = other.dispatchQueue;
|
||||
this.blockingExecutor = other.blockingExecutor;
|
||||
this.maxReadRate = other.maxReadRate;
|
||||
this.maxWriteRate = other.maxWriteRate;
|
||||
this.trafficClass = other.trafficClass;
|
||||
this.receiveBufferSize = other.receiveBufferSize;
|
||||
this.sendBufferSize = other.sendBufferSize;
|
||||
this.useLocalHost = other.useLocalHost;
|
||||
this.connect = new CONNECT(other.connect);
|
||||
this.reconnectDelay = other.reconnectDelay;
|
||||
this.reconnectDelayMax = other.reconnectDelayMax;
|
||||
this.reconnectBackOffMultiplier = other.reconnectBackOffMultiplier;
|
||||
this.reconnectAttemptsMax = other.reconnectAttemptsMax;
|
||||
this.connectAttemptsMax = other.connectAttemptsMax;
|
||||
this.tracer = other.tracer;
|
||||
}
|
||||
|
||||
public CallbackConnection callbackConnection() {
|
||||
if( !isCleanSession() && ( getClientId()==null || getClientId().length==0 )) {
|
||||
throw new IllegalArgumentException("The client id MUST be configured when clean session is set to false");
|
||||
}
|
||||
return new CallbackConnection(new MQTT(this));
|
||||
}
|
||||
public FutureConnection futureConnection() {
|
||||
return new FutureConnection(callbackConnection());
|
||||
}
|
||||
public BlockingConnection blockingConnection() {
|
||||
return new BlockingConnection(futureConnection());
|
||||
}
|
||||
|
||||
public UTF8Buffer getClientId() {
|
||||
return connect.clientId();
|
||||
}
|
||||
|
||||
public short getKeepAlive() {
|
||||
return connect.keepAlive();
|
||||
}
|
||||
|
||||
public UTF8Buffer getPassword() {
|
||||
return connect.password();
|
||||
}
|
||||
|
||||
public byte getType() {
|
||||
return connect.messageType();
|
||||
}
|
||||
|
||||
public UTF8Buffer getUserName() {
|
||||
return connect.userName();
|
||||
}
|
||||
|
||||
public UTF8Buffer getWillMessage() {
|
||||
return connect.willMessage();
|
||||
}
|
||||
|
||||
public QoS getWillQos() {
|
||||
return connect.willQos();
|
||||
}
|
||||
|
||||
public UTF8Buffer getWillTopic() {
|
||||
return connect.willTopic();
|
||||
}
|
||||
|
||||
public boolean isCleanSession() {
|
||||
return connect.cleanSession();
|
||||
}
|
||||
|
||||
public boolean isWillRetain() {
|
||||
return connect.willRetain();
|
||||
}
|
||||
|
||||
public void setCleanSession(boolean cleanSession) {
|
||||
connect.cleanSession(cleanSession);
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.setClientId(utf8(clientId));
|
||||
}
|
||||
public void setClientId(UTF8Buffer clientId) {
|
||||
connect.clientId(clientId);
|
||||
}
|
||||
|
||||
public void setKeepAlive(short keepAlive) {
|
||||
connect.keepAlive(keepAlive);
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.setPassword(utf8(password));
|
||||
}
|
||||
public void setPassword(UTF8Buffer password) {
|
||||
connect.password(password);
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.setUserName(utf8(userName));
|
||||
}
|
||||
public void setUserName(UTF8Buffer userName) {
|
||||
connect.userName(userName);
|
||||
}
|
||||
|
||||
public void setWillMessage(String willMessage) {
|
||||
connect.willMessage(utf8(willMessage));
|
||||
}
|
||||
public void setWillMessage(UTF8Buffer willMessage) {
|
||||
connect.willMessage(willMessage);
|
||||
}
|
||||
|
||||
public void setWillQos(QoS willQos) {
|
||||
connect.willQos(willQos);
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
if( "3.1".equals(version) ) {
|
||||
connect.version(3);
|
||||
} else if( "3.1.1".equals(version) ) {
|
||||
connect.version(4);
|
||||
}
|
||||
}
|
||||
public String getVersion() {
|
||||
switch(connect.version()) {
|
||||
case 3: return "3.1";
|
||||
case 4: return "3.1.1";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
public void setWillRetain(boolean willRetain) {
|
||||
connect.willRetain(willRetain);
|
||||
}
|
||||
|
||||
public void setWillTopic(String willTopic) {
|
||||
this.setWillTopic(utf8(willTopic));
|
||||
}
|
||||
public void setWillTopic(UTF8Buffer willTopic) {
|
||||
connect.willTopic(willTopic);
|
||||
}
|
||||
|
||||
public Executor getBlockingExecutor() {
|
||||
return blockingExecutor;
|
||||
}
|
||||
|
||||
public void setBlockingExecutor(Executor blockingExecutor) {
|
||||
this.blockingExecutor = blockingExecutor;
|
||||
}
|
||||
|
||||
public DispatchQueue getDispatchQueue() {
|
||||
return dispatchQueue;
|
||||
}
|
||||
|
||||
public void setDispatchQueue(DispatchQueue dispatchQueue) {
|
||||
this.dispatchQueue = dispatchQueue;
|
||||
}
|
||||
|
||||
public URI getLocalAddress() {
|
||||
return localAddress;
|
||||
}
|
||||
|
||||
public void setLocalAddress(String localAddress) throws URISyntaxException {
|
||||
this.setLocalAddress(new URI(localAddress));
|
||||
}
|
||||
public void setLocalAddress(URI localAddress) {
|
||||
this.localAddress = localAddress;
|
||||
}
|
||||
|
||||
public int getMaxReadRate() {
|
||||
return maxReadRate;
|
||||
}
|
||||
|
||||
public void setMaxReadRate(int maxReadRate) {
|
||||
this.maxReadRate = maxReadRate;
|
||||
}
|
||||
|
||||
public int getMaxWriteRate() {
|
||||
return maxWriteRate;
|
||||
}
|
||||
|
||||
public void setMaxWriteRate(int maxWriteRate) {
|
||||
this.maxWriteRate = maxWriteRate;
|
||||
}
|
||||
|
||||
public int getReceiveBufferSize() {
|
||||
return receiveBufferSize;
|
||||
}
|
||||
|
||||
public void setReceiveBufferSize(int receiveBufferSize) {
|
||||
this.receiveBufferSize = receiveBufferSize;
|
||||
}
|
||||
|
||||
public URI getHost() {
|
||||
return host;
|
||||
}
|
||||
public void setHost(String host, int port) throws URISyntaxException {
|
||||
this.setHost(new URI("tcp://"+host+":"+port));
|
||||
}
|
||||
public void setHost(String host) throws URISyntaxException {
|
||||
this.setHost(new URI(host));
|
||||
}
|
||||
public void setHost(URI host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public int getSendBufferSize() {
|
||||
return sendBufferSize;
|
||||
}
|
||||
|
||||
public void setSendBufferSize(int sendBufferSize) {
|
||||
this.sendBufferSize = sendBufferSize;
|
||||
}
|
||||
|
||||
public SSLContext getSslContext() {
|
||||
return sslContext;
|
||||
}
|
||||
|
||||
public void setSslContext(SSLContext sslContext) {
|
||||
this.sslContext = sslContext;
|
||||
}
|
||||
|
||||
public int getTrafficClass() {
|
||||
return trafficClass;
|
||||
}
|
||||
|
||||
public void setTrafficClass(int trafficClass) {
|
||||
this.trafficClass = trafficClass;
|
||||
}
|
||||
|
||||
public boolean isUseLocalHost() {
|
||||
return useLocalHost;
|
||||
}
|
||||
|
||||
public void setUseLocalHost(boolean useLocalHost) {
|
||||
this.useLocalHost = useLocalHost;
|
||||
}
|
||||
|
||||
public long getConnectAttemptsMax() {
|
||||
return connectAttemptsMax;
|
||||
}
|
||||
|
||||
public void setConnectAttemptsMax(long connectAttemptsMax) {
|
||||
this.connectAttemptsMax = connectAttemptsMax;
|
||||
}
|
||||
|
||||
public long getReconnectAttemptsMax() {
|
||||
return reconnectAttemptsMax;
|
||||
}
|
||||
|
||||
public void setReconnectAttemptsMax(long reconnectAttemptsMax) {
|
||||
this.reconnectAttemptsMax = reconnectAttemptsMax;
|
||||
}
|
||||
|
||||
public double getReconnectBackOffMultiplier() {
|
||||
return reconnectBackOffMultiplier;
|
||||
}
|
||||
|
||||
public void setReconnectBackOffMultiplier(double reconnectBackOffMultiplier) {
|
||||
this.reconnectBackOffMultiplier = reconnectBackOffMultiplier;
|
||||
}
|
||||
|
||||
public long getReconnectDelay() {
|
||||
return reconnectDelay;
|
||||
}
|
||||
|
||||
public void setReconnectDelay(long reconnectDelay) {
|
||||
this.reconnectDelay = reconnectDelay;
|
||||
}
|
||||
|
||||
public long getReconnectDelayMax() {
|
||||
return reconnectDelayMax;
|
||||
}
|
||||
|
||||
public void setReconnectDelayMax(long reconnectDelayMax) {
|
||||
this.reconnectDelayMax = reconnectDelayMax;
|
||||
}
|
||||
|
||||
public Tracer getTracer() {
|
||||
return tracer;
|
||||
}
|
||||
|
||||
public void setTracer(Tracer tracer) {
|
||||
this.tracer = tracer;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package org.fusesource.mqtt.client;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.fusesource.mqtt.codec.CONNACK;
|
||||
|
||||
public class MQTTException extends IOException {
|
||||
public final CONNACK connack;
|
||||
|
||||
public MQTTException(String msg, CONNACK connack) {
|
||||
super(msg);
|
||||
this.connack = connack;
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/**
|
||||
* 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.client;
|
||||
|
||||
import org.fusesource.hawtbuf.Buffer;
|
||||
import org.fusesource.hawtbuf.UTF8Buffer;
|
||||
import org.fusesource.hawtdispatch.DispatchQueue;
|
||||
import org.fusesource.hawtdispatch.Task;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class Message {
|
||||
|
||||
private final UTF8Buffer topic;
|
||||
private final Buffer payload;
|
||||
private Callback<Callback<byte[]>> onComplete;
|
||||
private final DispatchQueue queue;
|
||||
boolean blocking = false;
|
||||
|
||||
public Message(DispatchQueue queue, UTF8Buffer topic, Buffer payload, Callback<Callback<byte[]>> onComplete) {
|
||||
this.queue = queue;
|
||||
this.payload = payload;
|
||||
this.topic = topic;
|
||||
this.onComplete = onComplete;
|
||||
}
|
||||
|
||||
public byte[] getPayload() {
|
||||
return payload.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Using getPayloadBuffer() is lower overhead version of getPayload()
|
||||
* since it avoids a byte array copy.
|
||||
* @return
|
||||
*/
|
||||
public Buffer getPayloadBuffer() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
public String getTopic() {
|
||||
return topic.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Using getTopicBuffer is lower overhead version of getTopic()
|
||||
* since it avoid doing UTF-8 decode.
|
||||
* @return
|
||||
*/
|
||||
public UTF8Buffer getTopicBuffer() {
|
||||
return topic;
|
||||
}
|
||||
|
||||
public void ack() {
|
||||
if( blocking ) {
|
||||
final Promise<byte[]> future = new Promise<byte[]>();
|
||||
ack(future);
|
||||
try {
|
||||
future.await();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
ack(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void ack(final Callback<byte[]> onAcked) {
|
||||
if(onComplete!=null) {
|
||||
queue.execute(new Task() {
|
||||
Callback<Callback<byte[]>> onCompleteCopy = onComplete;
|
||||
@Override
|
||||
public void run() {
|
||||
onCompleteCopy.onSuccess(onAcked);
|
||||
}
|
||||
});
|
||||
onComplete = null;
|
||||
} else {
|
||||
if( onAcked!=null ) {
|
||||
onAcked.onSuccess(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* 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.client;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Function Result that carries one value.
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class NoopCallback<T> implements Callback<T> {
|
||||
|
||||
public final Callback<T> next;
|
||||
|
||||
public NoopCallback(Callback<T> next) {
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
public void onSuccess(T value) {
|
||||
if( next!=null ) {
|
||||
next.onSuccess(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void onFailure(Throwable value) {
|
||||
if( next!=null ) {
|
||||
next.onFailure(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/**
|
||||
* 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.client;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class Promise<T> implements Callback<T>, Future<T> {
|
||||
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
private Callback<T> next;
|
||||
private Throwable error;
|
||||
private T value;
|
||||
|
||||
public void onFailure(Throwable value) {
|
||||
Callback<T> callback = null;
|
||||
synchronized(this) {
|
||||
error = value;
|
||||
latch.countDown();
|
||||
callback = next;
|
||||
}
|
||||
if( callback!=null ) {
|
||||
callback.onFailure(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void onSuccess(T value) {
|
||||
Callback<T> callback = null;
|
||||
synchronized(this) {
|
||||
this.value = value;
|
||||
latch.countDown();
|
||||
callback = next;
|
||||
}
|
||||
if( callback!=null ) {
|
||||
callback.onSuccess(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void then(Callback<T> callback) {
|
||||
boolean fire = false;
|
||||
synchronized(this) {
|
||||
next = callback;
|
||||
if( latch.getCount() == 0 ) {
|
||||
fire = true;
|
||||
}
|
||||
}
|
||||
if( fire ) {
|
||||
if( error!=null ) {
|
||||
callback.onFailure(error);
|
||||
} else {
|
||||
callback.onSuccess(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public T await(long amount, TimeUnit unit) throws Exception {
|
||||
if( latch.await(amount, unit) ) {
|
||||
return get();
|
||||
} else {
|
||||
throw new TimeoutException();
|
||||
}
|
||||
}
|
||||
|
||||
public T await() throws Exception {
|
||||
latch.await();
|
||||
return get();
|
||||
}
|
||||
|
||||
private T get() throws Exception {
|
||||
Throwable e = error;
|
||||
if( e !=null ) {
|
||||
if( e instanceof RuntimeException ) {
|
||||
throw (RuntimeException) e;
|
||||
} else if( e instanceof Exception) {
|
||||
throw (Exception) e;
|
||||
} else if( e instanceof Error) {
|
||||
throw (Error) e;
|
||||
} else {
|
||||
// don't expect to hit this case.
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* 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.client;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Function Result that carries one value.
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class ProxyCallback<T> implements Callback<T> {
|
||||
|
||||
public final Callback<T> next;
|
||||
|
||||
public ProxyCallback(Callback<T> next) {
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
public void onSuccess(T value) {
|
||||
if( next!=null ) {
|
||||
next.onSuccess(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void onFailure(Throwable value) {
|
||||
if( next!=null ) {
|
||||
next.onFailure(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* 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.client;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public enum QoS {
|
||||
AT_MOST_ONCE,
|
||||
AT_LEAST_ONCE,
|
||||
EXACTLY_ONCE
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/**
|
||||
* 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.client;
|
||||
|
||||
import org.fusesource.hawtbuf.UTF8Buffer;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class Topic {
|
||||
|
||||
private final UTF8Buffer name;
|
||||
private final QoS qos;
|
||||
|
||||
public Topic(String name, QoS qos) {
|
||||
this(new UTF8Buffer(name), qos);
|
||||
}
|
||||
|
||||
public Topic(UTF8Buffer name, QoS qos) {
|
||||
this.name = name;
|
||||
this.qos = qos;
|
||||
}
|
||||
|
||||
public UTF8Buffer name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public QoS qos() {
|
||||
return qos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null) return false;
|
||||
if (o.getClass() != this.getClass()) return false;
|
||||
if (this == o) return true;
|
||||
|
||||
Topic topic = (Topic) o;
|
||||
|
||||
if (name != null ? !name.equals(topic.name) : topic.name != null) return false;
|
||||
if (qos != topic.qos) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = name != null ? name.hashCode() : 0;
|
||||
result = 31 * result + (qos != null ? qos.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{ name=" + name +
|
||||
", qos=" + qos +
|
||||
" }";
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/**
|
||||
* 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.client;
|
||||
|
||||
import org.fusesource.mqtt.codec.MQTTFrame;
|
||||
|
||||
/**
|
||||
* A subclass of this can be configured on an MQTT connection to
|
||||
* get more insight into what it's doing.
|
||||
*/
|
||||
public class Tracer {
|
||||
|
||||
/**
|
||||
* Override to log/capture debug level messages
|
||||
* @param message
|
||||
* @param args
|
||||
*/
|
||||
public void debug(String message, Object...args) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a MQTTFrame sent to the remote peer.
|
||||
* @param frame
|
||||
*/
|
||||
public void onSend(MQTTFrame frame) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a MQTTFrame is received from the remote peer.
|
||||
* @param frame
|
||||
*/
|
||||
public void onReceive(MQTTFrame frame) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/**
|
||||
* 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 org.fusesource.hawtbuf.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
import static org.fusesource.mqtt.codec.MessageSupport.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class CONNACK implements Message {
|
||||
|
||||
public static final byte TYPE = 2;
|
||||
|
||||
public byte[] payload;
|
||||
|
||||
public static enum Code {
|
||||
CONNECTION_ACCEPTED,
|
||||
CONNECTION_REFUSED_UNACCEPTED_PROTOCOL_VERSION,
|
||||
CONNECTION_REFUSED_IDENTIFIER_REJECTED,
|
||||
CONNECTION_REFUSED_SERVER_UNAVAILABLE,
|
||||
CONNECTION_REFUSED_BAD_USERNAME_OR_PASSWORD,
|
||||
CONNECTION_REFUSED_NOT_AUTHORIZED;
|
||||
}
|
||||
|
||||
private Code code = Code.CONNECTION_ACCEPTED;
|
||||
|
||||
|
||||
|
||||
public byte messageType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public CONNACK decode(MQTTFrame frame) throws ProtocolException {
|
||||
assert(frame.buffers.length == 1);
|
||||
DataByteArrayInputStream is = new DataByteArrayInputStream(frame.buffers[0]);
|
||||
is.skip(1);
|
||||
byte c = is.readByte();
|
||||
if( c >= Code.values().length ) {
|
||||
throw new ProtocolException("Invalid CONNACK encoding");
|
||||
}
|
||||
code = Code.values()[c];
|
||||
payload = new byte[is.available()];
|
||||
try {
|
||||
is.read(payload);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MQTTFrame encode() {
|
||||
try {
|
||||
DataByteArrayOutputStream os = new DataByteArrayOutputStream(2);
|
||||
os.writeByte(0);
|
||||
os.writeByte(code.ordinal());
|
||||
|
||||
MQTTFrame frame = new MQTTFrame();
|
||||
frame.commandType(TYPE);
|
||||
return frame.buffer(os.toBuffer());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("The impossible happened");
|
||||
}
|
||||
}
|
||||
|
||||
public Code code() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public CONNACK code(Code code) {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CONNACK{" +
|
||||
"code=" + code +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,284 +0,0 @@
|
||||
/**
|
||||
* 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 org.fusesource.mqtt.client.QoS;
|
||||
import org.fusesource.hawtbuf.DataByteArrayInputStream;
|
||||
import org.fusesource.hawtbuf.DataByteArrayOutputStream;
|
||||
import org.fusesource.hawtbuf.UTF8Buffer;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class CONNECT implements MessageSupport.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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* 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.net.ProtocolException;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class DISCONNECT extends MessageSupport.EmptyBase implements MessageSupport.Message {
|
||||
|
||||
public static final byte TYPE = 14;
|
||||
|
||||
public byte messageType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DISCONNECT decode(MQTTFrame frame) throws ProtocolException {
|
||||
return (DISCONNECT) super.decode(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DISCONNECT";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
/**
|
||||
* 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 org.fusesource.mqtt.client.QoS;
|
||||
import org.fusesource.hawtbuf.Buffer;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class MQTTFrame extends MessageSupport.HeaderBase {
|
||||
|
||||
private static final Buffer[] NO_BUFFERS = new Buffer[0];
|
||||
|
||||
public Buffer[] buffers = NO_BUFFERS;
|
||||
|
||||
public MQTTFrame() {
|
||||
}
|
||||
public MQTTFrame( Buffer buffer) {
|
||||
this(new Buffer[]{buffer});
|
||||
}
|
||||
public MQTTFrame( Buffer[] buffers) {
|
||||
this.buffers = buffers;
|
||||
}
|
||||
|
||||
public Buffer[] buffers() {
|
||||
return buffers;
|
||||
}
|
||||
public MQTTFrame buffers(Buffer...buffers) {
|
||||
this.buffers = buffers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MQTTFrame buffer(Buffer buffer) {
|
||||
this.buffers = new Buffer[]{buffer};
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte header() {
|
||||
return super.header();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MQTTFrame header(byte header) {
|
||||
return (MQTTFrame)super.header(header);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte messageType() {
|
||||
return super.messageType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MQTTFrame commandType(int type) {
|
||||
return (MQTTFrame)super.commandType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dup() {
|
||||
return super.dup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MQTTFrame dup(boolean dup) {
|
||||
return (MQTTFrame) super.dup(dup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QoS qos() {
|
||||
return super.qos();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MQTTFrame qos(QoS qos) {
|
||||
return (MQTTFrame) super.qos(qos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retain() {
|
||||
return super.retain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MQTTFrame retain(boolean retain) {
|
||||
return (MQTTFrame) super.retain(retain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String type = "unknown";
|
||||
switch(messageType()) {
|
||||
case CONNECT.TYPE:
|
||||
type = "CONNECT";
|
||||
break;
|
||||
case CONNACK.TYPE:
|
||||
type = "CONNACK";
|
||||
break;
|
||||
case DISCONNECT.TYPE:
|
||||
type = "DISCONNECT";
|
||||
break;
|
||||
case PINGREQ.TYPE:
|
||||
type = "PINGREQ";
|
||||
break;
|
||||
case PINGRESP.TYPE:
|
||||
type = "PINGRESP";
|
||||
break;
|
||||
case SUBSCRIBE.TYPE:
|
||||
type = "SUBSCRIBE";
|
||||
break;
|
||||
case UNSUBSCRIBE.TYPE:
|
||||
type = "UNSUBSCRIBE";
|
||||
break;
|
||||
case UNSUBACK.TYPE:
|
||||
type = "UNSUBACK";
|
||||
break;
|
||||
case PUBLISH.TYPE:
|
||||
type = "PUBLISH";
|
||||
break;
|
||||
case SUBACK.TYPE:
|
||||
type = "SUBACK";
|
||||
break;
|
||||
case PUBACK.TYPE:
|
||||
type = "PUBACK";
|
||||
break;
|
||||
case PUBREC.TYPE:
|
||||
type = "PUBREC";
|
||||
break;
|
||||
case PUBREL.TYPE:
|
||||
type = "PUBREL";
|
||||
break;
|
||||
case PUBCOMP.TYPE:
|
||||
type = "PUBCOMP";
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
return "MQTTFrame { type: "+type+", qos: "+qos()+", dup:"+dup()+" }";
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
/**
|
||||
* 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 org.fusesource.hawtbuf.Buffer;
|
||||
import org.fusesource.hawtdispatch.transport.AbstractProtocolCodec;
|
||||
import org.fusesource.hawtdispatch.util.BufferPools;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class MQTTProtocolCodec extends AbstractProtocolCodec {
|
||||
|
||||
private static final BufferPools BUFFER_POOLS = new BufferPools();
|
||||
|
||||
private int maxMessageLength = 1024*1024*100;
|
||||
|
||||
public MQTTProtocolCodec() {
|
||||
this.bufferPools = BUFFER_POOLS;
|
||||
}
|
||||
|
||||
public int getMaxMessageLength() {
|
||||
return maxMessageLength;
|
||||
}
|
||||
|
||||
public void setMaxMessageLength(int maxMessageLength) {
|
||||
this.maxMessageLength = maxMessageLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(Object value) throws IOException {
|
||||
MQTTFrame frame = (MQTTFrame) value;
|
||||
nextWriteBuffer.write(frame.header());
|
||||
|
||||
int remaining = 0;
|
||||
for(Buffer buffer : frame.buffers) {
|
||||
remaining += buffer.length;
|
||||
}
|
||||
do {
|
||||
byte digit = (byte) (remaining & 0x7F);
|
||||
remaining >>>= 7;
|
||||
if (remaining > 0) {
|
||||
digit |= 0x80;
|
||||
}
|
||||
nextWriteBuffer.write(digit);
|
||||
} while (remaining > 0);
|
||||
for(Buffer buffer : frame.buffers) {
|
||||
nextWriteBuffer.write(buffer.data, buffer.offset, buffer.length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Action initialDecodeAction() {
|
||||
return readHeader;
|
||||
}
|
||||
|
||||
private final Action readHeader = new Action() {
|
||||
public MQTTFrame apply() throws IOException {
|
||||
int length = readLength();
|
||||
if( length >= 0 ) {
|
||||
if( length > maxMessageLength) {
|
||||
throw new IOException("The maximum message length was exceeded");
|
||||
}
|
||||
byte header = readBuffer.get(readStart);
|
||||
readStart = readEnd;
|
||||
if( length > 0 ) {
|
||||
nextDecodeAction = readBody(header, length);
|
||||
} else {
|
||||
return new MQTTFrame().header(header);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
private int readLength() throws IOException {
|
||||
readEnd = readStart+2; // Header is at least 2 bytes..
|
||||
int limit = readBuffer.position();
|
||||
int length = 0;
|
||||
int multiplier = 1;
|
||||
byte digit;
|
||||
|
||||
while (readEnd-1 < limit) {
|
||||
// last byte is part of the encoded length..
|
||||
digit = readBuffer.get(readEnd-1);
|
||||
length += (digit & 0x7F) * multiplier;
|
||||
if( (digit & 0x80) == 0 ) {
|
||||
return length;
|
||||
}
|
||||
|
||||
// length extends out one more byte..
|
||||
multiplier <<= 7;
|
||||
readEnd++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Action readBody(final byte header, final int length) {
|
||||
return new Action() {
|
||||
public MQTTFrame apply() throws IOException {
|
||||
int limit = readBuffer.position();
|
||||
if ((limit - readStart) < length) {
|
||||
readEnd = limit;
|
||||
return null;
|
||||
} else {
|
||||
Buffer body = new Buffer(readBuffer.array(), readStart, length);
|
||||
readEnd = readStart = readStart + length;
|
||||
nextDecodeAction = readHeader;
|
||||
return new MQTTFrame(body).header(header);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
/**
|
||||
* 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 org.fusesource.mqtt.client.QoS;
|
||||
import org.fusesource.hawtbuf.Buffer;
|
||||
import org.fusesource.hawtbuf.DataByteArrayInputStream;
|
||||
import org.fusesource.hawtbuf.DataByteArrayOutputStream;
|
||||
import org.fusesource.hawtbuf.UTF8Buffer;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public final class MessageSupport {
|
||||
|
||||
private MessageSupport() throws InstantiationException {
|
||||
throw new InstantiationException("This class is not for instantiation");
|
||||
}
|
||||
|
||||
/**
|
||||
* All command objects implement this interface.
|
||||
*/
|
||||
static public interface Message {
|
||||
public byte messageType();
|
||||
public Message decode(MQTTFrame frame) throws ProtocolException;
|
||||
public MQTTFrame encode();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* All command objects that can get acked implement this interface.
|
||||
*/
|
||||
static public interface Acked extends Message {
|
||||
public boolean dup();
|
||||
public Acked dup(boolean dup);
|
||||
public QoS qos();
|
||||
public short messageId();
|
||||
public Acked messageId(short messageId);
|
||||
}
|
||||
|
||||
static protected UTF8Buffer readUTF(DataByteArrayInputStream is) throws ProtocolException {
|
||||
int size = is.readUnsignedShort();
|
||||
Buffer buffer = is.readBuffer(size);
|
||||
if (buffer == null || buffer.length != size) {
|
||||
throw new ProtocolException("Invalid message encoding");
|
||||
}
|
||||
return buffer.utf8();
|
||||
}
|
||||
|
||||
static protected void writeUTF(DataByteArrayOutputStream os, Buffer buffer) throws IOException {
|
||||
os.writeShort(buffer.length);
|
||||
os.write(buffer);
|
||||
}
|
||||
|
||||
static abstract public class AckBase {
|
||||
|
||||
private short messageId;
|
||||
public byte[] payload;
|
||||
|
||||
abstract byte messageType();
|
||||
|
||||
protected AckBase decode(MQTTFrame frame) throws ProtocolException {
|
||||
assert(frame.buffers.length == 1);
|
||||
DataByteArrayInputStream is = new DataByteArrayInputStream(frame.buffers[0]);
|
||||
messageId = is.readShort();
|
||||
payload = new byte[is.available()];
|
||||
try {
|
||||
is.read(payload);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MQTTFrame encode() {
|
||||
try {
|
||||
DataByteArrayOutputStream os = new DataByteArrayOutputStream(2);
|
||||
os.writeShort(messageId);
|
||||
|
||||
MQTTFrame frame = new MQTTFrame();
|
||||
frame.commandType(messageType());
|
||||
return frame.buffer(os.toBuffer());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("The impossible happened");
|
||||
}
|
||||
}
|
||||
|
||||
public short messageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
protected AckBase messageId(short messageId) {
|
||||
this.messageId = messageId;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName()+"{" +
|
||||
"messageId=" + messageId +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
static abstract public class EmptyBase {
|
||||
abstract byte messageType();
|
||||
|
||||
protected EmptyBase decode(MQTTFrame frame) throws ProtocolException {
|
||||
return this;
|
||||
}
|
||||
public MQTTFrame encode() {
|
||||
return new MQTTFrame().commandType(messageType());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
static public class HeaderBase {
|
||||
|
||||
protected byte header;
|
||||
|
||||
protected byte header() {
|
||||
return header;
|
||||
}
|
||||
protected HeaderBase header(byte header) {
|
||||
this.header = header;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected byte messageType() {
|
||||
return (byte) ((header & 0xF0) >>> 4);
|
||||
}
|
||||
protected HeaderBase commandType(int type) {
|
||||
this.header &= 0x0F;
|
||||
this.header |= (type << 4) & 0xF0;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected QoS qos() {
|
||||
return QoS.values()[((header & 0x06) >>> 1)];
|
||||
}
|
||||
protected HeaderBase qos(QoS qos) {
|
||||
this.header &= 0xF9;
|
||||
this.header |= (qos.ordinal() << 1) & 0x06;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected boolean dup() {
|
||||
return (header & 0x08) > 0;
|
||||
}
|
||||
protected HeaderBase dup(boolean dup) {
|
||||
if(dup) {
|
||||
this.header |= 0x08;
|
||||
} else {
|
||||
this.header &= 0xF7;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
protected boolean retain() {
|
||||
return (header & 0x01) > 0;
|
||||
}
|
||||
protected HeaderBase retain(boolean retain) {
|
||||
if(retain) {
|
||||
this.header |= 0x01;
|
||||
} else {
|
||||
this.header &= 0xFE;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/**
|
||||
* 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.net.ProtocolException;
|
||||
import static org.fusesource.mqtt.codec.MessageSupport.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class PINGREQ extends MessageSupport.EmptyBase implements Message {
|
||||
|
||||
public static final byte TYPE = 12;
|
||||
|
||||
public byte messageType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PINGREQ decode(MQTTFrame frame) throws ProtocolException {
|
||||
return (PINGREQ) super.decode(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PINGREQ";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* 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.net.ProtocolException;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class PINGRESP extends MessageSupport.EmptyBase implements MessageSupport.Message {
|
||||
|
||||
public static final byte TYPE = 13;
|
||||
|
||||
public byte messageType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PINGRESP decode(MQTTFrame frame) throws ProtocolException {
|
||||
return (PINGRESP) super.decode(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PINGRESP";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/**
|
||||
* 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.net.ProtocolException;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class PUBACK extends MessageSupport.AckBase implements MessageSupport.Message {
|
||||
|
||||
public static final byte TYPE = 4;
|
||||
|
||||
public byte messageType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PUBACK decode(MQTTFrame frame) throws ProtocolException {
|
||||
return (PUBACK) super.decode(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PUBACK messageId(short messageId) {
|
||||
return (PUBACK) super.messageId(messageId);
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* 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.net.ProtocolException;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class PUBCOMP extends MessageSupport.AckBase implements MessageSupport.Message {
|
||||
|
||||
public static final byte TYPE = 7;
|
||||
|
||||
public byte messageType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PUBCOMP decode(MQTTFrame frame) throws ProtocolException {
|
||||
return (PUBCOMP) super.decode(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PUBCOMP messageId(short messageId) {
|
||||
return (PUBCOMP) super.messageId(messageId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
/**
|
||||
* 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 org.fusesource.mqtt.client.QoS;
|
||||
import org.fusesource.hawtbuf.Buffer;
|
||||
import org.fusesource.hawtbuf.DataByteArrayInputStream;
|
||||
import org.fusesource.hawtbuf.DataByteArrayOutputStream;
|
||||
import org.fusesource.hawtbuf.UTF8Buffer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class PUBLISH extends MessageSupport.HeaderBase implements MessageSupport.Message, MessageSupport.Acked {
|
||||
|
||||
public static final byte TYPE = 3;
|
||||
|
||||
private UTF8Buffer topicName;
|
||||
private short messageId;
|
||||
private Buffer payload;
|
||||
|
||||
public PUBLISH() {
|
||||
qos(QoS.AT_LEAST_ONCE);
|
||||
}
|
||||
|
||||
public byte messageType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public PUBLISH decode(MQTTFrame frame) throws ProtocolException {
|
||||
assert(frame.buffers.length == 1);
|
||||
header(frame.header());
|
||||
|
||||
DataByteArrayInputStream is = new DataByteArrayInputStream(frame.buffers[0]);
|
||||
topicName = MessageSupport.readUTF(is);
|
||||
|
||||
QoS qos = qos();
|
||||
if(qos != QoS.AT_MOST_ONCE) {
|
||||
messageId = is.readShort();
|
||||
}
|
||||
payload = is.readBuffer(is.available());
|
||||
if( payload == null ) {
|
||||
payload = new Buffer(0);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MQTTFrame encode() {
|
||||
try {
|
||||
DataByteArrayOutputStream os = new DataByteArrayOutputStream();
|
||||
MessageSupport.writeUTF(os, topicName);
|
||||
QoS qos = qos();
|
||||
if(qos != QoS.AT_MOST_ONCE) {
|
||||
os.writeShort(messageId);
|
||||
}
|
||||
MQTTFrame frame = new MQTTFrame();
|
||||
frame.header(header());
|
||||
frame.commandType(TYPE);
|
||||
if(payload!=null && payload.length!=0) {
|
||||
os.write(payload);
|
||||
}
|
||||
frame.buffer(os.toBuffer());
|
||||
return frame;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("The impossible happened");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dup() {
|
||||
return super.dup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PUBLISH dup(boolean dup) {
|
||||
return (PUBLISH) super.dup(dup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QoS qos() {
|
||||
return super.qos();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PUBLISH qos(QoS qos) {
|
||||
return (PUBLISH) super.qos(qos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retain() {
|
||||
return super.retain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PUBLISH retain(boolean retain) {
|
||||
return (PUBLISH) super.retain(retain);
|
||||
}
|
||||
|
||||
public short messageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public PUBLISH messageId(short messageId) {
|
||||
this.messageId = messageId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Buffer payload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
public PUBLISH payload(Buffer payload) {
|
||||
this.payload = payload;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UTF8Buffer topicName() {
|
||||
return topicName;
|
||||
}
|
||||
|
||||
public PUBLISH topicName(UTF8Buffer topicName) {
|
||||
this.topicName = topicName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PUBLISH{" +
|
||||
"dup=" + dup() +
|
||||
", qos=" + qos() +
|
||||
", retain=" + retain() +
|
||||
", messageId=" + messageId +
|
||||
", topicName=" + topicName +
|
||||
", payload=" + payload +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* 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.net.ProtocolException;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class PUBREC extends MessageSupport.AckBase implements MessageSupport.Message {
|
||||
|
||||
public static final byte TYPE = 5;
|
||||
|
||||
public byte messageType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PUBREC decode(MQTTFrame frame) throws ProtocolException {
|
||||
return (PUBREC) super.decode(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PUBREC messageId(short messageId) {
|
||||
return (PUBREC) super.messageId(messageId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/**
|
||||
* 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 org.fusesource.mqtt.client.QoS;
|
||||
import org.fusesource.hawtbuf.DataByteArrayInputStream;
|
||||
import org.fusesource.hawtbuf.DataByteArrayOutputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class PUBREL extends MessageSupport.HeaderBase implements MessageSupport.Message, MessageSupport.Acked {
|
||||
|
||||
public static final byte TYPE = 6;
|
||||
|
||||
private short messageId;
|
||||
|
||||
public byte messageType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public PUBREL() {
|
||||
qos(QoS.AT_LEAST_ONCE);
|
||||
}
|
||||
|
||||
public PUBREL decode(MQTTFrame frame) throws ProtocolException {
|
||||
assert(frame.buffers.length == 1);
|
||||
header(frame.header());
|
||||
DataByteArrayInputStream is = new DataByteArrayInputStream(frame.buffers[0]);
|
||||
messageId = is.readShort();
|
||||
return this;
|
||||
}
|
||||
|
||||
public MQTTFrame encode() {
|
||||
try {
|
||||
DataByteArrayOutputStream os = new DataByteArrayOutputStream(2);
|
||||
os.writeShort(messageId);
|
||||
|
||||
MQTTFrame frame = new MQTTFrame();
|
||||
frame.header(header());
|
||||
frame.commandType(TYPE);
|
||||
return frame.buffer(os.toBuffer());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("The impossible happened");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean dup() {
|
||||
return super.dup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PUBREL dup(boolean dup) {
|
||||
return (PUBREL) super.dup(dup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QoS qos() {
|
||||
return super.qos();
|
||||
}
|
||||
|
||||
public short messageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public PUBREL messageId(short messageId) {
|
||||
this.messageId = messageId;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PUBREL{" +
|
||||
"dup=" + dup() +
|
||||
", qos=" + qos() +
|
||||
", messageId=" + messageId +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/**
|
||||
* 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 org.fusesource.hawtbuf.DataByteArrayInputStream;
|
||||
import org.fusesource.hawtbuf.DataByteArrayOutputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
import java.util.Arrays;
|
||||
import static org.fusesource.mqtt.codec.MessageSupport.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class SUBACK implements Message {
|
||||
|
||||
public static final byte[] NO_GRANTED_QOS = new byte[0];
|
||||
public static final byte TYPE = 9;
|
||||
|
||||
private short messageId;
|
||||
private byte[] grantedQos = NO_GRANTED_QOS;
|
||||
|
||||
public byte messageType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public SUBACK decode(MQTTFrame frame) throws ProtocolException {
|
||||
assert(frame.buffers.length == 1);
|
||||
DataByteArrayInputStream is = new DataByteArrayInputStream(frame.buffers[0]);
|
||||
messageId = is.readShort();
|
||||
grantedQos = is.readBuffer(is.available()).toByteArray();
|
||||
return this;
|
||||
}
|
||||
|
||||
public MQTTFrame encode() {
|
||||
try {
|
||||
DataByteArrayOutputStream os = new DataByteArrayOutputStream(2+grantedQos.length);
|
||||
os.writeShort(messageId);
|
||||
os.write(grantedQos);
|
||||
|
||||
MQTTFrame frame = new MQTTFrame();
|
||||
frame.commandType(TYPE);
|
||||
return frame.buffer(os.toBuffer());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("The impossible happened");
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] grantedQos() {
|
||||
return grantedQos;
|
||||
}
|
||||
|
||||
public SUBACK grantedQos(byte[] grantedQos) {
|
||||
this.grantedQos = grantedQos;
|
||||
return this;
|
||||
}
|
||||
|
||||
public short messageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public SUBACK messageId(short messageId) {
|
||||
this.messageId = messageId;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SUBACK{" +
|
||||
"grantedQos=" + Arrays.toString(grantedQos) +
|
||||
", messageId=" +messageId +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/**
|
||||
* 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 org.fusesource.mqtt.client.QoS;
|
||||
import org.fusesource.mqtt.client.Topic;
|
||||
import org.fusesource.hawtbuf.DataByteArrayInputStream;
|
||||
import org.fusesource.hawtbuf.DataByteArrayOutputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class SUBSCRIBE extends MessageSupport.HeaderBase implements MessageSupport.Message, MessageSupport.Acked {
|
||||
|
||||
public static final byte TYPE = 8;
|
||||
public static final Topic[] NO_TOPICS = new Topic[0];
|
||||
|
||||
private short messageId;
|
||||
private Topic[] topics = NO_TOPICS;
|
||||
|
||||
public SUBSCRIBE() {
|
||||
qos(QoS.AT_LEAST_ONCE);
|
||||
}
|
||||
|
||||
public byte messageType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public SUBSCRIBE decode(MQTTFrame frame) throws ProtocolException {
|
||||
assert(frame.buffers.length == 1);
|
||||
header(frame.header());
|
||||
|
||||
DataByteArrayInputStream is = new DataByteArrayInputStream(frame.buffers[0]);
|
||||
QoS qos = qos();
|
||||
if(qos != QoS.AT_MOST_ONCE) {
|
||||
messageId = is.readShort();
|
||||
}
|
||||
ArrayList<Topic> list = new ArrayList<Topic>();
|
||||
while(is.available() > 0) {
|
||||
Topic topic = new Topic(MessageSupport.readUTF(is), QoS.values()[is.readByte()]);
|
||||
list.add(topic);
|
||||
}
|
||||
topics = list.toArray(new Topic[list.size()]);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MQTTFrame encode() {
|
||||
try {
|
||||
DataByteArrayOutputStream os = new DataByteArrayOutputStream();
|
||||
QoS qos = qos();
|
||||
if(qos != QoS.AT_MOST_ONCE) {
|
||||
os.writeShort(messageId);
|
||||
}
|
||||
for(Topic topic: topics) {
|
||||
MessageSupport.writeUTF(os, topic.name());
|
||||
os.writeByte(topic.qos().ordinal());
|
||||
}
|
||||
|
||||
MQTTFrame frame = new MQTTFrame();
|
||||
frame.header(header());
|
||||
frame.commandType(TYPE);
|
||||
return frame.buffer(os.toBuffer());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("The impossible happened");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dup() {
|
||||
return super.dup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SUBSCRIBE dup(boolean dup) {
|
||||
return (SUBSCRIBE) super.dup(dup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QoS qos() {
|
||||
return super.qos();
|
||||
}
|
||||
|
||||
public short messageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public SUBSCRIBE messageId(short messageId) {
|
||||
this.messageId = messageId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Topic[] topics() {
|
||||
return topics;
|
||||
}
|
||||
|
||||
public SUBSCRIBE topics(Topic[] topics) {
|
||||
this.topics = topics;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SUBSCRIBE{" +
|
||||
"dup=" + dup() +
|
||||
", qos=" + qos() +
|
||||
", messageId=" + messageId +
|
||||
", topics=" + (topics == null ? null : Arrays.asList(topics)) +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
* 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.net.ProtocolException;
|
||||
|
||||
import static org.fusesource.mqtt.codec.MessageSupport.AckBase;
|
||||
import static org.fusesource.mqtt.codec.MessageSupport.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class UNSUBACK extends AckBase implements Message {
|
||||
|
||||
public static final byte TYPE = 11;
|
||||
|
||||
public byte messageType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UNSUBACK decode(MQTTFrame frame) throws ProtocolException {
|
||||
return (UNSUBACK) super.decode(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UNSUBACK messageId(short messageId) {
|
||||
return (UNSUBACK) super.messageId(messageId);
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
/**
|
||||
* 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 org.fusesource.mqtt.client.QoS;
|
||||
import org.fusesource.hawtbuf.DataByteArrayInputStream;
|
||||
import org.fusesource.hawtbuf.DataByteArrayOutputStream;
|
||||
import org.fusesource.hawtbuf.UTF8Buffer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class UNSUBSCRIBE extends MessageSupport.HeaderBase implements MessageSupport.Message, MessageSupport.Acked {
|
||||
|
||||
public static final byte TYPE = 10;
|
||||
public static final UTF8Buffer[] NO_TOPICS = new UTF8Buffer[0];
|
||||
|
||||
private short messageId;
|
||||
private UTF8Buffer[] topics = NO_TOPICS;
|
||||
|
||||
public UNSUBSCRIBE() {
|
||||
qos(QoS.AT_LEAST_ONCE);
|
||||
}
|
||||
|
||||
public byte messageType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public UNSUBSCRIBE decode(MQTTFrame frame) throws ProtocolException {
|
||||
assert(frame.buffers.length == 1);
|
||||
header(frame.header());
|
||||
|
||||
DataByteArrayInputStream is = new DataByteArrayInputStream(frame.buffers[0]);
|
||||
|
||||
messageId = is.readShort();
|
||||
ArrayList<UTF8Buffer> list = new ArrayList<UTF8Buffer>();
|
||||
while(is.available() > 0) {
|
||||
list.add(MessageSupport.readUTF(is));
|
||||
}
|
||||
topics = list.toArray(new UTF8Buffer[list.size()]);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MQTTFrame encode() {
|
||||
try {
|
||||
DataByteArrayOutputStream os = new DataByteArrayOutputStream();
|
||||
QoS qos = qos();
|
||||
if(qos != QoS.AT_MOST_ONCE) {
|
||||
os.writeShort(messageId);
|
||||
}
|
||||
for(UTF8Buffer topic: topics) {
|
||||
MessageSupport.writeUTF(os, topic);
|
||||
}
|
||||
|
||||
MQTTFrame frame = new MQTTFrame();
|
||||
frame.header(header());
|
||||
frame.commandType(TYPE);
|
||||
return frame.buffer(os.toBuffer());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("The impossible happened");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dup() {
|
||||
return super.dup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UNSUBSCRIBE dup(boolean dup) {
|
||||
return (UNSUBSCRIBE) super.dup(dup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QoS qos() {
|
||||
return super.qos();
|
||||
}
|
||||
|
||||
public short messageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public UNSUBSCRIBE messageId(short messageId) {
|
||||
this.messageId = messageId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UTF8Buffer[] topics() {
|
||||
return topics;
|
||||
}
|
||||
|
||||
public UNSUBSCRIBE topics(UTF8Buffer[] topics) {
|
||||
this.topics = topics;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UNSUBSCRIBE{" +
|
||||
"dup=" + dup() +
|
||||
", qos=" + qos() +
|
||||
", messageId=" + messageId +
|
||||
", topics=" + (topics == null ? null : Arrays.asList(topics)) +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<version>0.26</version>
|
||||
<version>0.31</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package cn.wildfirechat.pojos;
|
||||
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class InputSetGroupManager extends InputGroupBase {
|
||||
private String group_id;
|
||||
private List<String> members;
|
||||
private boolean is_manager;
|
||||
|
||||
public String getGroup_id() {
|
||||
return group_id;
|
||||
}
|
||||
|
||||
public void setGroup_id(String group_id) {
|
||||
this.group_id = group_id;
|
||||
}
|
||||
|
||||
public List<String> getMembers() {
|
||||
return members;
|
||||
}
|
||||
|
||||
public void setMembers(List<String> members) {
|
||||
this.members = members;
|
||||
}
|
||||
|
||||
public boolean isIs_manager() {
|
||||
return is_manager;
|
||||
}
|
||||
|
||||
public void setIs_manager(boolean is_manager) {
|
||||
this.is_manager = is_manager;
|
||||
}
|
||||
|
||||
public boolean isValide() {
|
||||
if (StringUtil.isNullOrEmpty(operator) || StringUtil.isNullOrEmpty(group_id) || members == null || members.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public WFCMessage.SetGroupManagerRequest toProtoGroupRequest() {
|
||||
WFCMessage.SetGroupManagerRequest.Builder addGroupBuilder = WFCMessage.SetGroupManagerRequest.newBuilder();
|
||||
addGroupBuilder.setGroupId(group_id);
|
||||
addGroupBuilder.addAllUserId(members);
|
||||
addGroupBuilder.setType(is_manager ? 1 : 0);
|
||||
|
||||
if (to_lines != null) {
|
||||
for (Integer line : to_lines
|
||||
) {
|
||||
addGroupBuilder.addToLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
if (notify_message != null) {
|
||||
addGroupBuilder.setNotifyContent(notify_message.toProtoMessageContent());
|
||||
}
|
||||
return addGroupBuilder.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -129,8 +129,8 @@ public class MessagePayload {
|
||||
builder.setSearchableContent(searchableContent);
|
||||
if (!StringUtil.isNullOrEmpty(pushContent))
|
||||
builder.setPushContent(pushContent);
|
||||
if (!StringUtil.isNullOrEmpty(searchableContent))
|
||||
builder.setContent(searchableContent);
|
||||
if (!StringUtil.isNullOrEmpty(content))
|
||||
builder.setContent(content);
|
||||
if (!StringUtil.isNullOrEmpty(base64edData))
|
||||
builder.setData(ByteString.copyFrom(Base64.getDecoder().decode(base64edData)));
|
||||
if (!StringUtil.isNullOrEmpty(remoteMediaUrl))
|
||||
|
||||
@@ -36,6 +36,7 @@ public interface APIPath {
|
||||
String Group_Member_List = "/admin/group/member/list";
|
||||
String Group_Member_Add = "/admin/group/member/add";
|
||||
String Group_Member_Kickoff = "/admin/group/member/del";
|
||||
String Group_Set_Manager = "/admin/group/manager/set";
|
||||
|
||||
|
||||
String Create_Channel = "/admin/channel/create";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@
|
||||
<!--<relativePath>../pom.xml</relativePath>-->
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<version>0.26</version>
|
||||
<version>0.31</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>distribution</artifactId>
|
||||
|
||||
@@ -19,11 +19,6 @@
|
||||
<directory>../broker/migrate/h2/</directory>
|
||||
<outputDirectory>/migrate/h2</outputDirectory>
|
||||
</fileSet>
|
||||
|
||||
<fileSet>
|
||||
<directory>../wildfire_jmeter/test_plan/</directory>
|
||||
<outputDirectory>/tools/jmeter</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
|
||||
<files>
|
||||
@@ -68,23 +63,12 @@
|
||||
<outputDirectory>config/</outputDirectory>
|
||||
</file>
|
||||
|
||||
<file>
|
||||
<source>../wildfire_jmeter/target/wildfire-xmeter-jar-with-dependencies.jar</source>
|
||||
<outputDirectory>tools/jmeter</outputDirectory>
|
||||
</file>
|
||||
|
||||
<file>
|
||||
<source>../broker/target/moquette-broker-${project.version}.jar</source>
|
||||
<outputDirectory>lib/</outputDirectory>
|
||||
<destName>moquette-broker-${project.version}.jar</destName>
|
||||
</file>
|
||||
|
||||
<file>
|
||||
<source>../client/target/client-${project.version}.jar</source>
|
||||
<outputDirectory>java_client/</outputDirectory>
|
||||
<destName>client-${project.version}.jar</destName>
|
||||
</file>
|
||||
|
||||
<file>
|
||||
<source>../common/target/common-${project.version}.jar</source>
|
||||
<outputDirectory>java_client/</outputDirectory>
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
# Server configuration
|
||||
#*********************************************************************
|
||||
#服务器的接入IP。给客户端提供是${server.ip}和${http_port}。
|
||||
#客户端会从${http_port}端口获取到长链接端口。
|
||||
#客户端会从${http_port}端口获取到长链接端口。这个地址一定要改成客户端可以访问到的IP地址
|
||||
# (如果您部署云服务器上或者具有独立公网出口的服务器上,请改为对应的公网IP;如果您部署在在内网环境下,在内网使用,这个地方改成内网地址)
|
||||
server.ip 0.0.0.0
|
||||
|
||||
##原生客户端长链接端口
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
<version>0.26</version>
|
||||
<version>0.31</version>
|
||||
<name>Wildfire Chat</name>
|
||||
<description>Wildfire Chat Server</description>
|
||||
<inceptionYear>2019</inceptionYear>
|
||||
@@ -32,8 +32,6 @@
|
||||
|
||||
<modules>
|
||||
<module>broker</module>
|
||||
<module>wildfire_jmeter</module>
|
||||
<module>client</module>
|
||||
<module>common</module>
|
||||
<module>sdk</module>
|
||||
<module>distribution</module>
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<version>0.26</version>
|
||||
<version>0.31</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package cn.wildfirechat.sdk;
|
||||
|
||||
import cn.wildfirechat.common.APIPath;
|
||||
import cn.wildfirechat.pojos.*;
|
||||
import cn.wildfirechat.sdk.model.IMResult;
|
||||
import cn.wildfirechat.sdk.utilities.AdminHttpUtils;
|
||||
|
||||
public class ChannelAdmin {
|
||||
public static IMResult<OutputCreateChatroom> createChannel(InputCreateChatroom input) throws Exception {
|
||||
String path = APIPath.Create_Chatroom;
|
||||
return AdminHttpUtils.httpJsonPost(path, input, OutputCreateChatroom.class);
|
||||
}
|
||||
|
||||
public static IMResult<Void> destroyChannel(String chatroomId) throws Exception {
|
||||
String path = APIPath.Chatroom_Destroy;
|
||||
InputDestoryChatroom input = new InputDestoryChatroom();
|
||||
input.setChatroomId(chatroomId);
|
||||
return AdminHttpUtils.httpJsonPost(path, input, Void.class);
|
||||
}
|
||||
|
||||
public static IMResult<OutputGetChatroomInfo> getChannelInfo(String chatroomId) throws Exception {
|
||||
String path = APIPath.Chatroom_Info;
|
||||
InputGetChatroomInfo input = new InputGetChatroomInfo(chatroomId);
|
||||
return AdminHttpUtils.httpJsonPost(path, input, OutputGetChatroomInfo.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,7 +19,7 @@ public class FriendAdmin {
|
||||
String path = APIPath.Friend_Get_List;
|
||||
InputGetFriendList input = new InputGetFriendList();
|
||||
input.setUserId(userId);
|
||||
input.setStatus(0);
|
||||
input.setStatus(status);
|
||||
return AdminHttpUtils.httpJsonPost(path, input, Void.class);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,20 @@ public class GroupAdmin {
|
||||
return AdminHttpUtils.httpJsonPost(path, addGroupMember, Void.class);
|
||||
}
|
||||
|
||||
public static IMResult<Void> setGroupManager(String operator, String groupId, List<String> groupMemberIds, boolean isManager, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
|
||||
String path = APIPath.Group_Set_Manager;
|
||||
InputSetGroupManager addGroupMember = new InputSetGroupManager();
|
||||
addGroupMember.setGroup_id(groupId);
|
||||
addGroupMember.setMembers(groupMemberIds);
|
||||
addGroupMember.setIs_manager(isManager);
|
||||
addGroupMember.setOperator(operator);
|
||||
addGroupMember.setTo_lines(to_lines);
|
||||
addGroupMember.setNotify_message(notify_message);
|
||||
return AdminHttpUtils.httpJsonPost(path, addGroupMember, Void.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static IMResult<Void> kickoffGroupMembers(String operator, String groupId, List<String> groupMemberIds, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
|
||||
String path = APIPath.Group_Member_Kickoff;
|
||||
InputKickoffGroupMember kickoffGroupMember = new InputKickoffGroupMember();
|
||||
|
||||
@@ -268,6 +268,24 @@ public class Main {
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
voidIMResult = GroupAdmin.setGroupManager("user1", groupInfo.getTarget_id(), Arrays.asList("user4", "user5"), true, null, null);
|
||||
if (voidIMResult != null && voidIMResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
System.out.println("set group manager success");
|
||||
} else {
|
||||
System.out.println("set group manager failure");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
voidIMResult = GroupAdmin.setGroupManager("user1", groupInfo.getTarget_id(), Arrays.asList("user4", "user5"), false, null, null);
|
||||
if (voidIMResult != null && voidIMResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
System.out.println("cancel group manager success");
|
||||
} else {
|
||||
System.out.println("cancel group manager failure");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//***********************************************
|
||||
//**** 消息相关功能
|
||||
//***********************************************
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
/target/
|
||||
@@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
||||
@@ -1,172 +0,0 @@
|
||||
# Release
|
||||
The version 1.0 is released at 18th, Dec, 2017. Now the plugin supports share MQTT connection in the same threadgroup. Also, this version fixed some of bugs reported in the community.
|
||||
|
||||
# mqtt-jmeter
|
||||
MQTT JMeter Plugin, it's used for testing MQTT protocol. The plugin was used for EMQ's performance benchmark test, and here is [report link](https://github.com/emqtt/emq-xmeter-cn).
|
||||
The plugin is developed and maintained by [XMeter](https://www.xmeter.net). XMeter is a professional performance testing service provider.
|
||||
|
||||
# Install instruction
|
||||
The plugin is a standard JMeter plugin. You can download the latest version of mqtt-jmeter from [here](https://github.com/emqtt/mqtt-jmeter/releases), and then copy the downloaded JAR files into $JMETER_HOME/lib/ext folder. After restart the JMeter, then you can see the 3 samplers provided by this plugin.
|
||||
|
||||
We recommend to use JMeter 3.0 or above.
|
||||
|
||||
## Build from source code
|
||||
|
||||
If you'd like to build binary by yourself, please clone the project and run 'mvn install'. Maven will download some JMeter dependency binary files, so the build elapsed time will up to your network status.
|
||||
|
||||
# How to use
|
||||
The plugin includes 3 samplers:
|
||||
|
||||
- Connection sampler, which can be used for connection mock. For example, in a large scale system, there could have lots of backend connections with no data transimission except some hearbeat signal. The sampler can be used in this case.
|
||||
|
||||
- Pub sampler, which can be used for publish message to MQTT server.
|
||||
|
||||
- Sub sampler, which can be used for sub message from MQTT server.
|
||||
|
||||
If MQTT JMeter plugin is installed successfully, then open JMeter and below 3 MQTT samplers can be found under 'Sampler'.
|
||||
|
||||

|
||||
|
||||
|
||||
## Connection sampler
|
||||
|
||||

|
||||
|
||||
### MQTT connection
|
||||
|
||||
This section includes basic connection settings.
|
||||
|
||||
- **Server name or IP**: The server install with MQTT server, it can be either IP address or server name. The default value is 127.0.0.1. **DO NOT** add protocol (e.g. tcp:// or ssl://) before server name or IP address!
|
||||
|
||||
- **Port number**: The port that opens by MQTT server, the default value is 1883 for TCP protocol, and normally 8883 for SSL protocol.
|
||||
|
||||
- **MQTT version**: The MQTT version, default is 3.1, and another option is 3.1.1. Sometimes the version is probably required for establish connection to [Azure IoTHub](https://github.com/emqtt/mqtt-jmeter/issues/21).
|
||||
|
||||
- **Timeout(s)**: The connection timeout seconds while connecting to MQTT server. The default is 10 seconds.
|
||||
|
||||
### MQTT Protocol
|
||||
|
||||
The sampler supports for 2 protocols, TCP and SSL. For the SSL protocol, it includes normal SSL and dual SSL authentication.
|
||||
|
||||
If **'Dual SSL authentication'** is checked, please follow 'Certification files for SSL/TLS connections' at end of this doc to set the client SSL configuration.
|
||||
|
||||

|
||||
|
||||
### User authentication
|
||||
|
||||
User can configure MQTT server with user name & password authentication, refer to [EMQ user name and password authentication guide](http://emqtt.com/docs/v2/guide.html#id3).
|
||||
|
||||
- **User name**: If MQTT server configured with user name, then specify user name here.
|
||||
|
||||
- **Password**: If MQTT server configured with password, then specify password here.
|
||||
|
||||
### Connection options
|
||||
|
||||
- **ClientId**: Identification of the client. (Default value is 'conn_'.) If 'Add random client id suffix' is selected, JMeter plugin will append generated uuid as suffix to represent the client, otherwise, the text of 'ClientId' will be passed as 'clientId' of current connection.
|
||||
|
||||
- **Keep alive(s)**: Ping packet send interval in seconds. Default value is 300, which means each connection sends a ping packet to MQTT server every 5 minutes.
|
||||
|
||||
- **Connection keep time(s)**: The value is to set the connection elapsed time after successfully establishing MQTT connection. The default value is 1800 seconds, which means that the connection will be alive within 30 minutes.
|
||||
|
||||
- **Connect attampt max**: The maximum number of reconnect attempts before an error is reported back to the client on the first attempt by the client to connect to a server. Set to -1 to use unlimited attempts. Defaults to 0.
|
||||
|
||||
- **Reconnect attampt max**: The maximum number of reconnect attempts before an error is reported back to the client after a server connection had previously been established. Set to -1 to use unlimited attempts. Defaults to 0.
|
||||
|
||||
## Pub sampler
|
||||

|
||||
|
||||
For **MQTT connection**, **User authentication**, **Connection options** and **MQTT version** section settings, please refer to *Connection sampler* for more detailed information.
|
||||
|
||||
- **Share conn in thread**: This option allows all of pub and sub samplers in the same threadgroup shares the same connection, this can simulate the situation of one device can be either pub or sub. For example, one IoT device can either send message to server, or receive the control message from server. Please notice, only those samplers in the same threadgroup check this option, then the connection will be shared. Also, the connection settings of first pub/sub sampler in the threadgroup will be used for shared connections, rests of connection settings in other samplers will be ignored if the option is checked. Please take a look at following 3 examples.
|
||||
|
||||
```
|
||||
ThreadGroupSample1
|
||||
PubSampler1 (shared connection), the connection settings will be used for the shared connection.
|
||||
SubSampler1 (shared connection), use shared connection of PubSampler1, the connection setting of this sampler will be ignored.
|
||||
|
||||
|
||||
ThreadGroupSample2
|
||||
PubSampler1 (shared connection), the connection settings will be used for the shared connection.
|
||||
SubSampler1 (not shared connection), another connection will be created with configuration in this sampler.
|
||||
|
||||
|
||||
ThreadGroupSample3
|
||||
PubSampler1 (not shared connection), one connection will be created for the connection setting in this sampler.
|
||||
SubSampler1 (shared connection), the connection settings will be used for the shared connection.
|
||||
SubSampler2 (shared connection), use shared connection of SubSampler1, so the connection setting of this sampler will be ignored.
|
||||
|
||||
```
|
||||
|
||||
### Pub options
|
||||
|
||||
- **QoS level**: The available QoS value, 0 is AT_MOST_ONCE, 1 is AT_LEAST_ONCE and 2 is EXACTLY_ONCE.
|
||||
|
||||
- **Topic name**: The topic name that the message will send to.
|
||||
|
||||
- **Add timestamp in payload**: Add timestamp in the payload or not. If the checkbox is enabled, then timestamp of running pub sampler will be added ahead of payload. Mostly it can be used together with **Sub sampler** to calculate message latency time.
|
||||
|
||||
### Payloads
|
||||
|
||||
**Message type**: 3 types of message can be used.
|
||||
|
||||
- String: The normal string that sent to MQTT server. It can also be a JMeter variable.
|
||||
|
||||
- Hex string: The hex string that sent to MQTT server. The chars input must be [0-9] or [A-F] or [a-f]. The hex string will be converted to binary and send to server. It can also be a JMeter variable.
|
||||
|
||||
- Random string with fixed length: Refer to below screenshot. If the option is selected, then it requires user to input 'Length'. The length means the auto generated string length. Default is 1024, which means generated a 1kb size of random string.
|
||||
|
||||

|
||||
|
||||
## Sub sampler
|
||||

|
||||
|
||||
For **MQTT connection**, **User authentication**, **Connection options** and **MQTT version** section settings, please refer to *Connection sampler* for more detailed information.
|
||||
|
||||
For **Share conn in thread**, please refer to the Pub sampler.
|
||||
|
||||
### Sub options
|
||||
|
||||
- **QoS level**: The available QoS value, 0 is AT_MOST_ONCE, 1 is AT_LEAST_ONCE and 2 is EXACTLY_ONCE.
|
||||
|
||||
- **Topic name**: The topic name that subscriber will subscribe to. If want to subscribe multiple topics, please use comma to separate the different topics.
|
||||
|
||||
- **Payload includes timestamp**: If the checkbox is enabled, then it means the payload includes timestamp. It can be used to calcuate the message latency time.
|
||||
|
||||
```
|
||||
message_latency = timestamp_in_sub_when_receive_msg - timestamp_in_payload (timestamp in pub machine when sending out message)
|
||||
|
||||
Please notice, if the machine publish message is not the same as subscriber, then the calculated message latency time is not accurate.
|
||||
It's because the time is almost not the same in different machines. So the latency time calculated by sub sampler could be only be a reference.
|
||||
```
|
||||
- **Sample on**: It controls how to sample. The default value is '**elapsed with specified time(ms)**', which means a sub sampler will be occurred every ms specified in next text field (default is 1000ms). During the 1000 ms, multiple messages could be received, and result in report is the summarized data during 1000 ms. If the value is set to 2000, then means summarized report during 2000 ms. Another option is '**received number of message**', which means a sub sampler will be occurred when received number of message that specified in next text field (default is 1).
|
||||
|
||||
- **Debug response**: If it's checked, then the received message will be print in response. It's recommend to enable it when you're debugging script.
|
||||
|
||||
## Certification files for SSL/TLS connections
|
||||
After deploying emqtt server, you get the following OOTB (out of the box) SSL/TLS certification files under ${EMQTTD_HOME}/etc/certs directory:
|
||||
|
||||
1) **cacert.pem** : the self-signed CA certification
|
||||
|
||||
2) **cert.pem** : certification for emqtt server
|
||||
|
||||
3) **client-cert.pem** : certfication for emqtt client in order to connect to server via SSL/TLS connection. In this jmeter plugin case, the client implies jmeter "virtual user"
|
||||
|
||||
4) **client-key.pem** **key.pem** : key files to protect client and server certification respectively
|
||||
|
||||
[Note:] The above server and client certifications are both issued by the self-signed CA. If you would like to use official certifications for your EMQTT deployment, please check out relevant document to configure it.
|
||||
|
||||
We will use the OOTB test certfications (as an example) to show you how to prepare the required certification files for this EMQTT JMeter plugin.
|
||||
|
||||
```
|
||||
export PATH=$PATH:<YOUR_JDK_HOME>/bin
|
||||
|
||||
keytool -import -alias cacert -keystore emqtt.jks -file cacert.pem -storepass <YOUR_PASSWORD> -trustcacerts -noprompt
|
||||
keytool -import -alias client -keystore emqtt.jks -file client-cert.pem -storepass <YOUR_PASSWORD>
|
||||
keytool -import -alias server -keystore emqtt.jks -file cert.pem -storepass <YOUR_PASSWORD>
|
||||
|
||||
openssl pkcs12 -export -inkey client-key.pem -in client-cert.pem -out client.p12 -password pass:<YOUR_PASSWORD>
|
||||
```
|
||||
|
||||
#### Specify key store, client certfication and corresponding pass phrases in plugin sampler:
|
||||
|
||||

|
||||
@@ -1,112 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<relativePath>../</relativePath>
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<version>0.26</version>
|
||||
</parent>
|
||||
|
||||
<groupId>net.xmeter</groupId>
|
||||
<artifactId>wildfire-jmeter</artifactId>
|
||||
|
||||
<properties>
|
||||
<jmeter-version>3.3</jmeter-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.jmeter</groupId>
|
||||
<artifactId>ApacheJMeter_core</artifactId>
|
||||
<version>${jmeter-version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.jmeter</groupId>
|
||||
<artifactId>ApacheJMeter_java</artifactId>
|
||||
<version>${jmeter-version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.fusesource.hawtbuf/hawtbuf -->
|
||||
<dependency>
|
||||
<groupId>org.fusesource.hawtbuf</groupId>
|
||||
<artifactId>hawtbuf</artifactId>
|
||||
<version>1.11</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.fusesource.hawtdispatch/hawtdispatch-transport -->
|
||||
<dependency>
|
||||
<groupId>org.fusesource.hawtdispatch</groupId>
|
||||
<artifactId>hawtdispatch-transport</artifactId>
|
||||
<version>1.22</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<artifactId>client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>2.5.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<type>jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>wildfire-xmeter</finalName>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.5.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>assemble-all</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 ikidou
|
||||
*
|
||||
* 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 ikidou.reflect;
|
||||
|
||||
import ikidou.reflect.exception.TypeException;
|
||||
import ikidou.reflect.typeimpl.ParameterizedTypeImpl;
|
||||
import ikidou.reflect.typeimpl.WildcardTypeImpl;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TypeBuilder {
|
||||
private final TypeBuilder parent;
|
||||
private final Class raw;
|
||||
private final List<Type> args = new ArrayList<>();
|
||||
|
||||
|
||||
private TypeBuilder(Class raw, TypeBuilder parent) {
|
||||
assert raw != null;
|
||||
this.raw = raw;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public static TypeBuilder newInstance(Class raw) {
|
||||
return new TypeBuilder(raw, null);
|
||||
}
|
||||
|
||||
private static TypeBuilder newInstance(Class raw, TypeBuilder parent) {
|
||||
return new TypeBuilder(raw, parent);
|
||||
}
|
||||
|
||||
|
||||
public TypeBuilder beginSubType(Class raw) {
|
||||
return newInstance(raw, this);
|
||||
}
|
||||
|
||||
public TypeBuilder endSubType() {
|
||||
if (parent == null) {
|
||||
throw new TypeException("expect beginSubType() before endSubType()");
|
||||
}
|
||||
|
||||
parent.addTypeParam(getType());
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
public TypeBuilder addTypeParam(Class clazz) {
|
||||
return addTypeParam((Type) clazz);
|
||||
}
|
||||
|
||||
public TypeBuilder addTypeParamExtends(Class... classes) {
|
||||
if (classes == null) {
|
||||
throw new NullPointerException("addTypeParamExtends() expect not null Class");
|
||||
}
|
||||
|
||||
WildcardTypeImpl wildcardType = new WildcardTypeImpl(null, classes);
|
||||
|
||||
return addTypeParam(wildcardType);
|
||||
}
|
||||
|
||||
public TypeBuilder addTypeParamSuper(Class... classes) {
|
||||
if (classes == null) {
|
||||
throw new NullPointerException("addTypeParamSuper() expect not null Class");
|
||||
}
|
||||
|
||||
WildcardTypeImpl wildcardType = new WildcardTypeImpl(classes, null);
|
||||
|
||||
return addTypeParam(wildcardType);
|
||||
}
|
||||
|
||||
public TypeBuilder addTypeParam(Type type) {
|
||||
if (type == null) {
|
||||
throw new NullPointerException("addTypeParam expect not null Type");
|
||||
}
|
||||
|
||||
args.add(type);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Type build() {
|
||||
if (parent != null) {
|
||||
throw new TypeException("expect endSubType() before build()");
|
||||
}
|
||||
|
||||
return getType();
|
||||
}
|
||||
|
||||
private Type getType() {
|
||||
if (args.isEmpty()) {
|
||||
return raw;
|
||||
}
|
||||
return new ParameterizedTypeImpl(raw, args.toArray(new Type[args.size()]), null);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 ikidou
|
||||
*
|
||||
* 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 ikidou.reflect;
|
||||
|
||||
import ikidou.reflect.exception.TypeException;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public abstract class TypeToken<T> {
|
||||
private final Type type;
|
||||
|
||||
public TypeToken() {
|
||||
Type superclass = getClass().getGenericSuperclass();
|
||||
if (superclass instanceof Class) {
|
||||
throw new TypeException("No generics found!");
|
||||
}
|
||||
ParameterizedType type = (ParameterizedType) superclass;
|
||||
this.type = type.getActualTypeArguments()[0];
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 ikidou
|
||||
*
|
||||
* 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 ikidou.reflect.exception;
|
||||
|
||||
public class TypeException extends RuntimeException {
|
||||
public TypeException() {
|
||||
}
|
||||
|
||||
public TypeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public TypeException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public TypeException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public TypeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 ikidou
|
||||
*
|
||||
* 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 ikidou.reflect.typeimpl;
|
||||
|
||||
import ikidou.reflect.exception.TypeException;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.Arrays;
|
||||
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
public class ParameterizedTypeImpl implements ParameterizedType {
|
||||
private final Class raw;
|
||||
private final Type[] args;
|
||||
private final Type owner;
|
||||
|
||||
public ParameterizedTypeImpl(Class raw, Type[] args, Type owner) {
|
||||
this.raw = raw;
|
||||
this.args = args != null ? args : new Type[0];
|
||||
this.owner = owner;
|
||||
checkArgs();
|
||||
}
|
||||
|
||||
private void checkArgs() {
|
||||
if (raw == null) {
|
||||
throw new TypeException("raw class can't be null");
|
||||
}
|
||||
TypeVariable[] typeParameters = raw.getTypeParameters();
|
||||
if (args.length != 0 && typeParameters.length != args.length) {
|
||||
throw new TypeException(raw.getName() + " expect " + typeParameters.length + " arg(s), got " + args.length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type[] getActualTypeArguments() {
|
||||
return args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getRawType() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getOwnerType() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(raw.getName());
|
||||
if (args.length != 0) {
|
||||
sb.append('<');
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (i != 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
Type type = args[i];
|
||||
if (type instanceof Class) {
|
||||
Class clazz = (Class) type;
|
||||
|
||||
if (clazz.isArray()) {
|
||||
int count = 0;
|
||||
do {
|
||||
count++;
|
||||
clazz = clazz.getComponentType();
|
||||
} while (clazz.isArray());
|
||||
|
||||
sb.append(clazz.getName());
|
||||
|
||||
for (int j = count; j > 0; j--) {
|
||||
sb.append("[]");
|
||||
}
|
||||
} else {
|
||||
sb.append(clazz.getName());
|
||||
}
|
||||
} else {
|
||||
sb.append(args[i].toString());
|
||||
}
|
||||
}
|
||||
sb.append('>');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ParameterizedTypeImpl that = (ParameterizedTypeImpl) o;
|
||||
|
||||
if (!raw.equals(that.raw)) return false;
|
||||
// Probably incorrect - comparing Object[] arrays with Arrays.equals
|
||||
if (!Arrays.equals(args, that.args)) return false;
|
||||
return owner != null ? owner.equals(that.owner) : that.owner == null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = raw.hashCode();
|
||||
result = 31 * result + Arrays.hashCode(args);
|
||||
result = 31 * result + (owner != null ? owner.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 ikidou
|
||||
*
|
||||
* 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 ikidou.reflect.typeimpl;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.WildcardType;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class WildcardTypeImpl implements WildcardType {
|
||||
private final Class[] upper;
|
||||
private final Class[] lower;
|
||||
|
||||
public WildcardTypeImpl(Class[] lower, Class[] upper) {
|
||||
this.lower = lower != null ? lower : new Class[0];
|
||||
this.upper = upper != null ? upper : new Class[0];
|
||||
|
||||
checkArgs();
|
||||
}
|
||||
|
||||
private void checkArgs() {
|
||||
if (lower.length == 0 && upper.length == 0) {
|
||||
throw new IllegalArgumentException("lower or upper can't be null");
|
||||
}
|
||||
|
||||
checkArgs(lower);
|
||||
checkArgs(upper);
|
||||
}
|
||||
|
||||
private void checkArgs(Class[] args) {
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
Class clazz = args[i];
|
||||
if (!clazz.isInterface()) {
|
||||
throw new IllegalArgumentException(clazz.getName() + " not a interface!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type[] getUpperBounds() {
|
||||
return upper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type[] getLowerBounds() {
|
||||
return lower;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (upper.length > 0) {
|
||||
if (upper[0] == Object.class) {
|
||||
return "?";
|
||||
}
|
||||
return getTypeString("? extends ", upper);
|
||||
} else {
|
||||
return getTypeString("? super ", lower);
|
||||
}
|
||||
}
|
||||
|
||||
private String getTypeString(String prefix, Class[] type) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(prefix);
|
||||
|
||||
for (int i = 0; i < type.length; i++) {
|
||||
if (i != 0) {
|
||||
sb.append(" & ");
|
||||
}
|
||||
sb.append(type[i].getName());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
WildcardTypeImpl that = (WildcardTypeImpl) o;
|
||||
|
||||
return Arrays.equals(upper, that.upper) && Arrays.equals(lower, that.lower);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Arrays.hashCode(upper);
|
||||
result = 31 * result + Arrays.hashCode(lower);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package net.xmeter;
|
||||
|
||||
public interface Constants {
|
||||
public static final String SERVER = "mqtt.server";
|
||||
public static final String MQTT_VERSION = "mqtt.version";
|
||||
public static final String PORT = "mqtt.port";
|
||||
public static final String CONN_TIMEOUT = "mqtt.conn_timeout";
|
||||
|
||||
public static final String PROTOCOL = "mqtt.protocol";
|
||||
|
||||
public static final String USER_NAME_AUTH = "mqtt.user_name";
|
||||
public static final String USER_NAME_PREFIX = "mqtt.user_name_prefix";
|
||||
|
||||
public static final String CONN_KEEP_TIME = "mqtt.keep_time";
|
||||
public static final String CONN_CLIENT_ID_PREFIX = "mqtt.client_id_prefix";
|
||||
public static final String CONN_CLIENT_ID_SUFFIX = "mqtt.client_id_suffix";
|
||||
|
||||
public static final String CONN_ATTAMPT_MAX = "mqtt.conn_attampt_max";
|
||||
public static final String CONN_KEEP_ALIVE = "mqtt.conn_keep_alive";
|
||||
public static final String CONN_RECONN_ATTAMPT_MAX = "mqtt.reconn_attampt_max";
|
||||
public static final String CONN_SHARE_CONNECTION = "mqtt.conn_share";
|
||||
|
||||
public static final String MESSAGE_TYPE = "mqtt.message_type";
|
||||
public static final String MESSAGE_FIX_LENGTH = "mqtt.message_type_fixed_length";
|
||||
public static final String MESSAGE_TO_BE_SENT = "mqtt.message_to_sent";
|
||||
|
||||
public static final String TOPIC_NAME = "mqtt.topic_name";
|
||||
public static final String QOS_LEVEL = "mqtt.qos_level";
|
||||
public static final String ADD_TIMESTAMP = "mqtt.add_timestamp";
|
||||
|
||||
public static final String SAMPLE_CONDITION_VALUE = "mqtt.sample_condition_value";
|
||||
public static final String SAMPLE_CONDITION = "mqtt.sample_condition";
|
||||
|
||||
public static final String TIME_STAMP_SEP_FLAG = "ts_sep_flag";
|
||||
|
||||
public static final String DEBUG_RESPONSE = "mqtt.debug_response";
|
||||
|
||||
public static final int QOS_0 = 0;
|
||||
public static final int QOS_1 = 1;
|
||||
public static final int QOS_2 = 2;
|
||||
|
||||
public static final String MESSAGE_TYPE_RANDOM_STR_WITH_FIX_LEN = "Random string with fixed length";
|
||||
public static final String MESSAGE_TYPE_HEX_STRING = "Hex string";
|
||||
public static final String MESSAGE_TYPE_STRING = "String";
|
||||
|
||||
public static final String MQTT_VERSION_3_1_1 = "3.1.1";
|
||||
public static final String MQTT_VERSION_3_1 = "3.1";
|
||||
|
||||
public static final String SAMPLE_ON_CONDITION_OPTION1 = "specified elapsed time (ms)";
|
||||
public static final String SAMPLE_ON_CONDITION_OPTION2 = "number of received messages";
|
||||
|
||||
public static final int MAX_CLIENT_ID_LENGTH = 23;
|
||||
|
||||
public static final String DEFAULT_SERVER = "127.0.0.1";
|
||||
public static final String DEFAULT_MQTT_VERSION = "3.1";
|
||||
public static final String DEFAULT_PORT = "8080";
|
||||
public static final String DEFAULT_CONN_TIME_OUT = "10";
|
||||
public static final String DEFAULT_PROTOCOL = "TCP";
|
||||
public static final boolean DEFAULT_CONNECTION_SHARE = true;
|
||||
|
||||
public static final String JMETER_VARIABLE_PREFIX = "${";
|
||||
|
||||
public static final String DEFAULT_TOPIC_NAME = "test_topic";
|
||||
|
||||
public static final String DEFAULT_TARGET = "test_user";
|
||||
|
||||
public static final String CONV_TYPE_SINGLE = "Single";
|
||||
public static final String CONV_TYPE_GROUP = "Group";
|
||||
|
||||
public static final String DEFAULT_CONN_PREFIX_FOR_CONN = "conn_";
|
||||
public static final String DEFAULT_CONN_PREFIX_FOR_PUB = "pub_";
|
||||
|
||||
public static final String DEFAULT_CONN_KEEP_ALIVE = "300";
|
||||
public static final String DEFAULT_CONN_KEEP_TIME = "1800";
|
||||
public static final String DEFAULT_CONN_ATTAMPT_MAX = "0";
|
||||
public static final String DEFAULT_CONN_RECONN_ATTAMPT_MAX = "0";
|
||||
|
||||
public static final String DEFAULT_SAMPLE_VALUE_COUNT = "1";
|
||||
public static final String DEFAULT_SAMPLE_VALUE_ELAPSED_TIME_SEC = "1000";
|
||||
|
||||
public static final boolean DEFAULT_ADD_TIMESTAMP = false;
|
||||
public static final String DEFAULT_MESSAGE_FIX_LENGTH = "1024";
|
||||
|
||||
public static final boolean DEFAULT_ADD_CLIENT_ID_SUFFIX = true;
|
||||
|
||||
public static final String TARGET = "wildfire.target";
|
||||
|
||||
public static final String CONV_TYPE = "wildfire.conv_type";
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package net.xmeter;
|
||||
|
||||
public class IMResult<T> {
|
||||
public enum IMResultCode {
|
||||
//General error
|
||||
IMRESULT_CODE_SUCCESS(0, "success"),
|
||||
IMRESULT_CODE_SECRECT_KEY_MISMATCH(1, "secrect key mismatch"),
|
||||
IMRESULT_CODE_INVALID_DATA(2, "invalid data"),
|
||||
IMRESULT_CODE_NODE_NOT_EXIST(3, "node not exist"),
|
||||
IMRESULT_CODE_SERVER_ERROR(4, "server error"),
|
||||
IMRESULT_CODE_NOT_MODIFIED(5, "not modified"),
|
||||
|
||||
|
||||
//Auth error
|
||||
IMRESULT_CODE_TOKEN_ERROR(6, "token error"),
|
||||
IMRESULT_CODE_USER_FORBIDDEN(8, "user forbidden"),
|
||||
|
||||
//Message error
|
||||
IMRESULT_CODE_NOT_IN_GROUP(9, "not in group"),
|
||||
IMRESULT_CODE_INVALID_MESSAGE(10, "invalid message"),
|
||||
|
||||
//Group error
|
||||
IMRESULT_CODE_GROUP_ALREADY_EXIST(11, "group aleady exist"),
|
||||
|
||||
|
||||
//user error
|
||||
IMRESULT_CODE_PASSWORD_INCORRECT(15, "password incorrect"),
|
||||
|
||||
//user error
|
||||
IMRESULT_CODE_FRIEND_ALREADY_REQUEST(16, "already send request"),
|
||||
IMRESULT_CODE_FRIEND_REQUEST_BLOCKED(18, "friend request blocked"),
|
||||
IMRESULT_CODE_FRIEND_REQUEST_OVERTIME(19, "friend request overtime"),
|
||||
|
||||
IMRESULT_CODE_NOT_IN_CHATROOM(20, "not in chatroom"),
|
||||
|
||||
IMRESULT_CODE_CLIENT_COUNT_OUT_OF_LIMIT(245, "client count out of limit"),
|
||||
IMRESULT_CODE_IN_BLACK_LIST(246, "user in balck list"),
|
||||
IMRESULT_CODE_FORBIDDEN_SEND_MSG(247, "forbidden send msg globally"),
|
||||
IMRESULT_CODE_NOT_RIGHT(248, "no right to operate"),
|
||||
IMRESULT_CODE_TIMEOUT(249, "timeout"),
|
||||
IMRESULT_CODE_OVER_FREQUENCY(250, "over frequency"),
|
||||
INVALID_PARAMETER(251, "Invalid parameter"),
|
||||
IMRESULT_CODE_NOT_EXIST(253, "not exist"),
|
||||
IMRESULT_CODE_NOT_IMPLEMENT(254, "not implement"),
|
||||
|
||||
|
||||
IMRESULT_CODE_ASYNC_HANDLER(255, "异步执行,服务器内部逻辑需要此代码,为正常情况,不能返回客户端"),;
|
||||
|
||||
public int code;
|
||||
public String msg;
|
||||
|
||||
IMResultCode(int code, String msg) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public static IMResultCode fromCode(int code) {
|
||||
for (IMResultCode errorCode : IMResultCode.values()) {
|
||||
if(errorCode.code == code) {
|
||||
return errorCode;
|
||||
}
|
||||
}
|
||||
return IMRESULT_CODE_SERVER_ERROR;
|
||||
}
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int code;
|
||||
public String msg;
|
||||
public T result;
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public T getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(T result) {
|
||||
this.result = result;
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package net.xmeter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SubBean {
|
||||
private int receivedMessageSize = 0;
|
||||
private int receivedCount = 0;
|
||||
private double avgElapsedTime = 0f;
|
||||
|
||||
private List<String> contents = new ArrayList<String>();
|
||||
|
||||
public int getReceivedMessageSize() {
|
||||
return receivedMessageSize;
|
||||
}
|
||||
|
||||
public void setReceivedMessageSize(int receivedMessageSize) {
|
||||
this.receivedMessageSize = receivedMessageSize;
|
||||
}
|
||||
|
||||
public int getReceivedCount() {
|
||||
return receivedCount;
|
||||
}
|
||||
|
||||
public void setReceivedCount(int receivedCount) {
|
||||
this.receivedCount = receivedCount;
|
||||
}
|
||||
|
||||
public double getAvgElapsedTime() {
|
||||
return avgElapsedTime;
|
||||
}
|
||||
|
||||
public void setAvgElapsedTime(double avgElapsedTime) {
|
||||
this.avgElapsedTime = avgElapsedTime;
|
||||
}
|
||||
|
||||
public List<String> getContents() {
|
||||
return contents;
|
||||
}
|
||||
|
||||
public void setContents(List<String> contents) {
|
||||
this.contents = contents;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package net.xmeter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
|
||||
import org.apache.http.ssl.SSLContexts;
|
||||
import org.apache.jmeter.services.FileServer;
|
||||
import org.apache.jorphan.logging.LoggingManager;
|
||||
import org.apache.log.Logger;
|
||||
|
||||
import net.xmeter.samplers.AbstractMQTTSampler;
|
||||
|
||||
public class Util implements Constants {
|
||||
|
||||
private static SecureRandom random = new SecureRandom();
|
||||
private static char[] seeds = "abcdefghijklmnopqrstuvwxmy0123456789".toCharArray();
|
||||
private transient static Logger logger = LoggingManager.getLoggerForClass();
|
||||
|
||||
public static String generateClientId(String prefix) {
|
||||
int leng = prefix.length();
|
||||
int postLeng = MAX_CLIENT_ID_LENGTH - leng;
|
||||
UUID uuid = UUID.randomUUID();
|
||||
String string = uuid.toString().replace("-", "");
|
||||
String post = string.substring(0, postLeng);
|
||||
return prefix + post;
|
||||
}
|
||||
|
||||
public static String generatePayload(int size) {
|
||||
StringBuffer res = new StringBuffer();
|
||||
for(int i = 0; i < size; i++) {
|
||||
res.append(seeds[random.nextInt(seeds.length - 1)]);
|
||||
}
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
package net.xmeter.gui;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import org.apache.jmeter.gui.util.HorizontalPanel;
|
||||
import org.apache.jmeter.gui.util.VerticalPanel;
|
||||
import org.apache.jorphan.gui.JLabeledTextField;
|
||||
|
||||
import net.xmeter.Constants;
|
||||
import net.xmeter.samplers.AbstractMQTTSampler;
|
||||
|
||||
public class CommonConnUI implements ChangeListener, ActionListener, Constants{
|
||||
private final JLabeledTextField serverAddr = new JLabeledTextField("Server name or IP:");
|
||||
private final JLabeledTextField serverPort = new JLabeledTextField("Port number:", 5);
|
||||
private JCheckBox connShared = new JCheckBox("Share conn in thread");
|
||||
private final JLabeledTextField timeout = new JLabeledTextField("Timeout(s):", 5);
|
||||
|
||||
private final JLabeledTextField userNameAuth = new JLabeledTextField("User name:");
|
||||
private JCheckBox userNameSuffix = new JCheckBox("Add random suffix for user name");
|
||||
|
||||
public final JLabeledTextField connNamePrefix = new JLabeledTextField("ClientId:", 8);
|
||||
private JCheckBox connNameSuffix = new JCheckBox("Add random suffix for ClientId");
|
||||
|
||||
private final JLabeledTextField connKeepAlive = new JLabeledTextField("Keep alive(s):", 4);
|
||||
|
||||
private final JLabeledTextField connKeeptime = new JLabeledTextField("Connection keep time(s):", 4);
|
||||
|
||||
private final JLabeledTextField connAttmptMax = new JLabeledTextField("Connect attampt max:", 0);
|
||||
private final JLabeledTextField reconnAttmptMax = new JLabeledTextField("Reconnect attampt max:", 0);
|
||||
|
||||
public JPanel createConnPanel() {
|
||||
JPanel con = new HorizontalPanel();
|
||||
|
||||
JPanel connPanel = new HorizontalPanel();
|
||||
connPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "MQTT connection"));
|
||||
connPanel.add(serverAddr);
|
||||
connPanel.add(serverPort);
|
||||
connPanel.add(connShared);
|
||||
|
||||
JPanel timeoutPannel = new HorizontalPanel();
|
||||
timeoutPannel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Timeout"));
|
||||
timeoutPannel.add(timeout);
|
||||
|
||||
con.add(connPanel);
|
||||
con.add(timeoutPannel);
|
||||
return con;
|
||||
}
|
||||
|
||||
public JPanel createConnOptions() {
|
||||
JPanel optsPanelCon = new VerticalPanel();
|
||||
optsPanelCon.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Connection options"));
|
||||
|
||||
JPanel optsPanel0 = new HorizontalPanel();
|
||||
optsPanel0.add(connNamePrefix);
|
||||
optsPanel0.add(connNameSuffix);
|
||||
connNameSuffix.setSelected(true);
|
||||
optsPanelCon.add(optsPanel0);
|
||||
|
||||
JPanel optsPanel1 = new HorizontalPanel();
|
||||
optsPanel1.add(connKeepAlive);
|
||||
optsPanel1.add(connKeeptime);
|
||||
optsPanelCon.add(optsPanel1);
|
||||
|
||||
optsPanel1.add(connAttmptMax);
|
||||
optsPanel1.add(reconnAttmptMax);
|
||||
optsPanelCon.add(optsPanel1);
|
||||
|
||||
return optsPanelCon;
|
||||
}
|
||||
|
||||
public JPanel createAuthentication() {
|
||||
JPanel optsPanelCon = new VerticalPanel();
|
||||
optsPanelCon.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "User authentication"));
|
||||
|
||||
JPanel optsPanel = new HorizontalPanel();
|
||||
optsPanel.add(userNameAuth);
|
||||
optsPanel.add(userNameSuffix);
|
||||
optsPanelCon.add(optsPanel);
|
||||
|
||||
return optsPanelCon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String action = e.getActionCommand();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
}
|
||||
|
||||
public void configure(AbstractMQTTSampler sampler) {
|
||||
serverAddr.setText(sampler.getServer());
|
||||
serverPort.setText(sampler.getPort());
|
||||
connShared.setSelected(sampler.isConnectionShare());
|
||||
if(!sampler.isConnectionShareShow()) {
|
||||
connShared.setVisible(false);
|
||||
}
|
||||
timeout.setText(sampler.getConnTimeout());
|
||||
|
||||
userNameAuth.setText(sampler.getUserNameProperty());
|
||||
userNameSuffix.setSelected(sampler.isUserNamePrefix());
|
||||
|
||||
connNamePrefix.setText(sampler.getConnPrefix());
|
||||
if(sampler.isClientIdSuffix()) {
|
||||
connNameSuffix.setSelected(true);
|
||||
} else {
|
||||
connNameSuffix.setSelected(false);
|
||||
}
|
||||
|
||||
connKeepAlive.setText(sampler.getConnKeepAlive());
|
||||
connKeeptime.setText(sampler.getConnKeepTime());
|
||||
if(!sampler.isKeepTimeShow()) {
|
||||
connKeeptime.setVisible(false);
|
||||
}
|
||||
connAttmptMax.setText(sampler.getConnAttamptMax());
|
||||
reconnAttmptMax.setText(sampler.getConnReconnAttamptMax());
|
||||
}
|
||||
|
||||
|
||||
public void setupSamplerProperties(AbstractMQTTSampler sampler) {
|
||||
sampler.setServer(serverAddr.getText());
|
||||
sampler.setPort(serverPort.getText());
|
||||
sampler.setConnectionShare(connShared.isSelected());
|
||||
sampler.setConnTimeout(timeout.getText());
|
||||
sampler.setConnectionShare(connShared.isSelected());
|
||||
|
||||
sampler.setUserNameAuth(userNameAuth.getText());
|
||||
sampler.setUserNamePrefix(userNameSuffix.isSelected());
|
||||
|
||||
sampler.setConnPrefix(connNamePrefix.getText());
|
||||
sampler.setClientIdSuffix(connNameSuffix.isSelected());
|
||||
|
||||
sampler.setConnKeepAlive(connKeepAlive.getText());
|
||||
sampler.setConnKeepTime(connKeeptime.getText());
|
||||
sampler.setConnAttamptMax(connAttmptMax.getText());
|
||||
sampler.setConnReconnAttamptMax(reconnAttmptMax.getText());
|
||||
}
|
||||
|
||||
public static int parseInt(String value) {
|
||||
if(value == null || "".equals(value.trim())) {
|
||||
return 0;
|
||||
}
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public void clearUI() {
|
||||
serverAddr.setText(DEFAULT_SERVER);
|
||||
serverPort.setText(DEFAULT_PORT);
|
||||
connShared.setSelected(DEFAULT_CONNECTION_SHARE);
|
||||
timeout.setText(DEFAULT_CONN_TIME_OUT);
|
||||
|
||||
|
||||
userNameAuth.setText("");
|
||||
userNameSuffix.setSelected(true);
|
||||
|
||||
connNamePrefix.setText(DEFAULT_CONN_PREFIX_FOR_CONN);
|
||||
connNameSuffix.setSelected(true);
|
||||
|
||||
connKeepAlive.setText(DEFAULT_CONN_KEEP_ALIVE);
|
||||
connKeeptime.setText(DEFAULT_CONN_KEEP_TIME);
|
||||
connAttmptMax.setText(DEFAULT_CONN_ATTAMPT_MAX);
|
||||
reconnAttmptMax.setText(DEFAULT_CONN_RECONN_ATTAMPT_MAX);
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package net.xmeter.gui;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import org.apache.jmeter.gui.util.VerticalPanel;
|
||||
import org.apache.jmeter.samplers.gui.AbstractSamplerGui;
|
||||
import org.apache.jmeter.testelement.TestElement;
|
||||
import org.apache.jorphan.logging.LoggingManager;
|
||||
import org.apache.log.Logger;
|
||||
|
||||
import net.xmeter.Constants;
|
||||
import net.xmeter.samplers.ConnectionSampler;
|
||||
|
||||
public class ConnectionSamplerUI extends AbstractSamplerGui implements Constants {
|
||||
private static final Logger logger = LoggingManager.getLoggerForClass();
|
||||
private CommonConnUI connUI = new CommonConnUI();
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1666890646673145131L;
|
||||
|
||||
public ConnectionSamplerUI() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
logger.info("Initializing the UI.");
|
||||
logger.info("Initializing the UI2.");
|
||||
setLayout(new BorderLayout());
|
||||
setBorder(makeBorder());
|
||||
|
||||
add(makeTitlePanel(), BorderLayout.NORTH);
|
||||
JPanel mainPanel = new VerticalPanel();
|
||||
add(mainPanel, BorderLayout.CENTER);
|
||||
|
||||
mainPanel.add(connUI.createConnPanel());
|
||||
mainPanel.add(connUI.createAuthentication());
|
||||
mainPanel.add(connUI.createConnOptions());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(TestElement element) {
|
||||
super.configure(element);
|
||||
ConnectionSampler sampler = (ConnectionSampler)element;
|
||||
connUI.configure(sampler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestElement createTestElement() {
|
||||
ConnectionSampler sampler = new ConnectionSampler();
|
||||
this.configureTestElement(sampler);
|
||||
connUI.setupSamplerProperties(sampler);
|
||||
return sampler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabelResource() {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStaticLabel() {
|
||||
return "Wildfire Connection Sampler";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyTestElement(TestElement arg0) {
|
||||
ConnectionSampler sampler = (ConnectionSampler)arg0;
|
||||
this.configureTestElement(sampler);
|
||||
connUI.setupSamplerProperties(sampler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearGui() {
|
||||
super.clearGui();
|
||||
connUI.clearUI();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
package net.xmeter.gui;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import net.xmeter.samplers.SendMessageSampler;
|
||||
import org.apache.jmeter.gui.util.HorizontalPanel;
|
||||
import org.apache.jmeter.gui.util.JSyntaxTextArea;
|
||||
import org.apache.jmeter.gui.util.JTextScrollPane;
|
||||
import org.apache.jmeter.gui.util.VerticalPanel;
|
||||
import org.apache.jmeter.samplers.gui.AbstractSamplerGui;
|
||||
import org.apache.jmeter.testelement.TestElement;
|
||||
import org.apache.jorphan.gui.JLabeledChoice;
|
||||
import org.apache.jorphan.gui.JLabeledTextField;
|
||||
import org.apache.jorphan.logging.LoggingManager;
|
||||
import org.apache.log.Logger;
|
||||
|
||||
import net.xmeter.Constants;
|
||||
|
||||
public class SendMessageSamplerUI extends AbstractSamplerGui implements Constants, ChangeListener {
|
||||
private static final Logger logger = LoggingManager.getLoggerForClass();
|
||||
private CommonConnUI connUI = new CommonConnUI();
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 2479085966683186422L;
|
||||
|
||||
private JLabeledChoice conversationTypeChoice = new JLabeledChoice("ConversationType:", new String[] { CONV_TYPE_SINGLE, CONV_TYPE_GROUP }, true, false);
|
||||
private final JLabeledTextField target = new JLabeledTextField("Target:");
|
||||
// private JCheckBox timestamp = new JCheckBox("Add timestamp in payload");
|
||||
|
||||
private final JSyntaxTextArea sendMessage = JSyntaxTextArea.getInstance(10, 50);
|
||||
private final JTextScrollPane messagePanel = JTextScrollPane.getInstance(sendMessage);
|
||||
// private JLabeledTextField stringLength = new JLabeledTextField("Length:");
|
||||
|
||||
public SendMessageSamplerUI() {
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
logger.info("Initializing the UI.");
|
||||
setLayout(new BorderLayout());
|
||||
setBorder(makeBorder());
|
||||
|
||||
add(makeTitlePanel(), BorderLayout.NORTH);
|
||||
JPanel mainPanel = new VerticalPanel();
|
||||
add(mainPanel, BorderLayout.CENTER);
|
||||
|
||||
mainPanel.add(connUI.createConnPanel());
|
||||
mainPanel.add(connUI.createAuthentication());
|
||||
mainPanel.add(connUI.createConnOptions());
|
||||
|
||||
mainPanel.add(createPubOption());
|
||||
mainPanel.add(createPayload());
|
||||
}
|
||||
|
||||
private JPanel createPubOption() {
|
||||
JPanel optsPanelCon = new VerticalPanel();
|
||||
optsPanelCon.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Send message options"));
|
||||
|
||||
conversationTypeChoice.addChangeListener(this);
|
||||
|
||||
JPanel optsPanel = new HorizontalPanel();
|
||||
optsPanel.add(conversationTypeChoice);
|
||||
optsPanel.add(target);
|
||||
optsPanelCon.add(optsPanel);
|
||||
|
||||
return optsPanelCon;
|
||||
}
|
||||
|
||||
private JPanel createPayload() {
|
||||
JPanel optsPanelCon = new VerticalPanel();
|
||||
optsPanelCon.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Message Content"));
|
||||
|
||||
JPanel horizon2 = new VerticalPanel();
|
||||
messagePanel.setVisible(true);
|
||||
horizon2.add(messagePanel);
|
||||
|
||||
optsPanelCon.add(horizon2);
|
||||
return optsPanelCon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStaticLabel() {
|
||||
return "Wildfire Send Message Sampler";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
// if(e.getSource() == this.messageTypes) {
|
||||
// int selectedIndex = this.messageTypes.getSelectedIndex();
|
||||
// if(selectedIndex == 0 || selectedIndex == 1) {
|
||||
// stringLength.setVisible(false);
|
||||
// messagePanel.setVisible(true);
|
||||
// } else if(selectedIndex == 2) {
|
||||
// messagePanel.setVisible(false);
|
||||
// stringLength.setVisible(true);
|
||||
// } else {
|
||||
// logger.info("Unknown message type.");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabelResource() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestElement createTestElement() {
|
||||
logger.info("createTestElement called.");
|
||||
SendMessageSampler sampler = new SendMessageSampler();
|
||||
this.setupSamplerProperties(sampler);
|
||||
connUI.setupSamplerProperties(sampler);
|
||||
return sampler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(TestElement element) {
|
||||
logger.info("configure called.");
|
||||
super.configure(element);
|
||||
SendMessageSampler sampler = (SendMessageSampler) element;
|
||||
|
||||
connUI.configure(sampler);
|
||||
// if(sampler.getQOS().trim().indexOf(JMETER_VARIABLE_PREFIX) == -1){
|
||||
if ("1".equals(sampler.getConvType())) {
|
||||
this.conversationTypeChoice.setSelectedIndex(1);
|
||||
} else {
|
||||
this.conversationTypeChoice.setSelectedIndex(0);
|
||||
}
|
||||
// } else {
|
||||
// this.conversationTypeChoice.setText(sampler.getQOS());
|
||||
// }
|
||||
|
||||
this.target.setText(sampler.getTarget());
|
||||
// this.timestamp.setSelected(sampler.isAddTimestamp());
|
||||
// if(MESSAGE_TYPE_STRING.equalsIgnoreCase(sampler.getMessageType())) {
|
||||
// this.messageTypes.setSelectedIndex(0);
|
||||
// this.messagePanel.setVisible(true);
|
||||
// } else if(MESSAGE_TYPE_HEX_STRING.equalsIgnoreCase(sampler.getMessageType())) {
|
||||
// this.messageTypes.setSelectedIndex(1);
|
||||
// } else if(MESSAGE_TYPE_RANDOM_STR_WITH_FIX_LEN.equalsIgnoreCase(sampler.getMessageType())) {
|
||||
// this.messageTypes.setSelectedIndex(2);
|
||||
// }
|
||||
//
|
||||
// stringLength.setText(String.valueOf(sampler.getMessageLength()));
|
||||
sendMessage.setText(sampler.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyTestElement(TestElement arg0) {
|
||||
logger.info("modifyTestElement called.");
|
||||
SendMessageSampler sampler = (SendMessageSampler) arg0;
|
||||
this.setupSamplerProperties(sampler);
|
||||
connUI.setupSamplerProperties(sampler);
|
||||
}
|
||||
|
||||
private void setupSamplerProperties(SendMessageSampler sampler) {
|
||||
this.configureTestElement(sampler);
|
||||
connUI.setupSamplerProperties(sampler);
|
||||
|
||||
sampler.setTarget(this.target.getText());
|
||||
sampler.setConvType(this.conversationTypeChoice.getSelectedIndex() + "");
|
||||
sampler.setMessage(this.sendMessage.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearGui() {
|
||||
logger.info("clearGui called.");
|
||||
super.clearGui();
|
||||
connUI.clearUI();
|
||||
connUI.connNamePrefix.setText(DEFAULT_CONN_PREFIX_FOR_PUB);
|
||||
this.target.setText(DEFAULT_TARGET);
|
||||
this.conversationTypeChoice.setSelectedIndex(0);
|
||||
this.sendMessage.setText("");
|
||||
}
|
||||
}
|
||||
@@ -1,259 +0,0 @@
|
||||
package net.xmeter.samplers;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import cn.wildfirechat.pojos.InputGetToken;
|
||||
import cn.wildfirechat.pojos.OutputGetIMTokenData;
|
||||
import net.xmeter.IMResult;
|
||||
import net.xmeter.Util;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.jmeter.samplers.AbstractSampler;
|
||||
|
||||
import net.xmeter.Constants;
|
||||
import org.apache.jorphan.logging.LoggingManager;
|
||||
import org.apache.log.Logger;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public abstract class AbstractMQTTSampler extends AbstractSampler implements Constants {
|
||||
private transient static Logger logger = LoggingManager.getLoggerForClass();
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 7163793218595455807L;
|
||||
|
||||
public String getServer() {
|
||||
return getPropertyAsString(SERVER, DEFAULT_SERVER);
|
||||
}
|
||||
|
||||
public void setServer(String server) {
|
||||
setProperty(SERVER, server);
|
||||
}
|
||||
|
||||
public String getMqttVersion() {
|
||||
return getPropertyAsString(MQTT_VERSION, DEFAULT_MQTT_VERSION);
|
||||
}
|
||||
|
||||
public String getPort() {
|
||||
return getPropertyAsString(PORT, DEFAULT_PORT);
|
||||
}
|
||||
|
||||
public void setPort(String port) {
|
||||
setProperty(PORT, port);
|
||||
}
|
||||
|
||||
public boolean isConnectionShare() {
|
||||
return getPropertyAsBoolean(CONN_SHARE_CONNECTION, DEFAULT_CONNECTION_SHARE);
|
||||
}
|
||||
|
||||
public void setConnectionShare(boolean shared) {
|
||||
setProperty(CONN_SHARE_CONNECTION, shared);
|
||||
}
|
||||
|
||||
public String getConnTimeout() {
|
||||
return getPropertyAsString(CONN_TIMEOUT, DEFAULT_CONN_TIME_OUT);
|
||||
}
|
||||
|
||||
public void setConnTimeout(String connTimeout) {
|
||||
setProperty(CONN_TIMEOUT, connTimeout);
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return getPropertyAsString(PROTOCOL, DEFAULT_PROTOCOL);
|
||||
}
|
||||
|
||||
public String getConnPrefix() {
|
||||
return getPropertyAsString(CONN_CLIENT_ID_PREFIX, DEFAULT_CONN_PREFIX_FOR_CONN);
|
||||
}
|
||||
|
||||
public void setConnPrefix(String connPrefix) {
|
||||
logger.info("set connprefix:" + connPrefix);
|
||||
setProperty(CONN_CLIENT_ID_PREFIX, connPrefix);
|
||||
}
|
||||
|
||||
public String getConnKeepAlive() {
|
||||
return getPropertyAsString(CONN_KEEP_ALIVE, DEFAULT_CONN_KEEP_ALIVE);
|
||||
}
|
||||
|
||||
public void setConnKeepAlive(String connKeepAlive) {
|
||||
setProperty(CONN_KEEP_ALIVE, connKeepAlive);
|
||||
}
|
||||
|
||||
public boolean isClientIdSuffix() {
|
||||
return getPropertyAsBoolean(CONN_CLIENT_ID_SUFFIX, DEFAULT_ADD_CLIENT_ID_SUFFIX);
|
||||
}
|
||||
|
||||
public void setClientIdSuffix(boolean clientIdSuffix) {
|
||||
setProperty(CONN_CLIENT_ID_SUFFIX, clientIdSuffix);
|
||||
}
|
||||
|
||||
public String getConnKeepTime() {
|
||||
return getPropertyAsString(CONN_KEEP_TIME, DEFAULT_CONN_KEEP_TIME);
|
||||
}
|
||||
|
||||
public void setConnKeepTime(String connKeepTime) {
|
||||
setProperty(CONN_KEEP_TIME, connKeepTime);
|
||||
}
|
||||
|
||||
public String getConnAttamptMax() {
|
||||
return getPropertyAsString(CONN_ATTAMPT_MAX, DEFAULT_CONN_ATTAMPT_MAX);
|
||||
}
|
||||
|
||||
public void setConnAttamptMax(String connAttamptMax) {
|
||||
setProperty(CONN_ATTAMPT_MAX, connAttamptMax);
|
||||
}
|
||||
|
||||
public String getConnReconnAttamptMax() {
|
||||
return getPropertyAsString(CONN_RECONN_ATTAMPT_MAX, DEFAULT_CONN_RECONN_ATTAMPT_MAX);
|
||||
}
|
||||
|
||||
public void setConnReconnAttamptMax(String connReconnAttamptMax) {
|
||||
setProperty(CONN_RECONN_ATTAMPT_MAX, connReconnAttamptMax);
|
||||
}
|
||||
|
||||
public String getUserNameProperty() {
|
||||
return getPropertyAsString(USER_NAME_AUTH, "").trim();
|
||||
}
|
||||
|
||||
private String userName;
|
||||
|
||||
public String getUserNameAuth() {
|
||||
if (userName == null) {
|
||||
String prefix = getUserNameProperty();
|
||||
if (isUserNamePrefix()) {
|
||||
userName = Util.generateClientId(prefix);
|
||||
} else {
|
||||
userName = prefix;
|
||||
}
|
||||
}
|
||||
return userName;
|
||||
}
|
||||
|
||||
|
||||
public void setUserNameAuth(String userName) {
|
||||
setProperty(USER_NAME_AUTH, userName);
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public boolean isUserNamePrefix() {
|
||||
return getPropertyAsBoolean(USER_NAME_PREFIX);
|
||||
}
|
||||
|
||||
public void setUserNamePrefix(boolean prefix) {
|
||||
setProperty(USER_NAME_PREFIX, prefix);
|
||||
}
|
||||
|
||||
public boolean isKeepTimeShow() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isConnectionShareShow() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private String clientId;
|
||||
|
||||
protected String getClientId() {
|
||||
if (clientId == null) {
|
||||
if (isClientIdSuffix()) {
|
||||
clientId = Util.generateClientId(getConnPrefix());
|
||||
} else {
|
||||
clientId = getConnPrefix();
|
||||
}
|
||||
}
|
||||
return clientId;
|
||||
}
|
||||
|
||||
protected String mqttServerIp;
|
||||
protected long mqttServerPort;
|
||||
private static byte[] commonSecret= {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F};
|
||||
protected String privateSecret;
|
||||
protected String token;
|
||||
|
||||
protected boolean getToken(String userId, String clientId) {
|
||||
String url = "http://" + getServer() + ":18080/admin/user/get_token";
|
||||
String adminSecret = "123456";
|
||||
HttpPost post = null;
|
||||
try {
|
||||
HttpClient httpClient = HttpClientBuilder.create().build();
|
||||
|
||||
int nonce = (int)(Math.random() * 100000 + 3);
|
||||
long timestamp = System.currentTimeMillis();
|
||||
String str = nonce + "|" + adminSecret + "|" + timestamp;
|
||||
String sign = DigestUtils.sha1Hex(str);
|
||||
|
||||
|
||||
post = new HttpPost(url);
|
||||
post.setHeader("Content-type", "application/json; charset=utf-8");
|
||||
post.setHeader("Connection", "Keep-Alive");
|
||||
post.setHeader("nonce", nonce + "");
|
||||
post.setHeader("timestamp", "" + timestamp);
|
||||
post.setHeader("sign", sign);
|
||||
|
||||
String jsonStr = new Gson().toJson(new InputGetToken(userId, clientId));
|
||||
logger.info("http request content: " + jsonStr);
|
||||
|
||||
StringEntity entity = new StringEntity(jsonStr, Charset.forName("UTF-8"));
|
||||
entity.setContentEncoding("UTF-8");
|
||||
entity.setContentType("application/json");
|
||||
post.setEntity(entity);
|
||||
HttpResponse response = httpClient.execute(post);
|
||||
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if(statusCode != HttpStatus.SC_OK){
|
||||
logger.info("Request error: " + statusCode);
|
||||
throw new Exception("Http request error with code:" + statusCode);
|
||||
}else{
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(response.getEntity()
|
||||
.getContent(),"utf-8"));
|
||||
StringBuffer sb = new StringBuffer();
|
||||
String line;
|
||||
String NL = System.getProperty("line.separator");
|
||||
while ((line = in.readLine()) != null) {
|
||||
sb.append(line + NL);
|
||||
}
|
||||
|
||||
in.close();
|
||||
|
||||
String content = sb.toString();
|
||||
logger.info("http request response content: " + content);
|
||||
|
||||
IMResult<OutputGetIMTokenData> result = fromJsonObject(content, OutputGetIMTokenData.class);
|
||||
if (result == null || result.getCode() != IMResult.IMResultCode.IMRESULT_CODE_SUCCESS.code) {
|
||||
logger.error("get token result failure");
|
||||
return false;
|
||||
}
|
||||
|
||||
OutputGetIMTokenData out = result.getResult();
|
||||
token = out.getToken();
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}finally{
|
||||
if(post != null){
|
||||
post.releaseConnection();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static <T> IMResult<T> fromJsonObject(String content, Class<T> clazz) {
|
||||
Type type = ikidou.reflect.TypeBuilder
|
||||
.newInstance(IMResult.class)
|
||||
.addTypeParam(clazz)
|
||||
.build();
|
||||
return new Gson().fromJson(content, type);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package net.xmeter.samplers;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import cn.wildfirechat.client.IMClient;
|
||||
|
||||
public class ClientManager {
|
||||
private ConcurrentHashMap<String, IMClient> clients = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
private static ClientManager clientManager = new ClientManager();
|
||||
private ClientManager() {
|
||||
|
||||
}
|
||||
|
||||
public static synchronized ClientManager getInstance() {
|
||||
return clientManager;
|
||||
}
|
||||
|
||||
public void putClient(String key, IMClient client) {
|
||||
clients.put(key, client);
|
||||
}
|
||||
|
||||
public IMClient getClient(String key) {
|
||||
return this.clients.get(key);
|
||||
}
|
||||
|
||||
|
||||
public void removeClient(String key) {
|
||||
this.clients.remove(key);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
package net.xmeter.samplers;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import cn.wildfirechat.client.IMClient;
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import io.moquette.spi.impl.security.AES;
|
||||
import org.apache.jmeter.JMeter;
|
||||
import org.apache.jmeter.samplers.Entry;
|
||||
import org.apache.jmeter.samplers.Interruptible;
|
||||
import org.apache.jmeter.samplers.SampleEvent;
|
||||
import org.apache.jmeter.samplers.SampleListener;
|
||||
import org.apache.jmeter.samplers.SampleResult;
|
||||
import org.apache.jmeter.testelement.TestStateListener;
|
||||
import org.apache.jmeter.testelement.ThreadListener;
|
||||
import org.apache.jorphan.logging.LoggingManager;
|
||||
import org.apache.log.Logger;
|
||||
import org.apache.log.Priority;
|
||||
import org.fusesource.hawtbuf.UTF8Buffer;
|
||||
import org.fusesource.mqtt.client.Future;
|
||||
import org.fusesource.mqtt.client.FutureConnection;
|
||||
import org.fusesource.mqtt.client.MQTT;
|
||||
|
||||
import static cn.wildfirechat.client.IMClient.ConnectionStatus.ConnectionStatus_Connected;
|
||||
import static cn.wildfirechat.client.IMClient.ConnectionStatus.ConnectionStatus_Connecting;
|
||||
|
||||
public class ConnectionSampler extends AbstractMQTTSampler
|
||||
implements TestStateListener, ThreadListener, Interruptible, SampleListener {
|
||||
private transient static Logger logger = LoggingManager.getLoggerForClass();
|
||||
private transient FutureConnection connection = null;
|
||||
private boolean interrupt = false;
|
||||
//Declare it as static, for the instance variable will be set to initial value 0 in testEnded method.
|
||||
//The static value will not be reset.
|
||||
private static int keepTime = 0;
|
||||
|
||||
private static AtomicBoolean sleepFlag = new AtomicBoolean(false);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1859006013465470528L;
|
||||
|
||||
@Override
|
||||
public boolean isKeepTimeShow() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SampleResult sample(Entry entry) {
|
||||
SampleResult result = new SampleResult();
|
||||
result.setSampleLabel(getName());
|
||||
try {
|
||||
result.sampleStart();
|
||||
|
||||
if (!getToken(getUserNameAuth(), getClientId())) {
|
||||
throw new Exception("get token failure!!!");
|
||||
}
|
||||
|
||||
IMClient client = new IMClient(getUserNameAuth(), token, getClientId(), getServer(), Integer.parseInt(getPort()));
|
||||
|
||||
final Object lock = new Object();
|
||||
|
||||
|
||||
final List<Boolean> ret = new ArrayList<>();
|
||||
client.setConnectionStatusCallback(new IMClient.ConnectionStatusCallback() {
|
||||
@Override
|
||||
public void onConnectionStatusChanged(IMClient.ConnectionStatus newStatus) {
|
||||
if (newStatus != ConnectionStatus_Connected && newStatus != ConnectionStatus_Connecting) {
|
||||
synchronized (lock) {
|
||||
ret.add(true);
|
||||
lock.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
synchronized (lock) {
|
||||
client.connect();
|
||||
lock.wait(Integer.parseInt(getConnKeepTime()) * 1000);
|
||||
}
|
||||
|
||||
result.sampleEnd();
|
||||
if (ret.size() > 0) {
|
||||
result.setSuccessful(false);
|
||||
result.setResponseMessage(MessageFormat.format("Connection {0} connected failed.", connection));
|
||||
result.setResponseData("Failed.".getBytes());
|
||||
result.setResponseCode("500");
|
||||
} else {
|
||||
result.setSuccessful(true);
|
||||
result.setResponseData("Successful.".getBytes());
|
||||
result.setResponseMessage(MessageFormat.format("Connection {0} connected successfully.", connection));
|
||||
result.setResponseCodeOK();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.log(Priority.ERROR, e.getMessage(), e);
|
||||
result.sampleEnd();
|
||||
result.setSuccessful(false);
|
||||
result.setResponseMessage(MessageFormat.format("Connection {0} connected failed.", connection));
|
||||
result.setResponseData("Failed.".getBytes());
|
||||
result.setResponseCode("500");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testEnded() {
|
||||
this.testEnded("local");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testEnded(String arg0) {
|
||||
logger.info("in testEnded, isNonGUI=" + JMeter.isNonGUI() + ", sleepFlag=" + sleepFlag.get());
|
||||
this.interrupt = true;
|
||||
|
||||
try {
|
||||
if (JMeter.isNonGUI()) {
|
||||
if(!sleepFlag.get()) {
|
||||
//The keepTime is saved as static variable, because keepTime variable in testEnded()
|
||||
//returns with initial value.
|
||||
logger.info("The work has been done, will keep connection for " + keepTime + " sceconds.");
|
||||
TimeUnit.SECONDS.sleep(keepTime);
|
||||
sleepFlag.set(true);
|
||||
}
|
||||
|
||||
//The following code does not make sense, because the connection variable was already reset.
|
||||
//But it makes sense in threadFinished method.
|
||||
//The reason for not sleeping and then disconnecting in threadFinished is to avoid maintain too much of threads.
|
||||
//Otherwise each thread (for a thread-group) will be kept open before sleeping.
|
||||
/*if(this.connection != null) {
|
||||
this.connection.disconnect();
|
||||
logger.info(MessageFormat.format("The connection {0} disconneted successfully.", connection));
|
||||
}*/
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
logger.error(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testStarted() {
|
||||
this.testStarted("local");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testStarted(String arg0) {
|
||||
sleepFlag.set(false);
|
||||
keepTime = Integer.parseInt(getConnKeepTime());
|
||||
logger.info("*** Keeptime is: " + keepTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadFinished() {
|
||||
|
||||
}
|
||||
|
||||
private void sleepCurrentThreadAndDisconnect() {
|
||||
try {
|
||||
//If the connection is null or does not connect successfully, then not necessary to keep the connection.
|
||||
if(connection == null || (!connection.isConnected())) {
|
||||
if(connection == null) {
|
||||
logger.info("Connection is null.");
|
||||
} else if(!connection.isConnected()) {
|
||||
logger.info("Connection is created, but is not connected.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
long start = System.currentTimeMillis();
|
||||
while ((System.currentTimeMillis() - start) <= TimeUnit.SECONDS.toMillis(keepTime)) {
|
||||
if (this.interrupt) {
|
||||
logger.info("interrupted flag is true, and stop the sleep.");
|
||||
break;
|
||||
}
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.kill().await();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
logger.log(Priority.INFO, MessageFormat.format("The connection {0} disconneted successfully.", connection));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadStarted() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean interrupt() {
|
||||
this.interrupt = true;
|
||||
if (!JMeter.isNonGUI()) {
|
||||
logger.info("In GUI mode, received the interrupt request from user.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Note: JMeter.isNonGUI() only valid when you specify "-n" option from command line,
|
||||
// other cases (such as JMeter GUI, remote engine) all treat isNonGUI() as "false"
|
||||
// So, if you use remote engine, it's actually follow isNonGUI()=false path in this sampler code,
|
||||
// and you can control the test and stop it in JMeter GUI via say "Remote Stop All" button.
|
||||
//
|
||||
// You can also start remote agent with say "jmeter-server -DJMeter.NonGui=true",
|
||||
// in order to fool JMeter to treat isNonGUI() as "true".
|
||||
// However, the interrupt mechanism may not work well with remote engine when stop the remote test from JMeter GUI,
|
||||
// you may need to manually kill remote jmeter process to clean it up.
|
||||
//
|
||||
|
||||
/**
|
||||
* In this listener, it can receive the interrupt event trigger by user.
|
||||
*/
|
||||
@Override
|
||||
public void sampleOccurred(SampleEvent event) {
|
||||
if (!JMeter.isNonGUI() && "Wildfire Connection Sampler".equals(event.getResult().getSampleLabel() )) {
|
||||
logger.info("Created the sampler results, will sleep current thread for " + getConnKeepTime() + " sceconds");
|
||||
sleepCurrentThreadAndDisconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sampleStarted(SampleEvent arg0) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sampleStopped(SampleEvent arg0) {
|
||||
}
|
||||
}
|
||||
@@ -1,300 +0,0 @@
|
||||
package net.xmeter.samplers;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import cn.wildfirechat.client.IMClient;
|
||||
import cn.wildfirechat.proto.ProtoConstants;
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import io.moquette.spi.impl.security.AES;
|
||||
import org.fusesource.mqtt.client.MQTT;
|
||||
import org.apache.jmeter.samplers.Entry;
|
||||
import org.apache.jmeter.samplers.SampleResult;
|
||||
import org.apache.jmeter.testelement.ThreadListener;
|
||||
import org.apache.jorphan.logging.LoggingManager;
|
||||
import org.apache.log.Logger;
|
||||
import org.apache.log.Priority;
|
||||
import org.fusesource.hawtbuf.Buffer;
|
||||
import org.fusesource.hawtbuf.UTF8Buffer;
|
||||
|
||||
import net.xmeter.Util;
|
||||
import org.fusesource.mqtt.client.Callback;
|
||||
import org.fusesource.mqtt.client.Listener;
|
||||
import org.fusesource.mqtt.client.QoS;
|
||||
|
||||
import static cn.wildfirechat.client.IMClient.ConnectionStatus.ConnectionStatus_Connected;
|
||||
import static cn.wildfirechat.client.IMClient.ConnectionStatus.ConnectionStatus_Connecting;
|
||||
|
||||
|
||||
public class SendMessageSampler extends AbstractMQTTSampler implements ThreadListener {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -4312341622759500786L;
|
||||
private transient static Logger logger = LoggingManager.getLoggerForClass();
|
||||
private transient MQTT mqtt = new MQTT();
|
||||
private transient IMClient imClient = null;
|
||||
private String payload = null;
|
||||
private QoS qos_enum = QoS.AT_MOST_ONCE;
|
||||
private String topicName = "";
|
||||
private String clientKey = "";
|
||||
|
||||
public String getQOS() {
|
||||
return getPropertyAsString(QOS_LEVEL, String.valueOf(QOS_0));
|
||||
}
|
||||
|
||||
public void setQOS(String qos) {
|
||||
setProperty(QOS_LEVEL, qos);
|
||||
}
|
||||
|
||||
public String getTopic() {
|
||||
return getPropertyAsString(TOPIC_NAME, DEFAULT_TOPIC_NAME);
|
||||
}
|
||||
|
||||
public void setTopic(String topicName) {
|
||||
setProperty(TOPIC_NAME, topicName);
|
||||
}
|
||||
|
||||
public void setTarget(String target) {
|
||||
setProperty(TARGET, target);
|
||||
}
|
||||
|
||||
public String getTarget() {
|
||||
return getPropertyAsString(TARGET, DEFAULT_TARGET);
|
||||
}
|
||||
|
||||
public void setConvType(String type) {
|
||||
setProperty(CONV_TYPE, type);
|
||||
}
|
||||
|
||||
public String getConvType() {
|
||||
return getPropertyAsString(CONV_TYPE, CONV_TYPE_SINGLE);
|
||||
}
|
||||
|
||||
public boolean isAddTimestamp() {
|
||||
return getPropertyAsBoolean(ADD_TIMESTAMP);
|
||||
}
|
||||
|
||||
public void setAddTimestamp(boolean addTimestamp) {
|
||||
setProperty(ADD_TIMESTAMP, addTimestamp);
|
||||
}
|
||||
|
||||
public String getMessageType() {
|
||||
return getPropertyAsString(MESSAGE_TYPE, MESSAGE_TYPE_RANDOM_STR_WITH_FIX_LEN);
|
||||
}
|
||||
|
||||
public void setMessageType(String messageType) {
|
||||
setProperty(MESSAGE_TYPE, messageType);
|
||||
}
|
||||
|
||||
public String getMessageLength() {
|
||||
return getPropertyAsString(MESSAGE_FIX_LENGTH, DEFAULT_MESSAGE_FIX_LENGTH);
|
||||
}
|
||||
|
||||
public void setMessageLength(String length) {
|
||||
setProperty(MESSAGE_FIX_LENGTH, length);
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return getPropertyAsString(MESSAGE_TO_BE_SENT, "hello");
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
setProperty(MESSAGE_TO_BE_SENT, message);
|
||||
}
|
||||
|
||||
public String getConnPrefix() {
|
||||
return getPropertyAsString(CONN_CLIENT_ID_PREFIX, DEFAULT_CONN_PREFIX_FOR_PUB);
|
||||
}
|
||||
|
||||
public static byte[] hexToBinary(String hex) {
|
||||
return DatatypeConverter.parseHexBinary(hex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnectionShareShow() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getKey() {
|
||||
String key = getThreadName();
|
||||
if(!isConnectionShare()) {
|
||||
key = new String(getThreadName() + this.hashCode());
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SampleResult sample(Entry arg0) {
|
||||
this.clientKey = getKey();
|
||||
|
||||
imClient = ClientManager.getInstance().getClient(clientKey);
|
||||
if(imClient == null) {
|
||||
try {
|
||||
if (!getToken(getUserNameAuth(), getClientId())) {
|
||||
throw new Exception("get token failure!!!");
|
||||
}
|
||||
|
||||
imClient = new IMClient(getUserNameAuth(), token, getClientId(), getServer(), Integer.parseInt(getPort()));
|
||||
|
||||
final Object lock = new Object();
|
||||
|
||||
|
||||
final List<Boolean> ret = new ArrayList<>();
|
||||
imClient.setConnectionStatusCallback(new IMClient.ConnectionStatusCallback() {
|
||||
@Override
|
||||
public void onConnectionStatusChanged(IMClient.ConnectionStatus newStatus) {
|
||||
if (newStatus == ConnectionStatus_Connected) {
|
||||
synchronized (lock) {
|
||||
ret.add(true);
|
||||
lock.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
synchronized (lock) {
|
||||
imClient.connect();
|
||||
lock.wait(Integer.parseInt(getConnKeepTime()) * 1000);
|
||||
}
|
||||
|
||||
ClientManager.getInstance().putClient(clientKey, imClient);
|
||||
} catch (Exception e) {
|
||||
logger.log(Priority.ERROR, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
payload = getMessage();
|
||||
if(payload == null) {
|
||||
payload = Util.generatePayload(120);
|
||||
}
|
||||
|
||||
final SampleResult result = new SampleResult();
|
||||
result.setSampleLabel(getName());
|
||||
|
||||
if(ClientManager.getInstance().getClient(clientKey) == null) {
|
||||
result.sampleStart();
|
||||
result.setSuccessful(false);
|
||||
result.sampleEnd();
|
||||
result.setResponseMessage(MessageFormat.format("Publish failed for imClient {0}.", imClient));
|
||||
result.setResponseData("Publish failed becasue the imClient has not been established.".getBytes());
|
||||
result.setResponseCode("500");
|
||||
return result;
|
||||
}
|
||||
try {
|
||||
result.sampleStart();
|
||||
|
||||
final Object lock = new Object();
|
||||
int converType = 0;
|
||||
if ("1".equals(getConvType())) {
|
||||
converType = 1;
|
||||
}
|
||||
WFCMessage.Conversation conversation = WFCMessage.Conversation.newBuilder().setType(converType).setTarget(getTarget()).setLine(0).build();
|
||||
WFCMessage.MessageContent messageContent = WFCMessage.MessageContent.newBuilder().setType(1).setSearchableContent(getMessage()).build();
|
||||
|
||||
final List<Long> ret = new ArrayList<>();
|
||||
final List<Integer> error = new ArrayList<>();
|
||||
synchronized (lock) {
|
||||
imClient.sendMessage(conversation, messageContent, new IMClient.SendMessageCallback() {
|
||||
@Override
|
||||
public void onSuccess(long messageUid, long timestamp) {
|
||||
System.out.println("send success");
|
||||
ret.add(messageUid);
|
||||
synchronized (lock) {
|
||||
lock.notify();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(int errorCode) {
|
||||
System.out.println("send failure");
|
||||
error.add(errorCode);
|
||||
synchronized (lock) {
|
||||
lock.notify();
|
||||
}
|
||||
}
|
||||
});
|
||||
lock.wait(30 * 1000);
|
||||
}
|
||||
|
||||
|
||||
result.sampleEnd();
|
||||
result.setSamplerData(getMessage());
|
||||
result.setSentBytes(getMessage().length());
|
||||
result.setLatency(result.getEndTime() - result.getStartTime());
|
||||
result.setSuccessful(!ret.isEmpty());
|
||||
|
||||
if(result.isSuccessful()) {
|
||||
result.setResponseData("Publish successfuly.".getBytes());
|
||||
result.setResponseMessage(MessageFormat.format("publish successfully for Connection {0}.", imClient));
|
||||
result.setResponseCodeOK();
|
||||
} else {
|
||||
result.setSuccessful(false);
|
||||
result.setResponseMessage(MessageFormat.format("Publish failed for imClient {0}.", imClient));
|
||||
result.setResponseData("Publish failed with error code " + error.get(0));
|
||||
result.setResponseCode("500");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
logger.log(Priority.ERROR, ex.getMessage(), ex);
|
||||
result.sampleEnd();
|
||||
result.setLatency(result.getEndTime() - result.getStartTime());
|
||||
result.setSuccessful(false);
|
||||
result.setResponseMessage(MessageFormat.format("Publish failed for imClient {0}.", imClient));
|
||||
result.setResponseData(ex.getMessage().getBytes());
|
||||
result.setResponseCode("500");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadStarted() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadFinished() {
|
||||
if (!isConnectionShare()) {
|
||||
if (this.imClient != null) {
|
||||
final CountDownLatch cdl = new CountDownLatch(1);
|
||||
Callback<Void> cb = new Callback<Void>() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void value) {
|
||||
logger.info(MessageFormat.format("The imClient {0} disconneted successfully.", imClient));
|
||||
cdl.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable value) {
|
||||
logger.log(Priority.ERROR, value.getMessage(), value);
|
||||
cdl.countDown();
|
||||
}
|
||||
};
|
||||
logger.info("before disconnection");
|
||||
this.imClient.disconnect(true, cb);
|
||||
logger.info("after disconnection");
|
||||
try {
|
||||
cdl.await();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
ClientManager.getInstance().removeClient(clientKey);
|
||||
} else {
|
||||
logger.info("share imClient, not disconnect the imClient");
|
||||
}
|
||||
}
|
||||
public static String getUUID() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
String str = uuid.toString();
|
||||
|
||||
str = str.replace("-", "");
|
||||
return str;
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<jmeterTestPlan version="1.2" properties="3.2" jmeter="3.3 r1808647">
|
||||
<hashTree>
|
||||
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
|
||||
<stringProp name="TestPlan.comments"></stringProp>
|
||||
<boolProp name="TestPlan.functional_mode">false</boolProp>
|
||||
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
|
||||
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="TestPlan.user_define_classpath"></stringProp>
|
||||
</TestPlan>
|
||||
<hashTree>
|
||||
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
|
||||
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
||||
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
||||
<boolProp name="LoopController.continue_forever">false</boolProp>
|
||||
<stringProp name="LoopController.loops">1</stringProp>
|
||||
</elementProp>
|
||||
<stringProp name="ThreadGroup.num_threads">1</stringProp>
|
||||
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
|
||||
<longProp name="ThreadGroup.start_time">1550068120000</longProp>
|
||||
<longProp name="ThreadGroup.end_time">1550068120000</longProp>
|
||||
<boolProp name="ThreadGroup.scheduler">false</boolProp>
|
||||
<stringProp name="ThreadGroup.duration"></stringProp>
|
||||
<stringProp name="ThreadGroup.delay"></stringProp>
|
||||
</ThreadGroup>
|
||||
<hashTree>
|
||||
<net.xmeter.samplers.ConnectionSampler guiclass="net.xmeter.gui.ConnectionSamplerUI" testclass="net.xmeter.samplers.ConnectionSampler" testname="Wildfire Connection Sampler" enabled="true">
|
||||
<stringProp name="mqtt.server">127.0.0.1</stringProp>
|
||||
<stringProp name="mqtt.port">8080</stringProp>
|
||||
<boolProp name="mqtt.conn_share">true</boolProp>
|
||||
<stringProp name="mqtt.conn_timeout">10</stringProp>
|
||||
<stringProp name="mqtt.user_name">user_</stringProp>
|
||||
<boolProp name="mqtt.user_name_prefix">true</boolProp>
|
||||
<stringProp name="mqtt.client_id_prefix">conn_</stringProp>
|
||||
<boolProp name="mqtt.client_id_suffix">true</boolProp>
|
||||
<stringProp name="mqtt.conn_keep_alive">300</stringProp>
|
||||
<stringProp name="mqtt.keep_time">1800</stringProp>
|
||||
<stringProp name="mqtt.conn_attampt_max">0</stringProp>
|
||||
<stringProp name="mqtt.reconn_attampt_max">0</stringProp>
|
||||
</net.xmeter.samplers.ConnectionSampler>
|
||||
<hashTree>
|
||||
<ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="Aggregate Report" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
<ResultCollector guiclass="AssertionVisualizer" testclass="ResultCollector" testname="Assertion Results" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
</hashTree>
|
||||
</hashTree>
|
||||
<WorkBench guiclass="WorkBenchGui" testclass="WorkBench" testname="WorkBench" enabled="true">
|
||||
<boolProp name="WorkBench.save">true</boolProp>
|
||||
</WorkBench>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
</jmeterTestPlan>
|
||||
@@ -1,126 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<jmeterTestPlan version="1.2" properties="3.2" jmeter="3.3 r1808647">
|
||||
<hashTree>
|
||||
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
|
||||
<stringProp name="TestPlan.comments"></stringProp>
|
||||
<boolProp name="TestPlan.functional_mode">false</boolProp>
|
||||
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
|
||||
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="TestPlan.user_define_classpath"></stringProp>
|
||||
</TestPlan>
|
||||
<hashTree>
|
||||
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
|
||||
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
||||
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
||||
<boolProp name="LoopController.continue_forever">false</boolProp>
|
||||
<stringProp name="LoopController.loops">1</stringProp>
|
||||
</elementProp>
|
||||
<stringProp name="ThreadGroup.num_threads">1</stringProp>
|
||||
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
|
||||
<longProp name="ThreadGroup.start_time">1550068120000</longProp>
|
||||
<longProp name="ThreadGroup.end_time">1550068120000</longProp>
|
||||
<boolProp name="ThreadGroup.scheduler">false</boolProp>
|
||||
<stringProp name="ThreadGroup.duration"></stringProp>
|
||||
<stringProp name="ThreadGroup.delay"></stringProp>
|
||||
</ThreadGroup>
|
||||
<hashTree>
|
||||
<net.xmeter.samplers.SendMessageSampler guiclass="net.xmeter.gui.SendMessageSamplerUI" testclass="net.xmeter.samplers.SendMessageSampler" testname="Wildfire Send Message Sampler" enabled="true">
|
||||
<stringProp name="mqtt.server">192.168.31.98</stringProp>
|
||||
<stringProp name="mqtt.port">8080</stringProp>
|
||||
<boolProp name="mqtt.conn_share">true</boolProp>
|
||||
<stringProp name="mqtt.conn_timeout">10</stringProp>
|
||||
<stringProp name="mqtt.user_name">user_</stringProp>
|
||||
<boolProp name="mqtt.user_name_prefix">true</boolProp>
|
||||
<stringProp name="mqtt.client_id_prefix">pub_</stringProp>
|
||||
<boolProp name="mqtt.client_id_suffix">true</boolProp>
|
||||
<stringProp name="mqtt.conn_keep_alive">300</stringProp>
|
||||
<stringProp name="mqtt.keep_time">1800</stringProp>
|
||||
<stringProp name="mqtt.conn_attampt_max">0</stringProp>
|
||||
<stringProp name="mqtt.reconn_attampt_max">0</stringProp>
|
||||
<stringProp name="wildfire.target">test_topic</stringProp>
|
||||
<stringProp name="mqtt.message_to_sent">test message content</stringProp>
|
||||
</net.xmeter.samplers.SendMessageSampler>
|
||||
<hashTree>
|
||||
<ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="Aggregate Report" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
<ResultCollector guiclass="AssertionVisualizer" testclass="ResultCollector" testname="Assertion Results" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
</hashTree>
|
||||
</hashTree>
|
||||
<WorkBench guiclass="WorkBenchGui" testclass="WorkBench" testname="WorkBench" enabled="true">
|
||||
<boolProp name="WorkBench.save">true</boolProp>
|
||||
</WorkBench>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
</jmeterTestPlan>
|
||||
@@ -1,169 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<jmeterTestPlan version="1.2" properties="3.2" jmeter="3.2 r1790748">
|
||||
<hashTree>
|
||||
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="测试计划" enabled="true">
|
||||
<stringProp name="TestPlan.comments"></stringProp>
|
||||
<boolProp name="TestPlan.functional_mode">false</boolProp>
|
||||
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
|
||||
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="TestPlan.user_define_classpath"></stringProp>
|
||||
</TestPlan>
|
||||
<hashTree>
|
||||
<Arguments guiclass="ArgumentsPanel" testclass="Arguments" testname="xmeter_runtime_vars" enabled="true">
|
||||
<collectionProp name="Arguments.arguments">
|
||||
<elementProp name="server" elementType="Argument">
|
||||
<stringProp name="Argument.name">server</stringProp>
|
||||
<stringProp name="Argument.value">10.91.41.18</stringProp>
|
||||
<stringProp name="Argument.desc">emq server IP</stringProp>
|
||||
<stringProp name="Argument.metadata">=</stringProp>
|
||||
</elementProp>
|
||||
<elementProp name="port" elementType="Argument">
|
||||
<stringProp name="Argument.name">port</stringProp>
|
||||
<stringProp name="Argument.value">1883</stringProp>
|
||||
<stringProp name="Argument.desc">emq server port</stringProp>
|
||||
<stringProp name="Argument.metadata">=</stringProp>
|
||||
</elementProp>
|
||||
<elementProp name="protocol" elementType="Argument">
|
||||
<stringProp name="Argument.name">protocol</stringProp>
|
||||
<stringProp name="Argument.value">TCP</stringProp>
|
||||
<stringProp name="Argument.metadata">=</stringProp>
|
||||
<stringProp name="Argument.desc">TCP or SSL</stringProp>
|
||||
</elementProp>
|
||||
<elementProp name="keep_alive" elementType="Argument">
|
||||
<stringProp name="Argument.name">keep_alive</stringProp>
|
||||
<stringProp name="Argument.value">300</stringProp>
|
||||
<stringProp name="Argument.metadata">=</stringProp>
|
||||
<stringProp name="Argument.desc">time interval to send heartbeat</stringProp>
|
||||
</elementProp>
|
||||
<elementProp name="conn_keep" elementType="Argument">
|
||||
<stringProp name="Argument.name">conn_keep</stringProp>
|
||||
<stringProp name="Argument.value">1800</stringProp>
|
||||
<stringProp name="Argument.desc">connection elapse time</stringProp>
|
||||
<stringProp name="Argument.metadata">=</stringProp>
|
||||
</elementProp>
|
||||
</collectionProp>
|
||||
</Arguments>
|
||||
<hashTree/>
|
||||
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="连接线程组" enabled="true">
|
||||
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
||||
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="循环控制器" enabled="true">
|
||||
<boolProp name="LoopController.continue_forever">false</boolProp>
|
||||
<stringProp name="LoopController.loops">1</stringProp>
|
||||
</elementProp>
|
||||
<stringProp name="ThreadGroup.num_threads">2</stringProp>
|
||||
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
|
||||
<longProp name="ThreadGroup.start_time">1487646110000</longProp>
|
||||
<longProp name="ThreadGroup.end_time">1487646110000</longProp>
|
||||
<boolProp name="ThreadGroup.scheduler">false</boolProp>
|
||||
<stringProp name="ThreadGroup.duration"></stringProp>
|
||||
<stringProp name="ThreadGroup.delay"></stringProp>
|
||||
<boolProp name="ThreadGroup.delayedStart">true</boolProp>
|
||||
</ThreadGroup>
|
||||
<hashTree>
|
||||
<net.xmeter.samplers.ConnectionSampler guiclass="net.xmeter.gui.ConnectionSamplerUI" testclass="net.xmeter.samplers.ConnectionSampler" testname="Wildfire Connection Sampler" enabled="true">
|
||||
<stringProp name="mqtt.keystore_file_path"></stringProp>
|
||||
<stringProp name="mqtt.clientcert_file_path"></stringProp>
|
||||
<stringProp name="mqtt.conn_keep_alive">${keep_alive}</stringProp>
|
||||
<stringProp name="mqtt.conn_attampt_max">0</stringProp>
|
||||
<stringProp name="mqtt.keep_time">${conn_keep}</stringProp>
|
||||
<stringProp name="mqtt.client_id_prefix">conn_</stringProp>
|
||||
<stringProp name="mqtt.reconn_attampt_max">0</stringProp>
|
||||
<stringProp name="mqtt.conn_timeout">10</stringProp>
|
||||
<boolProp name="mqtt.dual_ssl_authentication">false</boolProp>
|
||||
<stringProp name="mqtt.keystore_password"></stringProp>
|
||||
<stringProp name="mqtt.clientcert_password"></stringProp>
|
||||
<stringProp name="mqtt.port">${port}</stringProp>
|
||||
<stringProp name="mqtt.protocol">${protocol}</stringProp>
|
||||
<stringProp name="mqtt.server">${server}</stringProp>
|
||||
<stringProp name="mqtt.user_name"></stringProp>
|
||||
<stringProp name="mqtt.password"></stringProp>
|
||||
<boolProp name="mqtt.client_id_suffix">true</boolProp>
|
||||
</net.xmeter.samplers.ConnectionSampler>
|
||||
<hashTree>
|
||||
<ConstantTimer guiclass="ConstantTimerGui" testclass="ConstantTimer" testname="固定定时器" enabled="true">
|
||||
<stringProp name="ConstantTimer.delay">300</stringProp>
|
||||
</ConstantTimer>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
</hashTree>
|
||||
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="察看结果树" enabled="false">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
<ResultCollector guiclass="TableVisualizer" testclass="ResultCollector" testname="用表格察看结果" enabled="false">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
<WorkBench guiclass="WorkBenchGui" testclass="WorkBench" testname="工作台" enabled="true">
|
||||
<boolProp name="WorkBench.save">true</boolProp>
|
||||
</WorkBench>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
</jmeterTestPlan>
|
||||
@@ -1,124 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<jmeterTestPlan version="1.2" properties="3.2" jmeter="3.3 r1808647">
|
||||
<hashTree>
|
||||
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
|
||||
<stringProp name="TestPlan.comments"></stringProp>
|
||||
<boolProp name="TestPlan.functional_mode">false</boolProp>
|
||||
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
|
||||
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="TestPlan.user_define_classpath"></stringProp>
|
||||
</TestPlan>
|
||||
<hashTree>
|
||||
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
|
||||
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
||||
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
||||
<boolProp name="LoopController.continue_forever">false</boolProp>
|
||||
<stringProp name="LoopController.loops">1</stringProp>
|
||||
</elementProp>
|
||||
<stringProp name="ThreadGroup.num_threads">100</stringProp>
|
||||
<stringProp name="ThreadGroup.ramp_time">10</stringProp>
|
||||
<longProp name="ThreadGroup.start_time">1557848062000</longProp>
|
||||
<longProp name="ThreadGroup.end_time">1557848062000</longProp>
|
||||
<boolProp name="ThreadGroup.scheduler">false</boolProp>
|
||||
<stringProp name="ThreadGroup.duration"></stringProp>
|
||||
<stringProp name="ThreadGroup.delay"></stringProp>
|
||||
</ThreadGroup>
|
||||
<hashTree>
|
||||
<net.xmeter.samplers.ConnectionSampler guiclass="net.xmeter.gui.ConnectionSamplerUI" testclass="net.xmeter.samplers.ConnectionSampler" testname="Wildfire Connection Sampler" enabled="true">
|
||||
<stringProp name="mqtt.server">127.0.0.1</stringProp>
|
||||
<stringProp name="mqtt.port">80</stringProp>
|
||||
<boolProp name="mqtt.conn_share">true</boolProp>
|
||||
<stringProp name="mqtt.conn_timeout">10</stringProp>
|
||||
<stringProp name="mqtt.user_name">test</stringProp>
|
||||
<boolProp name="mqtt.user_name_prefix">true</boolProp>
|
||||
<stringProp name="mqtt.client_id_prefix">conn_</stringProp>
|
||||
<boolProp name="mqtt.client_id_suffix">true</boolProp>
|
||||
<stringProp name="mqtt.conn_keep_alive">300</stringProp>
|
||||
<stringProp name="mqtt.keep_time">1800</stringProp>
|
||||
<stringProp name="mqtt.conn_attampt_max">0</stringProp>
|
||||
<stringProp name="mqtt.reconn_attampt_max">0</stringProp>
|
||||
</net.xmeter.samplers.ConnectionSampler>
|
||||
<hashTree>
|
||||
<ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="Aggregate Report" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
</hashTree>
|
||||
</hashTree>
|
||||
<WorkBench guiclass="WorkBenchGui" testclass="WorkBench" testname="WorkBench" enabled="true">
|
||||
<boolProp name="WorkBench.save">true</boolProp>
|
||||
</WorkBench>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
</jmeterTestPlan>
|
||||
@@ -1,126 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<jmeterTestPlan version="1.2" properties="3.2" jmeter="3.3 r1808647">
|
||||
<hashTree>
|
||||
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
|
||||
<stringProp name="TestPlan.comments"></stringProp>
|
||||
<boolProp name="TestPlan.functional_mode">false</boolProp>
|
||||
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
|
||||
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="TestPlan.user_define_classpath"></stringProp>
|
||||
</TestPlan>
|
||||
<hashTree>
|
||||
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
|
||||
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
||||
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
||||
<boolProp name="LoopController.continue_forever">false</boolProp>
|
||||
<stringProp name="LoopController.loops">10</stringProp>
|
||||
</elementProp>
|
||||
<stringProp name="ThreadGroup.num_threads">1</stringProp>
|
||||
<stringProp name="ThreadGroup.ramp_time">10</stringProp>
|
||||
<longProp name="ThreadGroup.start_time">1557931868000</longProp>
|
||||
<longProp name="ThreadGroup.end_time">1557931868000</longProp>
|
||||
<boolProp name="ThreadGroup.scheduler">false</boolProp>
|
||||
<stringProp name="ThreadGroup.duration"></stringProp>
|
||||
<stringProp name="ThreadGroup.delay"></stringProp>
|
||||
</ThreadGroup>
|
||||
<hashTree>
|
||||
<net.xmeter.samplers.SendMessageSampler guiclass="net.xmeter.gui.SendMessageSamplerUI" testclass="net.xmeter.samplers.SendMessageSampler" testname="Wildfire Send Message Sampler" enabled="true">
|
||||
<stringProp name="mqtt.server">127.0.0.1</stringProp>
|
||||
<stringProp name="mqtt.port">80</stringProp>
|
||||
<boolProp name="mqtt.conn_share">true</boolProp>
|
||||
<stringProp name="mqtt.conn_timeout">10</stringProp>
|
||||
<stringProp name="mqtt.user_name">test_</stringProp>
|
||||
<boolProp name="mqtt.user_name_prefix">true</boolProp>
|
||||
<stringProp name="mqtt.client_id_prefix">client_</stringProp>
|
||||
<boolProp name="mqtt.client_id_suffix">true</boolProp>
|
||||
<stringProp name="mqtt.conn_keep_alive">300</stringProp>
|
||||
<stringProp name="mqtt.keep_time">1800</stringProp>
|
||||
<stringProp name="mqtt.conn_attampt_max">0</stringProp>
|
||||
<stringProp name="mqtt.reconn_attampt_max">0</stringProp>
|
||||
<stringProp name="wildfire.target">test_topic</stringProp>
|
||||
<stringProp name="mqtt.message_to_sent">hello from jmeter</stringProp>
|
||||
</net.xmeter.samplers.SendMessageSampler>
|
||||
<hashTree>
|
||||
<ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="Aggregate Report" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
</hashTree>
|
||||
</hashTree>
|
||||
<WorkBench guiclass="WorkBenchGui" testclass="WorkBench" testname="WorkBench" enabled="true">
|
||||
<boolProp name="WorkBench.save">true</boolProp>
|
||||
</WorkBench>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
</jmeterTestPlan>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user