Compare commits

...
103 Commits
Author SHA1 Message Date
heavyrain2012 b3ddda78a1 update version to 0.29 2019-10-23 21:07:43 +08:00
heavyrain2012 066e74be5f fix sdk error 2019-10-23 21:04:27 +08:00
heavyrain.lee f9c2cf8e31 update 2019-10-22 11:52:11 +08:00
heavyrain.lee d3fc625299 update 2019-10-22 09:57:26 +08:00
imndx 905763b6e3 update readme 2019-10-19 17:35:45 +08:00
imndx 877b7bc217 fix typo 2019-10-19 17:35:19 +08:00
heavyrain.lee d8b7f6a8de Update README.md 2019-10-11 16:12:48 +08:00
heavyrain.lee 579d3b529f 更新说明 2019-10-10 20:16:36 +08:00
heavyrain.lee 065a79adc3 调整代码 2019-10-10 20:14:17 +08:00
imndx 5ed45ef414 fix message content issue. fix wildfirechat/android-chat#184 2019-10-10 15:35:03 +08:00
heavyrain.lee ba671b3370 update 2019-10-09 20:22:02 +08:00
heavyrain.lee e975f20a6b alter voip token length to 240 2019-10-09 20:21:02 +08:00
heavyrain.lee 70bdebd4a0 update version to 0.27 2019-09-24 08:26:35 +08:00
heavyrain.lee 409078801e update 2019-09-24 08:23:25 +08:00
heavyrain2012 06fed7de52 update version to 0.26 2019-09-13 23:12:05 +08:00
heavyrain2012 da2428a9f9 modify windows bash error 2019-09-13 23:09:29 +08:00
heavyrain.lee 83b6ae5229 修改pc等平台pushtype越界问题 2019-09-07 14:50:34 +08:00
heavyrain.lee 4b7804cdfd update 2019-09-02 21:54:03 +08:00
heavyrain.lee d075b5212f 修改用户信息和频道信息时,改成同步修改 2019-09-01 22:16:49 +08:00
heavyrain.lee 636c8ce219 管理员撤回消息 2019-08-30 22:17:17 +08:00
heavyrain.lee 091d7fa3ec 解决乱码问题 2019-08-24 17:56:29 +08:00
heavyrain.lee 59356e5b52 获取敏感词列表按照逆序排列 2019-08-23 11:38:42 +08:00
heavyrain.lee 882bf29e63 1,更新群主时更新群成员信息中的群主状态
2,在敏感词列表中添加id列
2019-08-23 08:39:29 +08:00
heavyrain.lee f3eef4dd6b 添加修改群组信息接口 2019-08-17 10:30:39 +08:00
heavyrain.lee 898983c6e5 rm useless code 2019-08-10 22:14:46 +08:00
heavyrain.lee dae5d032fa 添加拉取历史消息开关。设置默认消息漫游和拉取都为关闭,这样无法删除问题就不会再出现 2019-08-09 13:44:44 +08:00
heavyrain.lee ff76622796 1, 修改撤回逻辑 2019-08-09 09:00:14 +08:00
heavyrain.lee a6fd11a243 修复部分接口 2019-08-08 08:28:00 +08:00
heavyrain2012 cf983be44c Update README.md 2019-08-07 23:48:59 +08:00
heavyrain.lee 1a72de0e85 修改群组反复添加删除群成员的问题 2019-08-06 22:55:52 +08:00
heavyrain.lee bf70a1e80e 退出群组时,删掉群相关用户设置 2019-08-06 22:55:41 +08:00
heavyrain.lee 512d797d4d Revert "jdbc character set to utf8mb4"
This reverts commit 608184ef9e.
2019-08-03 22:27:29 +08:00
heavyrain.lee 4a46dd1dde 添加组播消息 2019-08-03 20:43:27 +08:00
heavyrain lee 7494cdc071 update 2019-08-02 13:24:30 +08:00
heavyrain.lee 608184ef9e jdbc character set to utf8mb4 2019-08-02 13:16:15 +08:00
heavyrain2012 eb4d8a9b68 Update README.md 2019-07-30 18:22:03 +08:00
heavyrain.lee 6b92ae383c add weixin qrcode 2019-07-30 18:20:27 +08:00
heavyrain.lee 5fee857c4a add system settings 2019-07-30 07:54:49 +08:00
heavyrain2012 53b7139c30 Update README.md 2019-07-29 15:37:27 +08:00
imndx 83e864d667 update readme 2019-07-28 19:15:26 +08:00
imndx b88297e6de update readme 2019-07-26 22:52:39 +08:00
heavyrain lee 4aa2f46011 添加默认敏感词 2019-07-23 08:26:16 +08:00
heavyrain lee 1fb3c757e2 撤回消息时,先删除旧消息,再插入新消息 2019-07-21 22:29:24 +08:00
heavyrain lee 3f640b6c14 sync with commercial 2019-07-21 15:11:24 +08:00
heavyrain lee 49eb57af00 update version to 0.25 2019-07-21 13:09:07 +08:00
heavyrain lee 6bd6a80e71 添加是否接收历史消息的开关 2019-07-21 12:20:26 +08:00
heavyrain lee 38b84f0fa5 解决封禁用户无法踢用户下线的问题 2019-07-21 12:18:38 +08:00
heavyrain lee 1f46debd0c Fix test case error 2019-07-21 11:44:54 +08:00
heavyrain.lee 37998e76d1 添加漫游开关 2019-07-19 20:52:25 +08:00
heavyrain.lee 80b5508533 添加消息漫游开关 2019-07-19 20:51:52 +08:00
heavyrain lee a646cc8227 去掉启动时预读数据,加快启动速度 2019-07-17 00:36:17 +08:00
heavyrain lee 92692adf52 解决群组成员反复添加删除错误问题 2019-07-17 00:31:21 +08:00
heavyrain lee b2cfc2b99b add metrics for user channel 2019-07-16 21:56:51 +08:00
heavyrain lee 86e08f6151 Remove useless code 2019-07-16 21:32:55 +08:00
heavyrain lee 8d852eb2d1 退出群组时,删掉收藏的群组 2019-07-15 22:36:19 +08:00
heavyrain lee 8bef018911 转让群主后更新群组信息时间戳 2019-07-13 18:50:23 +08:00
heavyrain lee 5e13419c28 修改群管理逻辑错误 2019-07-13 18:31:08 +08:00
heavyrain.lee 503a7ae18f 取消管理员 2019-07-12 09:17:11 +08:00
heavyrain.lee dfb9638a38 update version to 0.24 2019-07-11 23:22:03 +08:00
heavyrain.lee 8452b0675b java10 not support this parameter 2019-07-11 23:21:48 +08:00
heavyrain.lee 930643706a 数据库message表中时间加8 2019-07-11 21:56:31 +08:00
heavyrain.lee a2eac1f721 update 2019-07-11 17:56:23 +08:00
heavyrain.lee 6e994cd694 update 2019-07-11 17:41:32 +08:00
heavyrain.lee c82869c631 群主可以在群禁言时发信息 2019-07-10 15:08:10 +08:00
heavyrain.lee f5a94868c4 remove useless code 2019-07-10 07:30:53 +08:00
heavyrain.lee c5628d613b 发送消息时,不能发送特殊类型的消息 2019-07-09 23:22:52 +08:00
heavyrain.lee 9509b1ffa8 敏感词大小写的问题 2019-07-09 23:22:52 +08:00
heavyrain2012 c1dac8a21e Update README.md 2019-07-09 14:07:31 +08:00
heavyrain.lee 950846df45 update 2019-07-09 08:50:14 +08:00
heavyrain.lee 31d96e2dc6 update 2019-07-09 08:08:04 +08:00
heavyrain.lee 031e96075e 群组权限 2019-07-08 23:08:04 +08:00
heavyrain.lee bf2caeac6c update 2019-07-06 16:56:08 +08:00
heavyrain.lee 12e523b37e 好友同步问题 2019-07-05 17:38:08 +08:00
heavyrain lee 576c985d8f 聊天室历史数据问题 2019-07-05 08:47:44 +08:00
heavyrain lee 9365807f47 群组踢人错误修改 2019-07-05 08:19:29 +08:00
heavyrain lee 25ce10fa61 删除好友通知 2019-07-04 10:19:07 +08:00
heavyrain lee 09c333501f Update 2019-07-04 08:54:34 +08:00
heavyrain.lee c51b09ee3a 修改用户关系逻辑错误问题 2019-07-04 08:23:55 +08:00
imndx 4d82eace86 quit group, then send notification 2019-07-04 07:07:36 +08:00
heavyrain.lee 385b03a163 add session table 2019-07-03 08:04:07 +08:00
heavyrain.lee 4b438afa69 fix issue 2019-06-30 10:31:12 +08:00
imndx 66961b94da route接口允许跨域访问 2019-06-29 16:42:55 +08:00
heavyrain lee 39b62e5942 Update to version 0.23 2019-06-29 12:20:58 +08:00
heavyrain.lee 5bb8ab3d95 update 2019-06-29 12:01:46 +08:00
heavyrain.lee 8dfc23b61d update code 2019-06-29 12:01:37 +08:00
heavyrain lee c8769808b5 Update proto 2019-06-29 12:01:11 +08:00
imndx 2fc106aa26 web route时,返回数据base64编码 2019-06-28 17:27:37 +08:00
imndx da0b725ff8 解决内存中好友备注错误 2019-06-24 22:04:05 +08:00
heavyrain.lee 15703d66fb 封禁用户问题修改 2019-06-23 18:30:58 +08:00
heavyrain.lee c68beeaa51 解决java客户端收消息的问题 2019-06-20 15:24:32 +08:00
heavyrain.lee 5ef8868916 接受新消息通知错误 2019-06-19 14:18:09 +08:00
heavyrain lee 24488cc985 Add group manage info 2019-06-18 08:26:29 +08:00
heavyrain.lee 1c9faaa1e6 apns推送在dev环境下也有voip证书,因此去掉voip一定是pro的假设 2019-06-16 20:25:33 +08:00
heavyrain.lee ac32875b14 fix migration error 2019-06-16 19:57:46 +08:00
heavyrain.lee 07c1dd29e0 alter table of token 2019-06-14 20:04:21 +08:00
imndx bd93233c34 update device token length 2019-06-14 18:43:50 +08:00
heavyrain.lee 20aec2fbcf 添加默认管理员 2019-06-12 23:21:21 +08:00
heavyrain.lee a149139143 添加敏感词命中消息表t_sensitive_messages 2019-06-12 22:57:30 +08:00
heavyrain.lee c857d1abb7 消息表中添加contenttype 2019-06-12 22:36:08 +08:00
heavyrain lee dde9da7006 用户表和群组表添加create_time字段 2019-06-12 21:48:57 +08:00
heavyrain2012 d18a1970e5 update script 2019-06-11 22:46:06 +08:00
heavyrain.lee 66b9aeec17 包名错误 2019-06-11 14:45:58 +08:00
heavyrain.lee ea301a3923 修改内部接口定义 2019-06-09 07:37:33 +08:00
1077 changed files with 9838 additions and 444732 deletions
+2
View File
@@ -49,6 +49,8 @@ maven-metadata.xml
/bin/
broker/config/git.properties
distribution/src/main/resources/git.properties
license/target
server/target
broker/h2db
broker/media
common/target
+12 -6
View File
@@ -1,13 +1,12 @@
## 野火IM解决方案
野火IM是一套跨平台、全开源的即时通讯解决方案,主要包含以下内容。
野火IM是一套跨平台、核心功能开源的即时通讯解决方案,主要包含以下内容。
| 仓库 | 说明 | 备注 |
| ------------------------------------------------------------ | ------------------------------------------------------- | ---------------------------------------------- |
| [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) | 机器人服务端 | |
@@ -20,11 +19,18 @@
开发一套IM系统真的很艰辛,请路过的朋友们给点个star,支持我们坚持下去🙏🙏🙏🙏🙏
### 联系我们
问题讨论请加群:822762829
微信公众号:
> 商务合作请优先采用邮箱和我们联系。技术问题请到[野火IM论坛](http://bbs.wildfirechat.cn/)发帖交流
1. heavyrain.lee 邮箱: heavyrain.lee@wildfirechat.cn 微信:wildfirechat
2. imndx 邮箱: imndx@wildfirechat.cn 微信:wfchat
### 问题交流
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% />
@@ -65,7 +71,7 @@
| 捐赠者 | 商标 | 金额 | 简介 |
| ----- | ---- | -------- | --- |
| [SAMOS](http://samos.io) | ![SAMOS](http://static.wildfirechat.cn/SAMOS.png) | 每月2000¥ | 一站式区块链解决方案服务商(http://samos.io) |
| [SAMOS](http://samos.io) | ![SAMOS](http://static.wildfirechat.cn/SAMOS.png) | 保密 | 一站式区块链解决方案服务商(http://samos.io) |
## 特别感谢
1. [moquette](https://github.com/moquette-io/moquette) 本项目是基于此项目二次开发而来,处理MQTT相关业务。
+4
View File
@@ -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
+30 -4
View File
@@ -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
##原生客户端长链接端口
@@ -26,14 +35,15 @@ host 0.0.0.0
##本地绑定端口
local_port 80
##是否使用内置DB。0使用mysql;1使用h2db
embed.db 1
##是否使用内置DB。0使用mysql;1使用h2db;2使用mysql+mongodb。社区版只支持0和1,专业版支持2
embed.db 0
##服务器管理接口密钥
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用逗号分割。这个密钥对安全性帮助不大,客户不要修改
@@ -42,6 +52,12 @@ client.proto.secret_key 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x78,0x79,0x7A,0
##用来生产im token的私钥,只在服务器使用,客户端不用。正式使用时为了安全一定要修改这个值,切记切记
token.key testim
##首次登录,是否接收之前的历史消息。0 不接收;1接收。
message.roaming 0
##是否开启拉取远程历史消息
message.remote_history_message 0
#*********************************************************************
# hazelcase configuration
#*********************************************************************
@@ -95,10 +111,20 @@ push.android.server.address http://localhost:8085/android/push
push.ios.server.address http://localhost:8085/ios/push
#*********************************************************************
# MongoDB configuration
#*********************************************************************
#专业版支持,社区版请忽略
#mongodb.client_uri mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]
#mongodb.client_uri mongodb://host1:27017,host2:27017,host3:27017/wfchat?replicaSet=myReplicaSet
mongodb.client_uri mongodb://wfc:123456@localhost:27017/wfchat
mongodb.database wfchat
mongodb.data_expire_days 1096
#*********************************************************************
# Sensitive configuration
#*********************************************************************
##文本敏感词过滤处理方法,0 发送失败;1 发送成功但消息被服务器直接丢弃;2 命中的敏感词被替换成***
##文本敏感词过滤处理方法,0 发送失败;1 发送成功但消息被服务器直接丢弃;2 命中的敏感词被替换成***;3 记录并继续发送
sensitive.filter.type 0
#*********************************************************************
+3
View File
@@ -0,0 +1,3 @@
insert into t_user (`_uid`,`_name`,`_display_name`,`_type`,`_dt`) values ('admin','admin','系统管理员',3,1);
+1
View File
@@ -0,0 +1 @@
ALTER TABLE t_user_session MODIFY _token VARCHAR(240) DEFAULT NULL;
+4
View File
@@ -0,0 +1,4 @@
alter table `t_group` add column `_mute` tinyint NOT NULL DEFAULT 0;
alter table `t_group` add column `_join_type` tinyint NOT NULL DEFAULT 0;
alter table `t_group` add column `_private_chat` tinyint NOT NULL DEFAULT 0;
alter table `t_group` add column `_searchable` tinyint NOT NULL DEFAULT 0;
+10
View File
@@ -0,0 +1,10 @@
DROP TABLE IF EXISTS `t_settings`;
CREATE TABLE `t_settings` (
`id` int(11) NOT NULL PRIMARY KEY,
`value` varchar(64) NOT NULL,
`desc` varchar(128) NOT NULL
);
insert into t_settings(`id`, `value`,`desc`) values (1, '3000', '最大群成员数量');
+1
View File
@@ -0,0 +1 @@
alter table `t_sensitiveword` add column `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT;
+3
View File
@@ -0,0 +1,3 @@
alter table `t_user_session` modify column `_voip_token` varchar(240) DEFAULT '';
@@ -0,0 +1,2 @@
alter table t_user add column `_createTime` TIMESTAMP default CURRENT_TIMESTAMP;
alter table t_group add column `_createTime` TIMESTAMP default CURRENT_TIMESTAMP;
+1
View File
@@ -0,0 +1 @@
alter table `t_messages` add column `_content_type` int(11) NOT NULL DEFAULT '0';
+15
View File
@@ -0,0 +1,15 @@
DROP TABLE IF EXISTS `t_sensitive_messages`;
CREATE TABLE `t_sensitive_messages` (
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`_mid` bigint(20) NOT NULL,
`_from` varchar(64) NOT NULL,
`_type` tinyint NOT NULL DEFAULT 0,
`_target` varchar(64) NOT NULL,
`_line` int(11) NOT NULL DEFAULT 0,
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL DEFAULT NOW(),
`_content_type` int(11) NOT NULL DEFAULT 0,
UNIQUE INDEX `sensitive_message_uid_index` (`_mid` ASC)
);
+3
View File
@@ -0,0 +1,3 @@
insert into t_user (`_uid`,`_name`,`_display_name`,`_type`,`_dt`) values ('admin','admin','系统管理员',3,1);
+1
View File
@@ -0,0 +1 @@
ALTER TABLE t_user_session MODIFY _token VARCHAR(240) DEFAULT NULL;
+4
View File
@@ -0,0 +1,4 @@
alter table `t_group` add column `_mute` tinyint NOT NULL DEFAULT 0;
alter table `t_group` add column `_join_type` tinyint NOT NULL DEFAULT 0;
alter table `t_group` add column `_private_chat` tinyint NOT NULL DEFAULT 0;
alter table `t_group` add column `_searchable` tinyint NOT NULL DEFAULT 0;
+14
View File
@@ -0,0 +1,14 @@
DROP TABLE IF EXISTS `t_session`;
CREATE TABLE `t_session` (
`row_id` varchar(50) not null primary key,
`del_flag` tinyint(1) default 0 null,
`create_time` timestamp null,
`update_time` timestamp null,
`expire_time` timestamp null,
`user_id` int null,
constraint `t_session_pk_2` unique (`user_id`, `expire_time`)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
COLLATE = utf8mb4_unicode_ci;
@@ -0,0 +1,2 @@
alter table t_user MODIFY column `_createTime` DATETIME NOT NULL DEFAULT NOW();
alter table t_group MODIFY column `_createTime` DATETIME NOT NULL DEFAULT NOW();
+4
View File
@@ -0,0 +1,4 @@
alter table t_session MODIFY column `create_time` DATETIME NOT NULL DEFAULT NOW();
alter table t_session MODIFY column `update_time` DATETIME NOT NULL DEFAULT NOW();
alter table t_session MODIFY column `expire_time` DATETIME NOT NULL DEFAULT NOW();
+40
View File
@@ -0,0 +1,40 @@
update t_messages_0 set _dt = date_add(_dt, interval 8 hour);
update t_messages_1 set _dt = date_add(_dt, interval 8 hour);
update t_messages_2 set _dt = date_add(_dt, interval 8 hour);
update t_messages_3 set _dt = date_add(_dt, interval 8 hour);
update t_messages_4 set _dt = date_add(_dt, interval 8 hour);
update t_messages_5 set _dt = date_add(_dt, interval 8 hour);
update t_messages_6 set _dt = date_add(_dt, interval 8 hour);
update t_messages_7 set _dt = date_add(_dt, interval 8 hour);
update t_messages_8 set _dt = date_add(_dt, interval 8 hour);
update t_messages_9 set _dt = date_add(_dt, interval 8 hour);
update t_messages_10 set _dt = date_add(_dt, interval 8 hour);
update t_messages_11 set _dt = date_add(_dt, interval 8 hour);
update t_messages_12 set _dt = date_add(_dt, interval 8 hour);
update t_messages_13 set _dt = date_add(_dt, interval 8 hour);
update t_messages_14 set _dt = date_add(_dt, interval 8 hour);
update t_messages_15 set _dt = date_add(_dt, interval 8 hour);
update t_messages_16 set _dt = date_add(_dt, interval 8 hour);
update t_messages_17 set _dt = date_add(_dt, interval 8 hour);
update t_messages_18 set _dt = date_add(_dt, interval 8 hour);
update t_messages_19 set _dt = date_add(_dt, interval 8 hour);
update t_messages_20 set _dt = date_add(_dt, interval 8 hour);
update t_messages_21 set _dt = date_add(_dt, interval 8 hour);
update t_messages_22 set _dt = date_add(_dt, interval 8 hour);
update t_messages_23 set _dt = date_add(_dt, interval 8 hour);
update t_messages_24 set _dt = date_add(_dt, interval 8 hour);
update t_messages_25 set _dt = date_add(_dt, interval 8 hour);
update t_messages_26 set _dt = date_add(_dt, interval 8 hour);
update t_messages_27 set _dt = date_add(_dt, interval 8 hour);
update t_messages_28 set _dt = date_add(_dt, interval 8 hour);
update t_messages_29 set _dt = date_add(_dt, interval 8 hour);
update t_messages_30 set _dt = date_add(_dt, interval 8 hour);
update t_messages_31 set _dt = date_add(_dt, interval 8 hour);
update t_messages_32 set _dt = date_add(_dt, interval 8 hour);
update t_messages_33 set _dt = date_add(_dt, interval 8 hour);
update t_messages_34 set _dt = date_add(_dt, interval 8 hour);
update t_messages_35 set _dt = date_add(_dt, interval 8 hour);
File diff suppressed because it is too large Load Diff
+13
View File
@@ -0,0 +1,13 @@
DROP TABLE IF EXISTS `t_settings`;
CREATE TABLE `t_settings` (
`id` int(11) NOT NULL PRIMARY KEY,
`value` varchar(64) NOT NULL,
`desc` varchar(128) NOT NULL default ""
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
COLLATE = utf8mb4_unicode_ci;
insert into t_settings(`id`, `value`,`desc`) values (1, "3000", "最大群成员数量");
+1
View File
@@ -0,0 +1 @@
alter table `t_sensitiveword` add column `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT;
+3
View File
@@ -0,0 +1,3 @@
alter table `t_user_session` modify column `_voip_token` varchar(240) DEFAULT '';
@@ -0,0 +1,2 @@
alter table t_user add column `_createTime` TIMESTAMP default CURRENT_TIMESTAMP;
alter table t_group add column `_createTime` TIMESTAMP default CURRENT_TIMESTAMP;
+36
View File
@@ -0,0 +1,36 @@
alter table `t_messages_0` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_1` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_2` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_3` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_4` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_5` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_6` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_7` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_8` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_9` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_10` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_11` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_12` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_13` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_14` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_15` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_16` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_17` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_18` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_19` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_20` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_21` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_22` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_23` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_24` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_25` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_26` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_27` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_28` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_29` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_30` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_31` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_32` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_33` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_34` add column `_content_type` int(11) NOT NULL DEFAULT '0';
alter table `t_messages_35` add column `_content_type` int(11) NOT NULL DEFAULT '0';
+18
View File
@@ -0,0 +1,18 @@
DROP TABLE IF EXISTS `t_sensitive_messages`;
CREATE TABLE `t_sensitive_messages` (
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`_mid` bigint(20) NOT NULL,
`_from` varchar(64) NOT NULL,
`_type` tinyint NOT NULL DEFAULT 0,
`_target` varchar(64) NOT NULL,
`_line` int(11) NOT NULL DEFAULT 0,
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
`_content_type` int(11) NOT NULL DEFAULT '0',
INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
COLLATE = utf8mb4_unicode_ci;
+8 -32
View File
@@ -5,7 +5,7 @@
<relativePath>../</relativePath>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.22</version>
<version>0.29</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>
@@ -110,34 +104,11 @@
<artifactId>netty-transport-native-epoll</artifactId>
<version>${netty.version}</version>
<classifier>linux-x86_64</classifier>
<!--
<!--
<classifier>${os.detected.name}-${os.detected.arch}</classifier>
-->
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-jvm</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>com.librato.metrics</groupId>
<artifactId>metrics-librato</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>com.bugsnag</groupId>
<artifactId>bugsnag</artifactId>
<version>[3.0,4.0)</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
@@ -170,7 +141,7 @@
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>[0.9.5.3,)</version>
<version>0.9.5.4</version>
</dependency>
<!-- needs extra dependencies: objenesis & hamcrest -->
@@ -256,6 +227,11 @@
<version>5.2.4</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.4.1</version>
</dependency>
</dependencies>
@@ -22,6 +22,8 @@ import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import cn.wildfirechat.common.ErrorCode;
import java.nio.charset.StandardCharsets;
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
/**
@@ -84,7 +86,7 @@ abstract public class Action {
if (request instanceof FullHttpRequest) {
FullHttpRequest fullHttpRequest = (FullHttpRequest) request;
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
String content = new String(bytes);
String content = new String(bytes, StandardCharsets.UTF_8);//contribute by JiaRG from github
Gson gson = new Gson();
T t = gson.fromJson(content, cls);
return t;
@@ -35,12 +35,15 @@ import java.util.concurrent.Executor;
@HttpMethod("POST")
public class RouteAction extends Action {
private static final Logger LOG = LoggerFactory.getLogger(RouteAction.class);
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
response.setContentType("application/octet-stream");
FullHttpRequest fullHttpRequest = (FullHttpRequest)request.getNettyRequest();
response.setHeader("Access-Control-Allow-Origin", "*");
FullHttpRequest fullHttpRequest = (FullHttpRequest) request.getNettyRequest();
boolean b = false;
if ("web".equalsIgnoreCase(fullHttpRequest.headers().get("p"))) {
b = true;
@@ -144,10 +147,14 @@ public class RouteAction extends Action {
private void sendResponse(Response response, ErrorCode errorCode, byte[] contents, boolean base64Response) {
response.setStatus(HttpResponseStatus.OK);
if(contents == null) {
if (contents == null) {
ByteBuf ackPayload = Unpooled.buffer();
ackPayload.ensureWritable(1).writeByte(errorCode.getCode());
response.setContent(ackPayload);
contents = ackPayload.array();
}
if (base64Response) {
byte[] base64Data = Base64.getEncoder().encode(contents);
response.setContent(base64Data);
} else {
response.setContent(contents);
}
@@ -45,7 +45,7 @@ public class BlockUserAction extends AdminAction {
result = RestResult.resultOf(errorCode);
response.setContent(new Gson().toJson(result));
if (inputUserBlock.getStatus() != 0) {
if (inputUserBlock.getStatus() == 2) {
RPCCenter.getInstance().sendRequest(null, null, RPCCenter.KICKOFF_USER_REQUEST, inputUserBlock.getUserId().getBytes(), inputUserBlock.getUserId(), TargetEntry.Type.TARGET_TYPE_USER, null, true);
}
@@ -0,0 +1,94 @@
/*
* 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.*;
import cn.wildfirechat.proto.WFCMessage;
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 io.netty.util.internal.StringUtil;
import win.liyufan.im.IMTopic;
import win.liyufan.im.UUIDGenerator;
import java.util.concurrent.Executor;
@Route(APIPath.Create_Channel)
@HttpMethod("POST")
public class CreateChannelAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputCreateChannel inputCreateChannel = getRequestBody(request.getNettyRequest(), InputCreateChannel.class);
if (inputCreateChannel != null
&& !StringUtil.isNullOrEmpty(inputCreateChannel.getName())) {
if(StringUtil.isNullOrEmpty(inputCreateChannel.getTargetId())) {
inputCreateChannel.setTargetId(messagesStore.getShortUUID());
}
RPCCenter.getInstance().sendRequest(inputCreateChannel.getOwner(), null, IMTopic.CreateChannelTopic, inputCreateChannel.toProtoChannelInfo().toByteArray(), inputCreateChannel.getOwner(), 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, new OutputCreateChannel(inputCreateChannel.getTargetId()));
} 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;
}
}
@@ -0,0 +1,61 @@
/*
* 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.InputGetUserInfo;
import cn.wildfirechat.pojos.InputOutputUserInfo;
import cn.wildfirechat.pojos.SystemSettingPojo;
import cn.wildfirechat.proto.WFCMessage;
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.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
@Route(APIPath.Get_System_Setting)
@HttpMethod("POST")
public class GetSystemSettingAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return false;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
SystemSettingPojo inputUserId = getRequestBody(request.getNettyRequest(), SystemSettingPojo.class);
if (inputUserId != null) {
SystemSettingPojo output = messagesStore.getSystemSetting(inputUserId.id);
response.setStatus(HttpResponseStatus.OK);
RestResult result;
if (output == null) {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
} else {
result = RestResult.ok(output);
}
response.setContent(new Gson().toJson(result));
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
return true;
}
}
@@ -42,7 +42,7 @@ public class KickoffGroupMemberAction extends AdminAction {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputKickoffGroupMember inputKickoffGroupMember = getRequestBody(request.getNettyRequest(), InputKickoffGroupMember.class);
if (inputKickoffGroupMember.isValide()) {
RPCCenter.getInstance().sendRequest(inputKickoffGroupMember.getOperator(), null, IMTopic.AddGroupMemberTopic, inputKickoffGroupMember.toProtoGroupRequest().toByteArray(), inputKickoffGroupMember.getOperator(), TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
RPCCenter.getInstance().sendRequest(inputKickoffGroupMember.getOperator(), null, IMTopic.KickoffGroupMemberTopic, inputKickoffGroupMember.toProtoGroupRequest().toByteArray(), inputKickoffGroupMember.getOperator(), TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
@Override
public void onSuccess(byte[] result) {
ByteBuf byteBuf = Unpooled.buffer();
@@ -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.InputModifyGroupInfo;
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_Modify_Info)
@HttpMethod("POST")
public class ModifyGroupInfoAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputModifyGroupInfo inputAddGroupMember = getRequestBody(request.getNettyRequest(), InputModifyGroupInfo.class);
if (inputAddGroupMember.isValide()) {
RPCCenter.getInstance().sendRequest(inputAddGroupMember.getOperator(), null, IMTopic.ModifyGroupInfoTopic, 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;
}
}
@@ -0,0 +1,90 @@
/*
* 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.BroadMessageData;
import cn.wildfirechat.pojos.BroadMessageResult;
import cn.wildfirechat.pojos.MultiMessageResult;
import cn.wildfirechat.pojos.MulticastMessageData;
import com.google.gson.Gson;
import com.hazelcast.util.StringUtil;
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.Msg_Multicast)
@HttpMethod("POST")
public class MulticastMessageAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
MulticastMessageData sendMessageData = getRequestBody(request.getNettyRequest(), MulticastMessageData.class);
if (MulticastMessageData.isValide(sendMessageData)) {
RPCCenter.getInstance().sendRequest(sendMessageData.getSender(), null, IMTopic.MultiCastMessageTopic, sendMessageData.toProtoMessage().toByteArray(), sendMessageData.getSender(), 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) {
long messageId = byteBuf.readLong();
long timestamp = byteBuf.readLong();
sendResponse(response, null, new MultiMessageResult(messageId, timestamp));
} 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;
}
}
@@ -0,0 +1,64 @@
/*
* 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.InputGetUserInfo;
import cn.wildfirechat.pojos.InputOutputUserInfo;
import cn.wildfirechat.pojos.SystemSettingPojo;
import cn.wildfirechat.proto.WFCMessage;
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.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
@Route(APIPath.Put_System_Setting)
@HttpMethod("POST")
public class PutSystemSettingAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
SystemSettingPojo inputUserId = getRequestBody(request.getNettyRequest(), SystemSettingPojo.class);
if (inputUserId != null
&& (!StringUtil.isNullOrEmpty(inputUserId.value) || !StringUtil.isNullOrEmpty(inputUserId.desc))) {
boolean success = messagesStore.updateSystemSetting(inputUserId.id, inputUserId.value, inputUserId.desc);
response.setStatus(HttpResponseStatus.OK);
RestResult result;
if (!success) {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_SERVER_ERROR);
} else {
result = RestResult.ok();
}
response.setContent(new Gson().toJson(result));
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
return true;
}
}
@@ -24,7 +24,7 @@ import org.slf4j.LoggerFactory;
/**
* Action处理单元
*
*
* @author Looly
*/
abstract public class ActionHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
@@ -45,7 +45,7 @@ abstract public class ActionHandler extends SimpleChannelInboundHandler<FullHttp
try {
//do filter
boolean isPass = this.doFilter(request, response);
if(isPass){
//do action
this.doAction(ctx, request, response);
@@ -64,10 +64,10 @@ abstract public class ActionHandler extends SimpleChannelInboundHandler<FullHttp
ctx.fireExceptionCaught(e);
ctx.close();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
@@ -75,7 +75,7 @@ abstract public class ActionHandler extends SimpleChannelInboundHandler<FullHttp
Channel channel = ctx.channel();
if(channel.isActive())ctx.close();
}
//---------------------------------------------------------------------------------------- Private method start
/**
* 执行过滤
@@ -91,7 +91,7 @@ abstract public class ActionHandler extends SimpleChannelInboundHandler<FullHttp
return false;
}
}
//自定义Path过滤器
filter = ServerSetting.getFilter(request.getPath());
if(null != filter){
@@ -99,7 +99,7 @@ abstract public class ActionHandler extends SimpleChannelInboundHandler<FullHttp
return false;
}
}
return true;
}
@@ -110,6 +110,10 @@ abstract public class ActionHandler extends SimpleChannelInboundHandler<FullHttp
* @param response 响应对象
*/
private void doAction(ChannelHandlerContext ctx, Request request, Response response){
if( "/route".equalsIgnoreCase(request.getPath()) && "OPTIONS".equalsIgnoreCase(request.getMethod())){
handleOptions(ctx, request, response);
return;
}
Action action;
if (isValidePath(request.getPath())) {
action = ServerSetting.getAction(request.getPath(), request.getMethod().toUpperCase());
@@ -141,4 +145,11 @@ abstract public class ActionHandler extends SimpleChannelInboundHandler<FullHttp
}
}
//---------------------------------------------------------------------------------------- Private method start
private void handleOptions(ChannelHandlerContext ctx, Request request, Response response){
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Headers", "*");
response.send();
}
}
@@ -60,13 +60,6 @@ public final class BrokerConstants {
public static final String NETTY_SO_KEEPALIVE_PROPERTY_NAME = "netty.so_keepalive";
public static final String NETTY_CHANNEL_TIMEOUT_SECONDS_PROPERTY_NAME = "netty.channel_timeout.seconds";
public static final String NETTY_EPOLL_PROPERTY_NAME = "netty.epoll";
public static final String METRICS_ENABLE_PROPERTY_NAME = "use_metrics";
public static final String METRICS_LIBRATO_EMAIL_PROPERTY_NAME = "metrics.librato.email";
public static final String METRICS_LIBRATO_TOKEN_PROPERTY_NAME = "metrics.librato.token";
public static final String METRICS_LIBRATO_SOURCE_PROPERTY_NAME = "metrics.librato.source";
public static final String BUGSNAG_ENABLE_PROPERTY_NAME = "use_bugsnag";
public static final String BUGSNAG_TOKEN_PROPERTY_NAME = "bugsnag.token";
public static final String STORAGE_CLASS_NAME = "storage_class";
@@ -103,7 +96,7 @@ public final class BrokerConstants {
public static final String HTTP_SERVER_API_NO_CHECK_TIME = "http.admin.no_check_time";
public static final String CLIENT_PROTO_SECRET_KEY = "client.proto.secret_key";
public static final String TOKEN_SECRET_KEY = "token.key ";
public static final String TOKEN_SECRET_KEY = "token.key";
public static final String EMBED_DB_PROPERTY_NAME = "embed.db";
@@ -113,6 +106,12 @@ public final class BrokerConstants {
public static final String MESSAGE_Forward_Url = "message.forward.url";
public static final String MONGODB_Client_URI = "mongodb.client_uri";
public static final String MONGODB_Database = "mongodb.database";
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.remote_history_message";
private BrokerConstants() {
@@ -22,7 +22,7 @@ public class AddGroupMember extends GroupHandler<WFCMessage.AddGroupMemberReques
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, boolean isAdmin, WFCMessage.AddGroupMemberRequest request, Qos1PublishHandler.IMCallback callback) {
ErrorCode errorCode = m_messagesStore.addGroupMembers(fromUser, request.getGroupId(), request.getAddedMemberList());
ErrorCode errorCode = m_messagesStore.addGroupMembers(fromUser, isAdmin, request.getGroupId(), request.getAddedMemberList());
if (errorCode == ERROR_CODE_SUCCESS) {
if (request.hasNotifyContent() && request.getNotifyContent().getType() > 0) {
sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), request.getNotifyContent());
@@ -14,10 +14,18 @@ import io.netty.buffer.ByteBuf;
import cn.wildfirechat.common.ErrorCode;
import win.liyufan.im.IMTopic;
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
@Handler(IMTopic.DeleteFriendTopic)
public class DeleteFriendHandler extends IMHandler<WFCMessage.IDBuf> {
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, boolean isAdmin, WFCMessage.IDBuf request, Qos1PublishHandler.IMCallback callback) {
return m_messagesStore.deleteFriend(fromUser, request.getId());
long[] head = new long[2];
ErrorCode errorCode = m_messagesStore.deleteFriend(fromUser, request.getId(), head);
if (errorCode == ERROR_CODE_SUCCESS) {
publisher.publishNotification(IMTopic.NotifyFriendTopic, fromUser, head[0]);
publisher.publishNotification(IMTopic.NotifyFriendTopic, request.getId(), head[1]);
}
return errorCode;
}
}
@@ -9,6 +9,7 @@
package io.moquette.imhandler;
import cn.wildfirechat.proto.WFCMessage;
import com.hazelcast.util.StringUtil;
import com.xiaoleilu.loServer.model.FriendData;
import io.moquette.spi.impl.Qos1PublishHandler;
import io.netty.buffer.ByteBuf;
@@ -25,7 +26,11 @@ public class FriendPullHandler extends IMHandler<WFCMessage.Version> {
WFCMessage.GetFriendsResult.Builder builder = WFCMessage.GetFriendsResult.newBuilder();
for (FriendData data : friendDatas
) {
builder.addEntry(WFCMessage.Friend.newBuilder().setState(data.getState()).setUid(data.getFriendUid()).setUpdateDt(data.getTimestamp()).setAlias(data.getAlias()).build());
WFCMessage.Friend.Builder builder1 = WFCMessage.Friend.newBuilder().setState(data.getState()).setUid(data.getFriendUid()).setUpdateDt(data.getTimestamp());
if (!StringUtil.isNullOrEmpty(data.getAlias())) {
builder1.setAlias(data.getAlias());
}
builder.addEntry(builder1.build());
}
byte[] data = builder.build().toByteArray();
ackPayload.ensureWritable(data.length).writeBytes(data);
@@ -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;
}
}
@@ -32,6 +32,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -233,13 +235,22 @@ abstract public class IMHandler<T> {
public void afterAction(String clientID, String fromUser, String topic, Qos1PublishHandler.IMCallback callback) {
}
protected long publish(String username, String clientID, WFCMessage.Message message) {
Set<String> notifyReceivers = new LinkedHashSet<>();
WFCMessage.Message.Builder messageBuilder = message.toBuilder();
int pullType = m_messagesStore.getNotifyReceivers(username, messageBuilder, notifyReceivers);
this.publisher.publish2Receivers(messageBuilder.build(), notifyReceivers, clientID, pullType);
return notifyReceivers.size();
}
protected long saveAndPublish(String username, String clientID, WFCMessage.Message message) {
Set<String> notifyReceivers = new LinkedHashSet<>();
message = m_messagesStore.storeMessage(username, clientID, message);
int pullType = m_messagesStore.getNotifyReceivers(username, message, notifyReceivers);
this.publisher.publish2Receivers(message, notifyReceivers, clientID, pullType);
WFCMessage.Message.Builder messageBuilder = message.toBuilder();
int pullType = m_messagesStore.getNotifyReceivers(username, messageBuilder, notifyReceivers);
this.publisher.publish2Receivers(messageBuilder.build(), notifyReceivers, clientID, pullType);
return notifyReceivers.size();
}
@@ -249,4 +260,12 @@ abstract public class IMHandler<T> {
mServer.getImBusinessScheduler().execute(() -> publisher.publish2Receivers(updatedMessage, notifyReceivers, clientID, ProtoConstants.PullType.Pull_Normal));
return notifyReceivers.size();
}
protected long saveAndMulticast(String username, String clientID, WFCMessage.Message message, Collection<String> targets) {
Set<String> notifyReceivers = new HashSet<>();
notifyReceivers.addAll(targets);
WFCMessage.Message updatedMessage = m_messagesStore.storeMessage(username, clientID, message);
mServer.getImBusinessScheduler().execute(() -> publisher.publish2Receivers(updatedMessage, notifyReceivers, clientID, ProtoConstants.PullType.Pull_Normal));
return notifyReceivers.size();
}
}
@@ -24,20 +24,38 @@ public class KickoffGroupMember extends GroupHandler<WFCMessage.RemoveGroupMembe
WFCMessage.GroupInfo groupInfo = m_messagesStore.getGroupInfo(request.getGroupId());
if (groupInfo == null) {
errorCode = ErrorCode.ERROR_CODE_NOT_EXIST;
} else if ((groupInfo.getType() == ProtoConstants.GroupType.GroupType_Normal || groupInfo.getType() == ProtoConstants.GroupType.GroupType_Restricted)
&& groupInfo.getOwner() != null && groupInfo.getOwner().equals(fromUser)) {
//send notify message first, then kickoff the member
if (request.hasNotifyContent() && request.getNotifyContent().getType() > 0) {
sendGroupNotification(fromUser, groupInfo.getTargetId(), request.getToLineList(), request.getNotifyContent());
} else {
WFCMessage.MessageContent content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, null, request.getRemovedMemberList()).getKickokfMemberGroupNotifyContent();
sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), content);
}
errorCode = m_messagesStore.kickoffGroupMembers(fromUser, request.getGroupId(), request.getRemovedMemberList());
} else {
errorCode = ErrorCode.ERROR_CODE_NOT_RIGHT;
boolean isAllow = isAdmin;
if (!isAllow) {
if (groupInfo.getOwner() != null) {
if (request.getRemovedMemberList().contains(groupInfo.getOwner())) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
}
if (groupInfo.getOwner() != null && groupInfo.getOwner().equals(fromUser)) {
isAllow = true;
}
}
if (!isAllow) {
WFCMessage.GroupMember gm = m_messagesStore.getGroupMember(request.getGroupId(), fromUser);
if (gm != null && gm.getType() == ProtoConstants.GroupMemberType.GroupMemberType_Manager) {
isAllow = true;
}
}
if (!isAllow && (groupInfo.getType() == ProtoConstants.GroupType.GroupType_Normal || groupInfo.getType() == ProtoConstants.GroupType.GroupType_Restricted)) {
errorCode = ErrorCode.ERROR_CODE_NOT_RIGHT;
} else {
//send notify message first, then kickoff the member
if (request.hasNotifyContent() && request.getNotifyContent().getType() > 0) {
sendGroupNotification(fromUser, groupInfo.getTargetId(), request.getToLineList(), request.getNotifyContent());
} else {
WFCMessage.MessageContent content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, null, request.getRemovedMemberList()).getKickokfMemberGroupNotifyContent();
sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), content);
}
errorCode = m_messagesStore.kickoffGroupMembers(fromUser, isAdmin, request.getGroupId(), request.getRemovedMemberList());
}
}
return errorCode;
}
@@ -14,16 +14,15 @@ import io.moquette.spi.impl.Qos1PublishHandler;
import io.netty.buffer.ByteBuf;
import cn.wildfirechat.common.ErrorCode;
import static cn.wildfirechat.proto.ProtoConstants.ModifyGroupInfoType.Modify_Group_Name;
import static cn.wildfirechat.proto.ProtoConstants.ModifyGroupInfoType.Modify_Group_Portrait;
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
import static cn.wildfirechat.proto.ProtoConstants.ModifyGroupInfoType.*;
import static win.liyufan.im.IMTopic.ModifyGroupInfoTopic;
@Handler(value = ModifyGroupInfoTopic)
public class ModifyGroupInfoHandler extends GroupHandler<WFCMessage.ModifyGroupInfoRequest> {
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, boolean isAdmin, WFCMessage.ModifyGroupInfoRequest request, Qos1PublishHandler.IMCallback callback) {
ErrorCode errorCode= m_messagesStore.modifyGroupInfo(fromUser, request.getGroupId(), request.getType(), request.getValue());
ErrorCode errorCode= m_messagesStore.modifyGroupInfo(fromUser, request.getGroupId(), request.getType(), request.getValue(), isAdmin);
if (errorCode == ERROR_CODE_SUCCESS) {
if(request.hasNotifyContent() && request.getNotifyContent().getType() > 0) {
sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), request.getNotifyContent());
@@ -33,6 +32,14 @@ public class ModifyGroupInfoHandler extends GroupHandler<WFCMessage.ModifyGroupI
content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, request.getValue(), "").getChangeGroupNameNotifyContent();
} else if(request.getType() == Modify_Group_Portrait) {
content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, null, "").getChangeGroupPortraitNotifyContent();
} else if(request.getType() == Modify_Group_Mute) {
content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, request.getValue(), "").getChangeGroupMuteNotifyContent();
} else if(request.getType() == Modify_Group_JoinType) {
content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, request.getValue(), "").getChangeGroupJointypeNotifyContent();
} else if(request.getType() == Modify_Group_PrivateChat) {
content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, request.getValue(), "").getChangeGroupPrivatechatNotifyContent();
} else if(request.getType() == Modify_Group_Searchable) {
content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, request.getValue(), "").getChangeGroupSearchableNotifyContent();
}
if (content != null) {
@@ -0,0 +1,57 @@
/*
* 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.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
import com.hazelcast.util.StringUtil;
import io.moquette.BrokerConstants;
import io.moquette.server.Server;
import io.moquette.spi.impl.Qos1PublishHandler;
import io.netty.buffer.ByteBuf;
import win.liyufan.im.IMTopic;
import win.liyufan.im.MessageShardingUtil;
import java.util.Set;
import static cn.wildfirechat.proto.ProtoConstants.ContentType.Text;
import static cn.wildfirechat.proto.ProtoConstants.ConversationType.ConversationType_Private;
@Handler(value = IMTopic.MultiCastMessageTopic)
public class MultiCastMessageHandler extends IMHandler<WFCMessage.MultiCastMessage> {
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, boolean isAdmin, WFCMessage.MultiCastMessage multiCastMessage, Qos1PublishHandler.IMCallback callback) {
ErrorCode errorCode = ErrorCode.ERROR_CODE_SUCCESS;
if (!isAdmin) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
long timestamp = System.currentTimeMillis();
long messageId = MessageShardingUtil.generateId();
WFCMessage.Message message = WFCMessage.Message.newBuilder()
.setContent(multiCastMessage.getContent())
.setConversation(WFCMessage.Conversation.newBuilder().setTarget(fromUser).setType(ConversationType_Private).setLine(multiCastMessage.getLine()).build())
.setFromUser(fromUser)
.setMessageId(messageId)
.setServerTimestamp(timestamp)
.build();
saveAndMulticast(fromUser, clientID, message, multiCastMessage.getToList());
ackPayload = ackPayload.capacity(20);
ackPayload.writeLong(messageId);
ackPayload.writeLong(timestamp);
return errorCode;
}
}
@@ -20,19 +20,14 @@ import static win.liyufan.im.IMTopic.QuitGroupTopic;
public class QuitGroupHandler extends GroupHandler<WFCMessage.QuitGroupRequest> {
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, boolean isAdmin, WFCMessage.QuitGroupRequest request, Qos1PublishHandler.IMCallback callback) {
ErrorCode errorCode = ErrorCode.ERROR_CODE_SUCCESS;
WFCMessage.GroupInfo groupInfo = m_messagesStore.getGroupInfo(request.getGroupId());
if (groupInfo == null) {
errorCode = m_messagesStore.quitGroup(fromUser, request.getGroupId());
} else {
//send notify message first, then quit group
ErrorCode errorCode = m_messagesStore.quitGroup(fromUser, request.getGroupId());
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
if (request.hasNotifyContent() && request.getNotifyContent().getType() > 0) {
sendGroupNotification(fromUser, groupInfo.getTargetId(), request.getToLineList(), request.getNotifyContent());
sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), request.getNotifyContent());
} else {
WFCMessage.MessageContent content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, null, "").getQuitGroupNotifyContent();
sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), content);
}
errorCode = m_messagesStore.quitGroup(fromUser, request.getGroupId());
}
return errorCode;
}
@@ -21,7 +21,7 @@ import java.util.Set;
public class RecallMessageHandler extends IMHandler<WFCMessage.INT64Buf> {
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, boolean isAdmin, WFCMessage.INT64Buf int64Buf, Qos1PublishHandler.IMCallback callback) {
ErrorCode errorCode = m_messagesStore.recallMessage(int64Buf.getId(), fromUser);
ErrorCode errorCode = m_messagesStore.recallMessage(int64Buf.getId(), fromUser, isAdmin);
if(errorCode != ErrorCode.ERROR_CODE_SUCCESS) {
return errorCode;
@@ -33,7 +33,11 @@ public class RecallMessageHandler extends IMHandler<WFCMessage.INT64Buf> {
return ErrorCode.ERROR_CODE_NOT_EXIST;
}
m_messagesStore.getNotifyReceivers(fromUser, message, notifyReceivers);
publish(fromUser, clientID, message);
//等待客户端实现根据撤回消息更新内容,之后可以删掉这段代码
m_messagesStore.getNotifyReceivers(fromUser, message.toBuilder(), notifyReceivers);
this.publisher.publishRecall2Receivers(int64Buf.getId(), fromUser, notifyReceivers, clientID);
return errorCode;
@@ -46,6 +46,10 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
ErrorCode errorCode = ErrorCode.ERROR_CODE_SUCCESS;
if (message != null) {
if (!isAdmin) { //admin do not check the right
// 不能在端上直接发送撤回和群通知
if (message.getContent().getType() == 80 || (message.getContent().getType() >= 100 && message.getContent().getType() < 200)) {
return ErrorCode.INVALID_PARAMETER;
}
int userStatus = m_messagesStore.getUserStatus(fromUser);
if (userStatus == 1 || userStatus == 2) {
return ErrorCode.ERROR_CODE_FORBIDDEN_SEND_MSG;
@@ -58,14 +62,19 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
}
if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Group && !m_messagesStore.isMemberInGroup(fromUser, message.getConversation().getTarget())) {
return ErrorCode.ERROR_CODE_NOT_IN_GROUP;
} else if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Group && m_messagesStore.isForbiddenInGroup(fromUser, message.getConversation().getTarget())) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
} else if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_ChatRoom && !m_messagesStore.checkUserClientInChatroom(fromUser, clientID, message.getConversation().getTarget())) {
return ErrorCode.ERROR_CODE_NOT_IN_CHATROOM;
} else if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Channel && !m_messagesStore.checkUserInChannel(fromUser, message.getConversation().getTarget())) {
return ErrorCode.ERROR_CODE_NOT_IN_CHANNEL;
if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Group ) {
errorCode = m_messagesStore.canSendMessageInGroup(fromUser, message.getConversation().getTarget());
if (errorCode != ErrorCode.ERROR_CODE_SUCCESS) {
return errorCode;
}
} else if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_ChatRoom) {
if(!m_messagesStore.checkUserClientInChatroom(fromUser, clientID, message.getConversation().getTarget())) {
return ErrorCode.ERROR_CODE_NOT_IN_CHATROOM;
}
} else if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Channel) {
if(!m_messagesStore.checkUserInChannel(fromUser, message.getConversation().getTarget())) {
return ErrorCode.ERROR_CODE_NOT_IN_CHANNEL;
}
}
}
@@ -81,17 +90,20 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
if (!isAdmin && message.getContent().getType() == Text) {
Set<String> matched = m_messagesStore.handleSensitiveWord(message.getContent().getSearchableContent());
if (matched != null && !matched.isEmpty()) {
m_messagesStore.storeSensitiveMessage(message);
if (mSensitiveType == 0) {
errorCode = ErrorCode.ERROR_CODE_SENSITIVE_MATCHED;
} else if(mSensitiveType == 1) {
ignoreMsg = true;
} else {
} else if(mSensitiveType == 2) {
String text = message.getContent().getSearchableContent();
for (String word : matched) {
text = text.replace(word, "***");
}
message = message.toBuilder().setContent(message.getContent().toBuilder().setSearchableContent(text).build()).build();
} else if(mSensitiveType == 3) {
}
}
}
@@ -0,0 +1,35 @@
/*
* 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.pojos.GroupNotificationBinaryContent;
import cn.wildfirechat.proto.WFCMessage;
import io.moquette.spi.impl.Qos1PublishHandler;
import io.netty.buffer.ByteBuf;
import win.liyufan.im.IMTopic;
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
@Handler(IMTopic.SetGroupManagerTopic)
public class SetGroupManagerHandler extends GroupHandler<WFCMessage.SetGroupManagerRequest> {
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, boolean isAdmin, WFCMessage.SetGroupManagerRequest request, Qos1PublishHandler.IMCallback callback) {
ErrorCode errorCode = m_messagesStore.setGroupManager(fromUser, request.getGroupId(), request.getType(), request.getUserIdList(), isAdmin);
if (errorCode == ERROR_CODE_SUCCESS) {
if (request.hasNotifyContent() && request.getNotifyContent().getType() > 0) {
sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), request.getNotifyContent());
} else {
WFCMessage.MessageContent content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, request.getType() + "", request.getUserIdList()).getSetGroupManagerNotifyContent();
sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), content);
}
}
return errorCode;
}
}
@@ -26,7 +26,7 @@ public class UploadDeviceTokenHandler extends IMHandler<WFCMessage.UploadDeviceT
session.setAppName(request.getAppName());
if (request.getPlatform() == ProtoConstants.Platform.Platform_iOS && request.getPushType() == 2) {
session.setVoipDeviceToken(request.getDeviceToken());
session.setPushType(0);//如果有voip token,肯定是生产环境的推送
// session.setPushType(0);//如果有voip token,肯定是生产环境的推送
m_sessionsStore.updateSessionToken(session, true);
} else {
session.setDeviceToken(request.getDeviceToken());
@@ -8,6 +8,7 @@
package io.moquette.persistence;
import cn.wildfirechat.pojos.SystemSettingPojo;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
import cn.wildfirechat.server.ThreadPoolExecutorWrapper;
@@ -76,6 +77,79 @@ public class DatabaseStore {
return out;
}
boolean updateSystemSetting(int id, String value, String desc) {
Connection connection = null;
PreparedStatement statement = null;
try {
connection = DBUtil.getConnection();
String sql = "insert into t_settings " +
" (`id`, `value`, `desc`) values(?, ?, ?)" +
" ON DUPLICATE KEY UPDATE " +
"`value` = ?," +
"`desc` = ?";
statement = connection.prepareStatement(sql);
int index = 1;
statement.setLong(index++, id);
statement.setString(index++, value);
statement.setString(index++, desc);
statement.setString(index++, value);
statement.setString(index++, desc);
int count = statement.executeUpdate();
LOG.info("Update rows {}", count);
return true;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Utility.printExecption(LOG, e);
} finally {
DBUtil.closeDB(connection, statement);
}
return false;
}
SystemSettingPojo getSystemSetting(int id) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet rs = null;
try {
connection = DBUtil.getConnection();
String sql = "select `value`, `desc` from t_settings where `id` = ?";
statement = connection.prepareStatement(sql);
int index = 1;
statement.setLong(index++, id);
rs = statement.executeQuery();
while (rs.next()) {
SystemSettingPojo out = new SystemSettingPojo();
index = 1;
out.id = id;
String value = rs.getString(index++);
value = (value == null ? "" : value);
out.value = value;
value = rs.getString(index++);
value = (value == null ? "" : value);
out.desc = value;
return out;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Utility.printExecption(LOG, e);
} finally {
DBUtil.closeDB(connection, statement, rs);
}
return null;
}
List<WFCMessage.User> searchUserFromDB(String keyword, boolean buzzy, int page) {
Connection connection = null;
PreparedStatement statement = null;
@@ -210,23 +284,27 @@ public class DatabaseStore {
return 0;
}
void reloadGroupMemberFromDB(HazelcastInstance hzInstance) {
synchronized Collection<WFCMessage.GroupMember> reloadGroupMemberFromDB(HazelcastInstance hzInstance, String groupId) {
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(MemoryMessagesStore.GROUP_MEMBERS);
if (groupMembers.get(groupId).size() > 0) {
return groupMembers.get(groupId);
}
Connection connection = null;
PreparedStatement statement = null;
ResultSet rs = null;
try {
connection = DBUtil.getConnection();
String sql = "select `_gid`" +
", `_mid`" +
String sql = "select `_mid`" +
", `_alias`" +
", `_type`" +
", `_dt` from t_group_member";
", `_dt` from t_group_member where _gid = ?";
statement = connection.prepareStatement(sql);
statement.setString(1, groupId);
int index;
rs = statement.executeQuery();
while (rs.next()) {
WFCMessage.GroupMember.Builder builder = WFCMessage.GroupMember.newBuilder();
@@ -234,10 +312,6 @@ public class DatabaseStore {
String value = rs.getString(index++);
value = (value == null ? "" : value);
String groupId = value;
value = rs.getString(index++);
value = (value == null ? "" : value);
builder.setMemberId(value);
value = rs.getString(index++);
@@ -254,6 +328,7 @@ public class DatabaseStore {
WFCMessage.GroupMember member = builder.build();
groupMembers.put(groupId, member);
}
return groupMembers.get(groupId);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
@@ -261,6 +336,7 @@ public class DatabaseStore {
} finally {
DBUtil.closeDB(connection, statement, rs);
}
return new ArrayList<>();
}
void reloadFriendsFromDB(HazelcastInstance hzInstance) {
@@ -384,11 +460,12 @@ public class DatabaseStore {
try {
connection = DBUtil.getConnection();
String sql = "insert into " + MessageShardingUtil.getMessageTable(message.getMessageId()) +
" (`_mid`, `_from`, `_type`, `_target`, `_line`, `_data`, `_searchable_key`, `_dt`) values(?, ?, ?, ?, ?, ?, ?, ?)" +
" (`_mid`, `_from`, `_type`, `_target`, `_line`, `_data`, `_searchable_key`, `_dt`, `_content_type`) values(?, ?, ?, ?, ?, ?, ?, ?, ?)" +
" ON DUPLICATE KEY UPDATE " +
"`_data` = ?," +
"`_searchable_key` = ?," +
"`_dt` = ?";
"`_dt` = ?," +
"`_content_type` = ?";
String searchableContent = message.getContent().getSearchableContent() == null ? "" : message.getContent().getSearchableContent();
@@ -404,10 +481,63 @@ public class DatabaseStore {
statement.setBlob(index++, blob);
statement.setString(index++, searchableContent);
statement.setTimestamp(index++, new Timestamp(message.getServerTimestamp()));
statement.setInt(index++, message.getContent().getType());
statement.setBlob(index++, blob);
statement.setString(index++, searchableContent);
statement.setTimestamp(index++, new Timestamp(message.getServerTimestamp()));
statement.setInt(index++, message.getContent().getType());
int count = statement.executeUpdate();
LOG.info("Update rows {}", count);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Utility.printExecption(LOG, e);
} finally {
DBUtil.closeDB(connection, statement);
}
});
}
void persistSensitiveMessage(final WFCMessage.Message message) {
if(message.getContent().getPersistFlag() == Transparent) {
return;
}
mScheduler.execute(()-> {
Connection connection = null;
PreparedStatement statement = null;
try {
connection = DBUtil.getConnection();
String sql = "insert into " + "t_sensitive_messages" +
" (`_mid`, `_from`, `_type`, `_target`, `_line`, `_data`, `_searchable_key`, `_dt`, `_content_type`) values(?, ?, ?, ?, ?, ?, ?, ?, ?)" +
" ON DUPLICATE KEY UPDATE " +
"`_data` = ?," +
"`_searchable_key` = ?," +
"`_dt` = ?," +
"`_content_type` = ?";
String searchableContent = message.getContent().getSearchableContent() == null ? "" : message.getContent().getSearchableContent();
statement = connection.prepareStatement(sql);
int index = 1;
statement.setLong(index++, message.getMessageId());
statement.setString(index++, message.getFromUser());
statement.setInt(index++, message.getConversation().getType());
statement.setString(index++, message.getConversation().getTarget());
statement.setInt(index++, message.getConversation().getLine());
Blob blob = connection.createBlob();
blob.setBytes(1, message.getContent().toByteArray());
statement.setBlob(index++, blob);
statement.setString(index++, searchableContent);
statement.setTimestamp(index++, new Timestamp(message.getServerTimestamp()));
statement.setInt(index++, message.getContent().getType());
statement.setBlob(index++, blob);
statement.setString(index++, searchableContent);
statement.setTimestamp(index++, new Timestamp(message.getServerTimestamp()));
statement.setInt(index++, message.getContent().getType());
int count = statement.executeUpdate();
LOG.info("Update rows {}", count);
@@ -538,6 +668,25 @@ public class DatabaseStore {
return null;
}
void deleteMessage(long messageId) {
String sql = "delete from " + MessageShardingUtil.getMessageTable(messageId) + " where _mid = ?";
Connection connection = null;
PreparedStatement statement = null;
try {
connection = DBUtil.getConnection();
statement = connection.prepareStatement(sql);
statement.setLong(1, messageId);
int count = statement.executeUpdate();
LOG.info("Update rows {}", count);
} catch (SQLException e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
} finally {
DBUtil.closeDB(connection, statement);
}
}
List<WFCMessage.Message> loadRemoteMessages(String user, WFCMessage.Conversation conversation, long beforeUid, int count) {
List<WFCMessage.Message> messages = loadRemoteMessagesFromTable(user, conversation, beforeUid, count, MessageShardingUtil.getMessageTable(beforeUid));
if (messages != null && messages.size() < count) {
@@ -634,6 +783,43 @@ public class DatabaseStore {
});
}
void removeFavGroup(final String groupId, final List<String> memberIds) {
Connection connection = null;
PreparedStatement statement = null;
try {
connection = DBUtil.getConnection();
StringBuilder sb = new StringBuilder("update t_user_setting set _value = ?, _dt = ? where _uid in (");
for (int i = 0; i < memberIds.size(); i++) {
sb.append("?");
if (i != memberIds.size() - 1) {
sb.append(",");
}
}
sb.append(")");
sb.append(" and _scope = 6 and _key = ?");
statement = connection.prepareStatement(sb.toString());
int index = 1;
statement.setString(index++, "0");
statement.setLong(index++, System.currentTimeMillis());
for (int i = 0; i < memberIds.size(); i++) {
statement.setString(index++, memberIds.get(i));
}
statement.setString(index++, groupId);
int count = statement.executeUpdate();
LOG.info("Update rows {}", count);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Utility.printExecption(LOG, e);
} finally {
DBUtil.closeDB(connection, statement);
}
}
void persistUserSetting(final String userId, WFCMessage.UserSettingEntry entry) {
mScheduler.execute(()->{
Connection connection = null;
@@ -725,6 +911,41 @@ public class DatabaseStore {
}
void removeGroupUserSettings(String groupId, List<String> users) {
Connection connection = null;
PreparedStatement statement = null;
try {
connection = DBUtil.getConnection();
StringBuilder sb = new StringBuilder("delete from t_user_setting where _scope in (1,3,5,7) and _uid in (");
for (int i = 0; i < users.size(); i++) {
sb.append("?");
if (i != users.size() - 1) {
sb.append(",");
}
}
sb.append(") and _key like '1-_-");
sb.append(groupId);
sb.append("'");
statement = connection.prepareStatement(sb.toString());
int index = 1;
for (String userId:users) {
statement.setString(index++, userId);
}
int count = statement.executeUpdate();
LOG.info("Update rows {}", count);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Utility.printExecption(LOG, e);
} finally {
DBUtil.closeDB(connection, statement);
}
}
void persistGroupInfo(final WFCMessage.GroupInfo groupInfo) {
mScheduler.execute(()->{
Connection connection = null;
@@ -739,7 +960,11 @@ public class DatabaseStore {
", `_extra`" +
", `_dt`" +
", `_member_count`" +
", `_member_dt`) values(?, ?, ?, ?, ?, ?, ?, ?, ?)" +
", `_mute`" +
", `_join_type`" +
", `_private_chat`" +
", `_searchable`" +
", `_member_dt`) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" +
" ON DUPLICATE KEY UPDATE " +
"`_name` = ?," +
"`_portrait` = ?," +
@@ -748,6 +973,10 @@ public class DatabaseStore {
"`_extra` = ?," +
"`_dt` = ?," +
"`_member_count` = ?," +
"`_mute` = ?" +
", `_join_type` = ?" +
", `_private_chat` = ?" +
", `_searchable` = ?, " +
"`_member_dt` = ?";
@@ -762,6 +991,10 @@ public class DatabaseStore {
statement.setString(index++, groupInfo.getExtra());
statement.setLong(index++, groupInfo.getUpdateDt() == 0 ? System.currentTimeMillis() : groupInfo.getUpdateDt());
statement.setInt(index++, groupInfo.getMemberCount());
statement.setInt(index++, groupInfo.getMute());
statement.setInt(index++, groupInfo.getJoinType());
statement.setInt(index++, groupInfo.getPrivateChat());
statement.setInt(index++, groupInfo.getSearchable());
statement.setLong(index++, groupInfo.getMemberUpdateDt() == 0 ? System.currentTimeMillis() : groupInfo.getUpdateDt());
statement.setString(index++, groupInfo.getName());
@@ -771,6 +1004,10 @@ public class DatabaseStore {
statement.setString(index++, groupInfo.getExtra());
statement.setLong(index++, groupInfo.getUpdateDt() == 0 ? System.currentTimeMillis() : groupInfo.getUpdateDt());
statement.setInt(index++, groupInfo.getMemberCount());
statement.setInt(index++, groupInfo.getMute());
statement.setInt(index++, groupInfo.getJoinType());
statement.setInt(index++, groupInfo.getPrivateChat());
statement.setInt(index++, groupInfo.getSearchable());
statement.setLong(index++, groupInfo.getMemberUpdateDt() == 0 ? System.currentTimeMillis() : groupInfo.getUpdateDt());
int count = statement.executeUpdate();
LOG.info("Update rows {}", count);
@@ -916,7 +1153,7 @@ public class DatabaseStore {
statement.setInt(index++, request.getPlatform());
}
if (session.getPushType() != request.getPushType()) {
statement.setInt(index++, request.getPushType());
statement.setInt(index++, request.getPushType() >= 32 ? 0 : request.getPushType());
}
if (!strEqual(session.getDeviceName(), request.getDeviceName())) {
statement.setString(index++, request.getDeviceName());
@@ -1028,7 +1265,6 @@ public class DatabaseStore {
}
void persistGroupMember(final String groupId, final List<WFCMessage.GroupMember> memberList) {
mScheduler.execute(()->{
Connection connection = null;
PreparedStatement statement = null;
try {
@@ -1084,7 +1320,70 @@ public class DatabaseStore {
} finally {
DBUtil.closeDB(connection, statement);
}
});
}
int removeGroupMember(String groupId, List<String> groupMembers) {
Connection connection = null;
PreparedStatement statement = null;
PreparedStatement statement2 = null;
try {
connection = DBUtil.getConnection();
connection.setAutoCommit(false);
StringBuilder sqlBuilder = new StringBuilder("update t_group_member set `_type` = ?, `_dt` = ? where `_mid` in (");
for (int i = 0; i < groupMembers.size(); i++) {
sqlBuilder.append("?");
if (i != groupMembers.size()-1) {
sqlBuilder.append(",");
}
}
sqlBuilder.append(")");
sqlBuilder.append(" and _gid = ?");
statement = connection.prepareStatement(sqlBuilder.toString());
int index = 1;
long current = System.currentTimeMillis();
statement.setInt(index++, ProtoConstants.GroupMemberType.GroupMemberType_Removed);
statement.setLong(index++, current);
for (String memberId:groupMembers) {
statement.setString(index++, memberId);
}
statement.setString(index++, groupId);
int count = statement.executeUpdate();
LOG.info("Update rows {}", count);
String sql = "update t_group set `_member_count` = `_member_count` - ?, `_member_dt` = ? , `_dt` = ? where `_gid` = ?";
statement2 = connection.prepareStatement(sql);
index = 1;
statement2.setInt(index++, count);
statement2.setLong(index++, current);
statement2.setLong(index++, current);
statement2.setString(index++, groupId);
statement2.executeUpdate();
connection.commit();
connection.setAutoCommit(true);
} catch (SQLException e) {
// TODO Auto-generated catch block
if (connection != null) {
try {
connection.commit();
connection.setAutoCommit(true);
} catch (SQLException e1) {
e1.printStackTrace();
}
}
e.printStackTrace();
Utility.printExecption(LOG, e);
} finally {
DBUtil.closeDB(connection, statement);
DBUtil.closeDB(connection, statement2);
}
return 0;
}
WFCMessage.GroupInfo getPersistGroupInfo(String groupId) {
@@ -1101,6 +1400,10 @@ public class DatabaseStore {
", `_dt`" +
", `_member_count`" +
", `_member_dt`" +
", `_mute`" +
", `_join_type`" +
", `_private_chat`" +
", `_searchable`" +
" from t_group where `_gid` = ?";
statement = connection.prepareStatement(sql);
@@ -1136,18 +1439,27 @@ public class DatabaseStore {
strValue = (strValue == null ? "" : strValue);
builder.setExtra(strValue);
long longValue = rs.getLong(index++);
builder.setUpdateDt(longValue);
intValue = rs.getInt(index++);
builder.setMemberCount(intValue);
longValue = rs.getLong(index++);
builder.setMemberUpdateDt(longValue);
intValue = rs.getInt(index++);
builder.setMute(intValue);
intValue = rs.getInt(index++);
builder.setJoinType(intValue);
intValue = rs.getInt(index++);
builder.setPrivateChat(intValue);
intValue = rs.getInt(index++);
builder.setSearchable(intValue);
return builder.build();
}
} catch (SQLException e) {
@@ -1492,7 +1804,6 @@ public class DatabaseStore {
void updateUser(final WFCMessage.User user) {
LOG.info("Database update user info {} {}", user.getUid(), user.getUpdateDt());
mScheduler.execute(()->{
Connection connection = null;
PreparedStatement statement = null;
LOG.info("Database update user info {}", user.getDisplayName());
@@ -1557,6 +1868,26 @@ public class DatabaseStore {
statement.setInt(index++, user.getType());
statement.setLong(index++, user.getUpdateDt() == 0 ? System.currentTimeMillis() : user.getUpdateDt());
int count = statement.executeUpdate();
LOG.info("Update rows {}", count);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Utility.printExecption(LOG, e);
} finally {
DBUtil.closeDB(connection, statement);
}
}
void deleteUserStatus(String userId) {
mScheduler.execute(()->{
Connection connection = null;
PreparedStatement statement = null;
try {
connection = DBUtil.getConnection();
String sql = "delete from t_user_status where _uid = ?";
statement = connection.prepareStatement(sql);
statement.setString(1, userId);
int count = statement.executeUpdate();
LOG.info("Update rows {}", count);
} catch (SQLException e) {
@@ -1903,7 +2234,6 @@ public class DatabaseStore {
}
void persistFriendRequestUnreadStatus(String userId, long readDt, long updateDt) {
mScheduler.execute(()->{
Connection connection = null;
PreparedStatement statement = null;
try {
@@ -1930,11 +2260,9 @@ public class DatabaseStore {
} finally {
DBUtil.closeDB(connection, statement);
}
});
}
//
void persistOrUpdateFriendRequest(final WFCMessage.FriendRequest request) {
mScheduler.execute(()->{
Connection connection = null;
PreparedStatement statement = null;
try {
@@ -1971,11 +2299,9 @@ public class DatabaseStore {
} finally {
DBUtil.closeDB(connection, statement);
}
});
}
void persistOrUpdateFriendData(final FriendData request) {
mScheduler.execute(()->{
Connection connection = null;
PreparedStatement statement = null;
try {
@@ -2006,7 +2332,6 @@ public class DatabaseStore {
} finally {
DBUtil.closeDB(connection, statement);
}
});
}
boolean removeGroupInfoFromDB(String groupId) {
@@ -2054,7 +2379,6 @@ public class DatabaseStore {
void updateChannelInfo(final WFCMessage.ChannelInfo channelInfo) {
LOG.info("Database update channel info {} {}", channelInfo.getTargetId(), channelInfo.getUpdateDt());
mScheduler.execute(()->{
Connection connection = null;
PreparedStatement statement = null;
try {
@@ -2117,7 +2441,6 @@ public class DatabaseStore {
} finally {
DBUtil.closeDB(connection, statement);
}
});
}
void removeChannelInfo(final String channelId) {
@@ -2443,7 +2766,7 @@ public class DatabaseStore {
try {
connection = DBUtil.getConnection();
String sql = "select `_word` from t_sensitiveword";
String sql = "select `_word` from t_sensitiveword order by `id` desc";
statement = connection.prepareStatement(sql);
rs = statement.executeQuery();
File diff suppressed because it is too large Load Diff
@@ -27,7 +27,7 @@ public class UserStatusLoader implements MapStore<String, Integer> {
@Override
public void delete(String s) {
getDatabaseStore().deleteUser(s);
getDatabaseStore().deleteUserStatus(s);
}
@Override
@@ -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");
@@ -1,41 +0,0 @@
/*
* Copyright (c) 2012-2017 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.server.netty;
import com.bugsnag.Bugsnag;
import io.moquette.server.config.IConfig;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import static io.moquette.BrokerConstants.BUGSNAG_TOKEN_PROPERTY_NAME;
@Sharable
public class BugSnagErrorsHandler extends ChannelInboundHandlerAdapter {
private Bugsnag bugsnag;
public void init(IConfig props) {
final String token = props.getProperty(BUGSNAG_TOKEN_PROPERTY_NAME);
this.bugsnag = new Bugsnag(token);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
bugsnag.notify(cause);
ctx.fireExceptionCaught(cause);
}
}
@@ -139,25 +139,11 @@ public class NettyAcceptor implements ServerAcceptor {
final NettyMQTTHandler mqttHandler = new NettyMQTTHandler(processor);
final boolean useFineMetrics = Boolean.parseBoolean(props.getProperty(METRICS_ENABLE_PROPERTY_NAME, "false"));
if (useFineMetrics) {
DropWizardMetricsHandler metricsHandler = new DropWizardMetricsHandler();
metricsHandler.init(props);
this.metrics = Optional.of(metricsHandler);
} else {
this.metrics = Optional.empty();
}
this.metrics = Optional.empty();
this.errorsCather = Optional.empty();
final boolean useBugSnag = Boolean.parseBoolean(props.getProperty(BUGSNAG_ENABLE_PROPERTY_NAME, "false"));
if (useBugSnag) {
BugSnagErrorsHandler bugSnagHandler = new BugSnagErrorsHandler();
bugSnagHandler.init(props);
this.errorsCather = Optional.of(bugSnagHandler);
} else {
this.errorsCather = Optional.empty();
}
initializePlainTCPTransport(mqttHandler, props);
initializeWebSocketTransport(mqttHandler, props);
String sslTcpPortProp = props.getProperty(BrokerConstants.SSL_PORT_PROPERTY_NAME);
String wssPortProp = props.getProperty(BrokerConstants.WSS_PORT_PROPERTY_NAME);
if (sslTcpPortProp != null || wssPortProp != null) {
@@ -239,42 +225,6 @@ public class NettyAcceptor implements ServerAcceptor {
});
}
private void initializeWebSocketTransport(final NettyMQTTHandler handler, IConfig props) throws IOException {
LOG.info("Configuring Websocket MQTT transport");
String webSocketPortProp = props.getProperty(WEB_SOCKET_PORT_PROPERTY_NAME, DISABLED_PORT_BIND);
if (DISABLED_PORT_BIND.equals(webSocketPortProp)) {
// Do nothing no WebSocket configured
LOG.info("Property {} has been setted to {}. Websocket MQTT will be disabled",
BrokerConstants.WEB_SOCKET_PORT_PROPERTY_NAME, DISABLED_PORT_BIND);
return;
}
int port = Integer.parseInt(webSocketPortProp);
final MoquetteIdleTimeoutHandler timeoutHandler = new MoquetteIdleTimeoutHandler();
String host = props.getProperty(BrokerConstants.HOST_PROPERTY_NAME);
initFactory(host, port, "Websocket MQTT", new PipelineInitializer() {
@Override
void init(ChannelPipeline pipeline) {
pipeline.addLast(new HttpServerCodec());
pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
pipeline.addLast("webSocketHandler",
new WebSocketServerProtocolHandler("/org/fusesource/mqtt", MQTT_SUBPROTOCOL_CSV_LIST));
pipeline.addLast("ws2bytebufDecoder", new WebSocketFrameToByteBufDecoder());
pipeline.addLast("bytebuf2wsEncoder", new ByteBufToWebSocketFrameEncoder());
pipeline.addFirst("idleStateHandler", new IdleStateHandler(nettyChannelTimeoutSeconds, 0, 0));
pipeline.addAfter("idleStateHandler", "idleEventHandler", timeoutHandler);
pipeline.addFirst("bytemetrics", new BytesMetricsHandler(m_bytesMetricsCollector));
pipeline.addLast("decoder", new MqttDecoder());
pipeline.addLast("encoder", MqttEncoder.INSTANCE);
pipeline.addLast("metrics", new MessageMetricsHandler(m_metricsCollector));
pipeline.addLast("messageLogger", new MQTTMessageLogger());
pipeline.addLast("handler", handler);
}
});
}
private void initializeSSLTCPTransport(final NettyMQTTHandler handler, IConfig props, final SSLContext sslContext)
throws IOException {
LOG.info("Configuring SSL MQTT transport");
@@ -16,6 +16,7 @@
package io.moquette.server.netty.metrics;
import io.moquette.server.netty.NettyUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
@@ -23,10 +24,15 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static io.moquette.server.netty.NettyUtils.ATTR_USERNAME;
public class BytesMetricsHandler extends ChannelDuplexHandler {
private static final Logger LOG = LoggerFactory.getLogger(BytesMetricsHandler.class);
private static final AttributeKey<BytesMetrics> ATTR_KEY_METRICS = AttributeKey.valueOf("BytesMetrics");
private static final AttributeKey<String> ATTR_KEY_USERNAME = AttributeKey.valueOf(ATTR_USERNAME);
private BytesMetricsCollector m_collector;
@@ -59,6 +65,12 @@ public class BytesMetricsHandler extends ChannelDuplexHandler {
@Override
public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
BytesMetrics metrics = ctx.channel().attr(ATTR_KEY_METRICS).get();
String userId = ctx.channel().attr(ATTR_KEY_USERNAME).get();
if (userId == null) {
userId = "";
}
LOG.info("channel<{}> closing after read {} bytes and wrote {} bytes", userId, metrics.readBytes(), metrics.wroteBytes());
m_collector.sumReadBytes(metrics.readBytes());
m_collector.sumWroteBytes(metrics.wroteBytes());
super.close(ctx, promise);
@@ -1,95 +0,0 @@
/*
* Copyright (c) 2012-2017 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.server.netty.metrics;
import com.codahale.metrics.Counter;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.librato.metrics.reporter.Librato;
import io.moquette.server.config.IConfig;
import io.moquette.server.netty.NettyUtils;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.mqtt.MqttMessage;
import io.netty.handler.codec.mqtt.MqttMessageType;
import java.util.concurrent.TimeUnit;
import static io.moquette.BrokerConstants.*;
import static io.netty.channel.ChannelHandler.Sharable;
/**
* Pipeline handler use to track some MQTT metrics.
*/
@Sharable
public final class DropWizardMetricsHandler extends ChannelInboundHandlerAdapter {
private MetricRegistry metrics;
private Meter publishesMetrics;
private Meter subscribeMetrics;
private Counter connectedClientsMetrics;
public void init(IConfig props) {
this.metrics = new MetricRegistry();
this.publishesMetrics = metrics.meter("publish.requests");
this.subscribeMetrics = metrics.meter("subscribe.requests");
this.connectedClientsMetrics = metrics.counter("connect.num_clients");
// ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics)
// .convertRatesTo(TimeUnit.SECONDS)
// .convertDurationsTo(TimeUnit.MILLISECONDS)
// .build();
// reporter.start(1, TimeUnit.MINUTES);
final String email = props.getProperty(METRICS_LIBRATO_EMAIL_PROPERTY_NAME);
final String token = props.getProperty(METRICS_LIBRATO_TOKEN_PROPERTY_NAME);
final String source = props.getProperty(METRICS_LIBRATO_SOURCE_PROPERTY_NAME);
Librato.reporter(this.metrics, email, token)
.setSource(source)
.start(10, TimeUnit.SECONDS);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object message) {
MqttMessage msg = (MqttMessage) message;
MqttMessageType messageType = msg.fixedHeader().messageType();
switch (messageType) {
case PUBLISH:
this.publishesMetrics.mark();
break;
case SUBSCRIBE:
this.subscribeMetrics.mark();
break;
case CONNECT:
this.connectedClientsMetrics.inc();
break;
case DISCONNECT:
this.connectedClientsMetrics.dec();
break;
default:
break;
}
ctx.fireChannelRead(message);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
String clientID = NettyUtils.clientID(ctx.channel());
if (clientID != null && !clientID.isEmpty()) {
this.connectedClientsMetrics.dec();
}
ctx.fireChannelInactive();
}
}
@@ -22,10 +22,16 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static io.moquette.server.netty.NettyUtils.ATTR_USERNAME;
public class MessageMetricsHandler extends ChannelDuplexHandler {
private static final Logger LOG = LoggerFactory.getLogger(MessageMetricsHandler.class);
private static final AttributeKey<MessageMetrics> ATTR_KEY_METRICS = AttributeKey.valueOf("MessageMetrics");
private static final AttributeKey<String> ATTR_KEY_USERNAME = AttributeKey.valueOf(ATTR_USERNAME);
private MessageMetricsCollector m_collector;
@@ -58,6 +64,12 @@ public class MessageMetricsHandler extends ChannelDuplexHandler {
@Override
public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
MessageMetrics metrics = ctx.channel().attr(ATTR_KEY_METRICS).get();
String userId = ctx.channel().attr(ATTR_KEY_USERNAME).get();
if (userId == null) {
userId = "";
}
LOG.info("channel<{}> closing after read {} messages and wrote {} messages", userId, metrics.messagesRead(), metrics.messagesWrote());
m_collector.sumReadMessages(metrics.messagesRead());
m_collector.sumWroteMessages(metrics.messagesWrote());
super.close(ctx, promise);
@@ -16,6 +16,7 @@
package io.moquette.spi;
import cn.wildfirechat.pojos.SystemSettingPojo;
import cn.wildfirechat.proto.WFCMessage;
import com.xiaoleilu.loServer.model.FriendData;
import cn.wildfirechat.pojos.InputOutputUserBlockStatus;
@@ -96,26 +97,29 @@ public interface IMessagesStore {
DatabaseStore getDatabaseStore();
WFCMessage.Message storeMessage(String fromUser, String fromClientId, WFCMessage.Message message);
int getNotifyReceivers(String fromUser, WFCMessage.Message message, Set<String> notifyReceivers);
void storeSensitiveMessage(WFCMessage.Message message);
int getNotifyReceivers(String fromUser, WFCMessage.Message.Builder message, Set<String> notifyReceivers);
Set<String> getAllEnds();
WFCMessage.PullMessageResult fetchMessage(String user, String exceptClientId, long fromMessageId, int pullType);
WFCMessage.PullMessageResult loadRemoteMessages(String user, WFCMessage.Conversation conversation, long beforeUid, int count);
long insertUserMessages(String sender, int conversationType, String target, int line, int messageContentType, String userId, long messageId);
WFCMessage.GroupInfo createGroup(String operator, WFCMessage.GroupInfo groupInfo, List<WFCMessage.GroupMember> memberList);
ErrorCode addGroupMembers(String operator, String groupId, List<WFCMessage.GroupMember> memberList);
ErrorCode kickoffGroupMembers(String operator, String groupId, List<String> memberList);
ErrorCode addGroupMembers(String operator, boolean isAdmin, String groupId, List<WFCMessage.GroupMember> memberList);
ErrorCode kickoffGroupMembers(String operator, boolean isAdmin, String groupId, List<String> memberList);
ErrorCode quitGroup(String operator, String groupId);
ErrorCode dismissGroup(String operator, String groupId, boolean isAdmin);
ErrorCode modifyGroupInfo(String operator, String groupId, int modifyType, String value);
ErrorCode modifyGroupInfo(String operator, String groupId, int modifyType, String value, boolean isAdmin);
ErrorCode modifyGroupAlias(String operator, String groupId, String alias);
List<WFCMessage.GroupInfo> getGroupInfos(List<WFCMessage.UserRequest> requests);
WFCMessage.GroupInfo getGroupInfo(String groupId);
ErrorCode getGroupMembers(String groupId, long maxDt, List<WFCMessage.GroupMember> members);
WFCMessage.GroupMember getGroupMember(String groupId, String memberId);
ErrorCode transferGroup(String operator, String groupId, String newOwner, boolean isAdmin);
ErrorCode setGroupManager(String operator, String groupId, int type, List<String> userList, boolean isAdmin);
boolean isMemberInGroup(String member, String groupId);
boolean isForbiddenInGroup(String member, String groupId);
ErrorCode canSendMessageInGroup(String member, String groupId);
ErrorCode recallMessage(long messageUid, String operatorId);
ErrorCode recallMessage(long messageUid, String operatorId, boolean isAdmin);
WFCMessage.Robot getRobot(String robotId);
void addRobot(WFCMessage.Robot robot);
@@ -132,6 +136,8 @@ public interface IMessagesStore {
WFCMessage.User getUserInfoByMobile(String mobile);
List<WFCMessage.User> searchUser(String keyword, boolean buzzy, int page);
boolean updateSystemSetting(int id, String value, String desc);
SystemSettingPojo getSystemSetting(int id);
void createChatroom(String chatroomId, WFCMessage.ChatroomInfo chatroomInfo);
void destoryChatroom(String chatroomId);
WFCMessage.ChatroomInfo getChatroomInfo(String chatroomId);
@@ -152,7 +158,7 @@ public interface IMessagesStore {
ErrorCode saveAddFriendRequest(String userId, WFCMessage.AddFriendRequest request, long[] head);
ErrorCode handleFriendRequest(String userId, WFCMessage.HandleFriendRequest request, WFCMessage.Message.Builder msgBuilder, long[] heads, boolean isAdmin);
ErrorCode deleteFriend(String userId, String friendUid);
ErrorCode deleteFriend(String userId, String friendUid, long[] head);
ErrorCode blackUserRequest(String fromUser, String targetUserId, int status, long[] head);
ErrorCode SyncFriendRequestUnread(String userId, long unreadDt, long[] head);
boolean isBlacked(String fromUser, String userId);
@@ -308,7 +308,7 @@ public class MessagesPublisher {
}
if (isSlient) {
LOG.info("The conversation is slient");
LOG.info("Slient of user or conversation");
continue;
}
@@ -623,7 +623,8 @@ public class ProtocolProcessor {
public void onRpcMsg(String fromUser, String clientId, byte[] message, int messageId, String from, String request, boolean isAdmin) {
if(request.equals(RPCCenter.KICKOFF_USER_REQUEST)) {
mServer.getImBusinessScheduler().execute(()->handleTargetRemovedFromCurrentNode(new TargetEntry(TargetEntry.Type.TARGET_TYPE_USER, from)));
String userId = new String(message);
mServer.getImBusinessScheduler().execute(()->handleTargetRemovedFromCurrentNode(new TargetEntry(TargetEntry.Type.TARGET_TYPE_USER, userId)));
return;
}
qos1PublishHandler.onRpcMsg(fromUser, clientId, message, messageId, from, request, isAdmin);
@@ -33,7 +33,7 @@ public class IDUtils {
return ch - 'a' + 26;
} else if(ch >= '0' && ch <= '9') {
return ch - '0' + 52;
} else if(ch == '_') {
} else if(ch == '+') {
return 62;
}
return 63;
@@ -10,6 +10,7 @@ package win.liyufan.im;
public interface IMTopic {
String SendMessageTopic = "MS";
String MultiCastMessageTopic = "MMC";
String RecallMessageTopic = "MR";
String PullMessageTopic = "MP";
String NotifyMessageTopic = "MN";
@@ -30,7 +31,7 @@ public interface IMTopic {
String GetGroupInfoTopic = "GPGI";
String GetGroupMemberTopic = "GPGM";
String TransferGroupTopic = "GTG";
String SetGroupManagerTopic = "GSM";
String GetUserInfoTopic = "UPUI";
String ModifyMyInfoTopic = "MMI";
@@ -54,10 +54,15 @@ public class MessageShardingUtil {
return "t_messages";
}
mid >>= (nodeIdWidth + rotateIdWidth);
Date date = new Date(mid + T201801010000);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
if (mid != Long.MAX_VALUE) {
mid >>= (nodeIdWidth + rotateIdWidth);
Date date = new Date(mid + T201801010000);
calendar.setTime(date);
} else {
Date date = new Date(System.currentTimeMillis());
calendar.setTime(date);
}
int month = calendar.get(Calendar.MONTH);
int year = calendar.get(Calendar.YEAR);
year %= 3;
@@ -58,6 +58,7 @@ public class SensitiveFilter {
return sensitiveWords;
}
text = text.toLowerCase();
for(int i=0;i<text.length();i++){
int sensitiveWordLength = getSensitiveWordLength(text, i, matchType);
if(sensitiveWordLength>0){
+5 -2
View File
@@ -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>
@@ -15,11 +18,11 @@
<!--MySQL数据库驱动程序-->
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<!--MySQL数据库地址-->
<property name="jdbcUrl">jdbc:mysql://localhost:3306/wfchat?useSSL=false&amp;serverTimezone=GMT&amp;allowPublicKeyRetrieval=true&amp;useUnicode=true&amp;characterEncoding=utf8</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/wfchat?useSSL=false&amp;serverTimezone=GMT%2B8&amp;allowPublicKeyRetrieval=true&amp;useUnicode=true&amp;characterEncoding=utf8</property>
<!--MySQL数据库用户名-->
<property name="user">root</property>
<!--MySQL数据库密码-->
<property name="password">123456</property>
<property name="password">lt123456</property>
<!-- 初始化连接池中的连接数,取值应在minPoolSize与maxPoolSize之间,默认为3-->
<property name="initialPoolSize">10</property>
<!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。默认值: 0 -->
-85
View File
@@ -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.22</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,398 +0,0 @@
package cn.wildfirechat.client;
import cn.wildfirechat.proto.WFCMessage;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import cn.wildfirechat.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.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();
connection.publish("MP", request.toByteArray(), QoS.AT_LEAST_ONCE, false, new Callback<byte[]>(){
@Override
public void onSuccess(byte[] value) {
try {
byte[] data = AES.AESDecrypt(value, privateSecret, true);
try {
WFCMessage.PullMessageResult result = WFCMessage.PullMessageResult.parseFrom(data);
if (receiveMessageCallback != null) {
receiveMessageCallback.onReceiveMessages(result.getMessageList(), 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非法
IMClient client = new IMClient("yzyOyOKK", "7SJk13q+YdHHe6EwDzry9BKogxTNf3UgtYj50cBTZgWNkNuxEkiqg2koKg0lXViONIX1LmwCR1jN0Mw8hvk6KGpiSKFi+IRaRkIb3mNzgIfrq4afhyIHaQfa2HOfsi6Ws+9YobkdDgdq7W70bEdVfiCSU9+JOIY449nxZzfg2Zw=", "DD72C212-26C7-4B38-A5FC-88550896B170", "192.168.1.101", 80);
client.setReceiveMessageCallback(new ReceiveMessageCallback() {
@Override
public void onReceiveMessages(List<WFCMessage.Message> messageList, boolean hasMore) {
}
@Override
public void onRecallMessage(long messageUid) {
}
});
client.setConnectionStatusCallback((ConnectionStatus newStatus) -> {
if (newStatus == ConnectionStatus_Connected) {
WFCMessage.Conversation conversation = WFCMessage.Conversation.newBuilder().setType(0).setTarget("yzyOyOKK").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 +
'}';
}
}

Some files were not shown because too many files have changed in this diff Show More