Compare commits

...
51 Commits
Author SHA1 Message Date
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
1039 changed files with 7570 additions and 444442 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
+14 -4
View File
@@ -1,6 +1,6 @@
## 野火IM解决方案
野火IM是一套跨平台、全开源的即时通讯解决方案,主要包含以下内容。
野火IM是一套跨平台、核心功能开源的即时通讯解决方案,主要包含以下内容。
| 仓库 | 说明 | 备注 |
| ------------------------------------------------------------ | ------------------------------------------------------- | ---------------------------------------------- |
@@ -20,11 +20,21 @@
开发一套IM系统真的很艰辛,请路过的朋友们给点个star,支持我们坚持下去🙏🙏🙏🙏🙏
### 联系我们
问题讨论请加群:822762829
微信公众号:
> 商务合作请优先采用邮箱和我们联系。技术问题请到[野火IM论坛](http://bbs.wildfirechat.cn/)发帖交流
1. heavyrain.lee 邮箱: heavyrain.lee@wildfirechat.cn 微信:wildfirechat
2. imndx 邮箱: imndx@wildfirechat.cn 微信:wfchat
### 问题交流
1. 微信交流群(请先加我们好友,我们再邀请您进群)
![微信二维码](http://static.wildfirechat.cn/wx_qr_wildfirechat.jpeg) ![微信二维码](http://static.wildfirechat.cn/wx_qr_wfc.jpeg)
2. 如果大家发现bug,请在GitHub提issue
3. 其他问题,请到[野火IM论坛](http://bbs.wildfirechat.cn/)进行交流学习
4. 微信公众号
<img src="http://static.wildfirechat.cn/wx_wfc_qrcode.jpg" width = 50% height = 50% />
+21 -4
View File
@@ -2,8 +2,8 @@
# Server configuration
#*********************************************************************
#服务器的接入IP。给客户端提供是${server.ip}和${http_port}。
#客户端会从${http_port}端口获取到长链接端口。
server.ip 192.168.1.100
#客户端会从${http_port}端口获取到长链接端口。这个地址一定要改成该服务的公网IP。
server.ip 0.0.0.0
##原生客户端长链接端口
port 1883
@@ -26,7 +26,7 @@ host 0.0.0.0
##本地绑定端口
local_port 80
##是否使用内置DB。0使用mysql;1使用h2db
##是否使用内置DB。0使用mysql;1使用h2db;2使用mysql+mongodb。社区版只支持0和1,专业版支持2
embed.db 0
##服务器管理接口密钥
@@ -34,6 +34,7 @@ http.admin.secret_key 123456
##服务器API接口参数是否检查时间。当设置为false时,所有的请求会检查时间的有效性;当设置为true时,可以在http.admin.secret_key保持不变的情况下,使用固定的服务API签名
##nonce = "76616", timestamp = "1558350862502", sign = "b98f9b0717f59febccf1440067a7f50d9b31bdde"
##这个地方上线前要改为false
http.admin.no_check_time true
##客户端协议栈密钥,需要与客户端协议栈文件libemqq.cc文件中的aes_key值保持一致,16进制byte用逗号分割。这个密钥对安全性帮助不大,客户不要修改
@@ -42,6 +43,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 +102,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
#*********************************************************************
+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 '';
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 '';
+8 -26
View File
@@ -5,7 +5,7 @@
<relativePath>../</relativePath>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.24</version>
<version>0.28</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,28 +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>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
@@ -164,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 -->
@@ -250,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;
@@ -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;
}
}
@@ -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;
}
}
@@ -60,10 +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 STORAGE_CLASS_NAME = "storage_class";
@@ -100,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";
@@ -110,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.reomte_history_message";
private BrokerConstants() {
@@ -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,6 +235,14 @@ 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<>();
@@ -250,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();
}
}
@@ -27,6 +27,12 @@ public class KickoffGroupMember extends GroupHandler<WFCMessage.RemoveGroupMembe
} else {
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;
}
@@ -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;
}
}
@@ -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,6 +33,10 @@ public class RecallMessageHandler extends IMHandler<WFCMessage.INT64Buf> {
return ErrorCode.ERROR_CODE_NOT_EXIST;
}
publish(fromUser, clientID, message);
//等待客户端实现根据撤回消息更新内容,之后可以删掉这段代码
m_messagesStore.getNotifyReceivers(fromUser, message.toBuilder(), notifyReceivers);
this.publisher.publishRecall2Receivers(int64Buf.getId(), fromUser, notifyReceivers, clientID);
@@ -95,13 +95,15 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
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) {
}
}
}
@@ -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) {
@@ -592,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) {
@@ -688,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;
@@ -779,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;
@@ -986,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());
@@ -1098,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 {
@@ -1154,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) {
@@ -1575,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());
@@ -1649,7 +1877,6 @@ public class DatabaseStore {
} finally {
DBUtil.closeDB(connection, statement);
}
});
}
void deleteUserStatus(String userId) {
@@ -2152,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 {
@@ -2215,7 +2441,6 @@ public class DatabaseStore {
} finally {
DBUtil.closeDB(connection, statement);
}
});
}
void removeChannelInfo(final String channelId) {
@@ -2541,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();
@@ -16,6 +16,7 @@
package io.moquette.persistence;
import cn.wildfirechat.pojos.SystemSettingPojo;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
import com.google.protobuf.ByteString;
@@ -61,7 +62,6 @@ import static win.liyufan.im.MyInfoType.*;
public class MemoryMessagesStore implements IMessagesStore {
private static final String MESSAGES_MAP = "messages_map";
private static final String GROUPS_MAP = "groups_map";
private static final String GROUP_ID_COUNTER = "group_id_counter";
static int dumy = 0;
static final String GROUP_MEMBERS = "group_members";
@@ -83,6 +83,10 @@ public class MemoryMessagesStore implements IMessagesStore {
private static final String CHANNEL_LISTENERS = "channel_listeners";
private static boolean IS_MESSAGE_ROAMING = true;
private static boolean IS_MESSAGE_REMOTE_HISTORY_MESSAGE = true;
static final String USER_ROBOTS = "user_robots";
static final String USER_THINGS = "user_things";
@@ -116,22 +120,20 @@ public class MemoryMessagesStore implements IMessagesStore {
MemoryMessagesStore(Server server, DatabaseStore databaseStore) {
m_Server = server;
this.databaseStore = databaseStore;
IS_MESSAGE_ROAMING = "1".equals(m_Server.getConfig().getProperty(MESSAGE_ROAMING));
IS_MESSAGE_REMOTE_HISTORY_MESSAGE = "1".equals(m_Server.getConfig().getProperty(MESSAGE_Remote_History_Message));
}
@Override
public void initStore() {
//TODO reload data from mysql
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(MemoryMessagesStore.GROUP_MEMBERS);
if (groupMembers.size() == 0) {
databaseStore.reloadGroupMemberFromDB(hzInstance);
databaseStore.reloadFriendsFromDB(hzInstance);
databaseStore.reloadFriendRequestsFromDB(hzInstance);
}
updateSensitiveWord();
}
private Collection<WFCMessage.GroupMember> loadGroupMemberFromDB(HazelcastInstance hzInstance, String groupId) {
Collection<WFCMessage.GroupMember> members = databaseStore.reloadGroupMemberFromDB(hzInstance, groupId);
return members;
}
private void updateSensitiveWord() {
long now = System.currentTimeMillis();
if (now - lastUpdateSensitiveTime > 2 * 60 * 60 * 1000) {
@@ -195,6 +197,9 @@ public class MemoryMessagesStore implements IMessagesStore {
} else {
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
Collection<WFCMessage.GroupMember> members = groupMembers.get(message.getConversation().getTarget());
if (members == null || members.size() == 0) {
members = loadGroupMemberFromDB(hzInstance, message.getConversation().getTarget());
}
for (WFCMessage.GroupMember member : members) {
if (member.getType() != GroupMemberType_Removed) {
notifyReceivers.add(member.getMemberId());
@@ -316,6 +321,10 @@ public class MemoryMessagesStore implements IMessagesStore {
});
}
boolean noRoaming = false;
if (pullType == ProtoConstants.PullType.Pull_Normal && fromMessageId == 0 && !IS_MESSAGE_ROAMING) {
noRoaming = true;
}
while (true) {
Map.Entry<Long, Long> entry = maps.higherEntry(current);
if (entry == null) {
@@ -335,6 +344,10 @@ public class MemoryMessagesStore implements IMessagesStore {
}
}
if (noRoaming && (System.currentTimeMillis() - bundle.getMessage().getServerTimestamp() > 5 * 60 * 1000)) {
continue;
}
size += bundle.getMessage().getSerializedSize();
if (size >= 1 * 1024 * 1024) { //3M
break;
@@ -360,7 +373,12 @@ public class MemoryMessagesStore implements IMessagesStore {
@Override
public WFCMessage.PullMessageResult loadRemoteMessages(String user, WFCMessage.Conversation conversation, long beforeUid, int count) {
WFCMessage.PullMessageResult.Builder builder = WFCMessage.PullMessageResult.newBuilder();
List<WFCMessage.Message> messages = databaseStore.loadRemoteMessages(user, conversation, beforeUid, count);
List<WFCMessage.Message> messages;
if (IS_MESSAGE_REMOTE_HISTORY_MESSAGE) {
messages = databaseStore.loadRemoteMessages(user, conversation, beforeUid, count);
} else {
messages = new ArrayList<>();
}
builder.setCurrent(0).setHead(0);
if(messages != null) {
builder.addAllMessage(messages);
@@ -630,20 +648,21 @@ public class MemoryMessagesStore implements IMessagesStore {
}
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
if (!isAdmin) {
boolean isMember = false;
boolean isManager = false;
WFCMessage.GroupMember gm = getGroupMember(groupId, operator);
if (gm != null) {
if (gm.getType() == GroupMemberType_Removed) {
if (gm.getType() == GroupMemberType_Removed && !(memberList.size() == 1 && operator.equals(memberList.get(0).getMemberId()))) {
return ErrorCode.ERROR_CODE_NOT_IN_GROUP;
} else if (gm.getType() == GroupMemberType_Silent) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
isManager = (gm.getType() == GroupMemberType_Manager || gm.getType() == GroupMemberType_Owner);
isMember = true;
if (gm.getType() != GroupMemberType_Removed) {
isMember = true;
}
}
if (groupInfo.getType() == ProtoConstants.GroupType.GroupType_Restricted && ((groupInfo.getOwner() == null || !groupInfo.getOwner().equals(operator)) && !isManager)) {
@@ -655,7 +674,7 @@ public class MemoryMessagesStore implements IMessagesStore {
}
}
if (!isMember) {
if (!isMember && !(memberList.size() == 1 && operator.equals(memberList.get(0).getMemberId()))) {
return ErrorCode.ERROR_CODE_NOT_IN_GROUP;
}
}
@@ -671,7 +690,13 @@ public class MemoryMessagesStore implements IMessagesStore {
}
memberList = tmp;
for (WFCMessage.GroupMember member : groupMembers.get(groupId)) {
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
Collection<WFCMessage.GroupMember> members = groupMembers.get(groupId);
if (members == null || members.size() == 0) {
members = loadGroupMemberFromDB(hzInstance, groupId);
}
for (WFCMessage.GroupMember member : members) {
if (newInviteUsers.contains(member.getMemberId())) {
groupMembers.remove(groupId, member);
}
@@ -696,43 +721,33 @@ public class MemoryMessagesStore implements IMessagesStore {
@Override
public ErrorCode kickoffGroupMembers(String operator, boolean isAdmin, String groupId, List<String> memberList) {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
IMap<String, WFCMessage.GroupInfo> mIMap = hzInstance.getMap(GROUPS_MAP);
removeGroupMember(groupId, memberList);
removeFavGroup(groupId, memberList);
removeGroupUserSettings(groupId, memberList);
return ErrorCode.ERROR_CODE_SUCCESS;
}
WFCMessage.GroupInfo groupInfo = mIMap.get(groupId);
if (groupInfo == null) {
return ErrorCode.ERROR_CODE_NOT_EXIST;
}
if (!isAdmin && ((groupInfo.getType() == ProtoConstants.GroupType.GroupType_Restricted || groupInfo.getType() == ProtoConstants.GroupType.GroupType_Normal)
&& (groupInfo.getOwner() == null || !groupInfo.getOwner().equals(operator)))) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
void removeGroupMember(String groupId, List<String> memberIds) {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
databaseStore.removeGroupMember(groupId, memberIds);
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
groupMembers.remove(groupId);
IMap<String, WFCMessage.GroupInfo> mIMap = hzInstance.getMap(GROUPS_MAP);
mIMap.evict(groupId);
int removeCount = 0;
long updateDt = System.currentTimeMillis();
ArrayList<WFCMessage.GroupMember> list = new ArrayList<>();
List<WFCMessage.GroupMember> allMembers = new ArrayList<>(groupMembers.get(groupId));
for (WFCMessage.GroupMember member : allMembers) {
if (memberList.contains(member.getMemberId())) {
boolean removed = groupMembers.remove(groupId, member);
if (removed) {
removeCount++;
member = member.toBuilder().setType(GroupMemberType_Removed).setUpdateDt(updateDt).build();
groupMembers.put(groupId, member);
list.add(member);
}
}
}
void removeFavGroup(String groupId, List<String> memberIds) {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
MultiMap<String, WFCMessage.UserSettingEntry> userSettingMap = hzInstance.getMultiMap(USER_SETTING);
databaseStore.removeFavGroup(groupId, memberIds);
for (String member : memberIds) {
userSettingMap.remove(member);
}
if (removeCount > 0) {
databaseStore.persistGroupMember(groupId, list);
databaseStore.updateGroupMemberCountDt(groupId, groupInfo.getMemberCount()-removeCount, updateDt);
mIMap.put(groupId, groupInfo.toBuilder().setMemberUpdateDt(updateDt).setUpdateDt(updateDt).setMemberCount(groupInfo.getMemberCount() - removeCount).build());
}
return ErrorCode.ERROR_CODE_SUCCESS;
}
@Override
@@ -743,8 +758,11 @@ public class MemoryMessagesStore implements IMessagesStore {
WFCMessage.GroupInfo groupInfo = mIMap.get(groupId);
if (groupInfo == null) {
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
for (WFCMessage.GroupMember member : groupMembers.get(groupId)
) {
Collection<WFCMessage.GroupMember> members = groupMembers.get(groupId);
if (members == null || members.size() == 0) {
members = loadGroupMemberFromDB(hzInstance, groupId);
}
for (WFCMessage.GroupMember member :members) {
if (member.getMemberId().equals(operator)) {
groupMembers.remove(groupId, member);
break;
@@ -756,33 +774,24 @@ public class MemoryMessagesStore implements IMessagesStore {
if (groupInfo.getType() != ProtoConstants.GroupType.GroupType_Free && groupInfo.getOwner() != null && groupInfo.getOwner().equals(operator)) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
long updateDt = System.currentTimeMillis();
boolean removed = false;
for (WFCMessage.GroupMember member : groupMembers.get(groupId)
) {
if (member.getMemberId().equals(operator)) {
removed = groupMembers.remove(groupId, member);
if (removed) {
ArrayList<WFCMessage.GroupMember> list = new ArrayList<>();
list.add(member);
databaseStore.persistGroupMember(groupId, list);
databaseStore.updateGroupMemberCountDt(groupId, groupInfo.getMemberCount()-1, updateDt);
member = member.toBuilder().setType(GroupMemberType_Removed).setUpdateDt(updateDt).build();
groupMembers.put(groupId, member);
}
break;
}
}
if (removed) {
mIMap.put(groupId, groupInfo.toBuilder().setMemberUpdateDt(updateDt).setUpdateDt(updateDt).setMemberCount(groupInfo.getMemberCount() - 1).build());
}
removeGroupMember(groupId, Arrays.asList(operator));
removeFavGroup(groupId, Arrays.asList(operator));
removeGroupUserSettings(groupId, Arrays.asList(operator));
return ErrorCode.ERROR_CODE_SUCCESS;
}
private void removeGroupUserSettings(String groupId, List<String> users) {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
MultiMap<String, WFCMessage.UserSettingEntry> userSettingMap = hzInstance.getMultiMap(USER_SETTING);
databaseStore.removeGroupUserSettings(groupId, users);
for (String userId:users) {
userSettingMap.remove(userId);
}
}
@Override
public ErrorCode dismissGroup(String operator, String groupId, boolean isAdmin) {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
@@ -805,11 +814,25 @@ public class MemoryMessagesStore implements IMessagesStore {
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
Collection<WFCMessage.GroupMember> members = groupMembers.get(groupId);
if (members == null || members.size() == 0) {
members = loadGroupMemberFromDB(hzInstance, groupId);
}
groupMembers.remove(groupId);
mIMap.remove(groupId);
databaseStore.removeGroupMemberFromDB(groupId);
ArrayList<String> ids = new ArrayList<>();
if (members != null) {
for (WFCMessage.GroupMember member : members) {
ids.add(member.getMemberId());
}
}
removeFavGroup(groupId, ids);
removeGroupUserSettings(groupId, ids);
return ErrorCode.ERROR_CODE_SUCCESS;
}
@@ -898,8 +921,10 @@ public class MemoryMessagesStore implements IMessagesStore {
long updateDt = System.currentTimeMillis();
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
Collection<WFCMessage.GroupMember> members = groupMembers.get(groupId);
for (WFCMessage.GroupMember member : members
) {
if (members == null || members.size() == 0) {
members = loadGroupMemberFromDB(hzInstance, groupId);
}
for (WFCMessage.GroupMember member : members) {
if (member.getMemberId().equals(operator)) {
groupMembers.remove(groupId, member);
member = member.toBuilder().setAlias(alias).setUpdateDt(updateDt).build();
@@ -943,12 +968,6 @@ public class MemoryMessagesStore implements IMessagesStore {
IMap<String, WFCMessage.GroupInfo> mIMap = hzInstance.getMap(GROUPS_MAP);
WFCMessage.GroupInfo groupInfo = mIMap.get(groupId);
if (groupInfo == null) {
groupInfo = databaseStore.getPersistGroupInfo(groupId);
if (groupInfo != null) {
mIMap.put(groupId, groupInfo);
}
}
return groupInfo;
}
@@ -965,8 +984,16 @@ public class MemoryMessagesStore implements IMessagesStore {
}
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
Collection<WFCMessage.GroupMember> memberCollection = groupMembers.get(groupId);
if (memberCollection == null || memberCollection.size() == 0) {
memberCollection = loadGroupMemberFromDB(hzInstance, groupId);
}
for (WFCMessage.GroupMember member:memberCollection) {
if (member.getUpdateDt() > maxDt) {
members.add(member);
}
}
members.addAll(groupMembers.get(groupId));
return ErrorCode.ERROR_CODE_SUCCESS;
}
@@ -977,6 +1004,10 @@ public class MemoryMessagesStore implements IMessagesStore {
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
Collection<WFCMessage.GroupMember> members = groupMembers.get(groupId);
if (members == null || members.size() == 0) {
members = loadGroupMemberFromDB(hzInstance, groupId);
}
if (members != null) {
for (WFCMessage.GroupMember gm : members) {
if (gm.getMemberId().equals(memberId)) {
@@ -1007,10 +1038,29 @@ public class MemoryMessagesStore implements IMessagesStore {
}
//check the new owner is in member list? is that necessary?
groupInfo = groupInfo.toBuilder().setOwner(newOwner).build();
long updateDt = System.currentTimeMillis();
groupInfo = groupInfo.toBuilder().setOwner(newOwner).setUpdateDt(updateDt).build();
mIMap.set(groupId, groupInfo);
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
Collection<WFCMessage.GroupMember> members = groupMembers.get(groupId);
if (members == null || members.size() == 0) {
members = loadGroupMemberFromDB(hzInstance, groupId);
}
for (WFCMessage.GroupMember member : members) {
if (newOwner.equals(member.getMemberId())) {
groupMembers.remove(groupId, member);
member = member.toBuilder().setType(GroupMemberType_Owner).setUpdateDt(updateDt).build();
databaseStore.persistGroupMember(groupId, Arrays.asList(member));
groupMembers.put(groupId, member);
} else if(member.getType() == GroupMemberType_Owner) {
groupMembers.remove(groupId, member);
member = member.toBuilder().setType(GroupMemberType_Normal).setUpdateDt(updateDt).build();
databaseStore.persistGroupMember(groupId, Arrays.asList(member));
groupMembers.put(groupId, member);
}
}
return ErrorCode.ERROR_CODE_SUCCESS;
}
@@ -1035,10 +1085,13 @@ public class MemoryMessagesStore implements IMessagesStore {
long updateDt = System.currentTimeMillis();
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
Collection<WFCMessage.GroupMember> members = groupMembers.get(groupId);
if (members == null || members.size() == 0) {
members = loadGroupMemberFromDB(hzInstance, groupId);
}
for (WFCMessage.GroupMember member : members) {
if (userList.contains(member.getMemberId())) {
groupMembers.remove(groupId, member);
member = member.toBuilder().setType(ProtoConstants.GroupMemberType.GroupMemberType_Manager).setUpdateDt(updateDt).build();
member = member.toBuilder().setType(type == 0 ? ProtoConstants.GroupMemberType.GroupMemberType_Normal : ProtoConstants.GroupMemberType.GroupMemberType_Manager).setUpdateDt(updateDt).build();
databaseStore.persistGroupMember(groupId, Arrays.asList(member));
groupMembers.put(groupId, member);
// userList.remove(member.getMemberId());
@@ -1055,7 +1108,9 @@ public class MemoryMessagesStore implements IMessagesStore {
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
Collection<WFCMessage.GroupMember> members = groupMembers.get(groupId);
if (members == null || members.size() == 0) {
members = loadGroupMemberFromDB(hzInstance, groupId);
}
for (WFCMessage.GroupMember member : members
) {
if (member.getMemberId().equals(memberId) && member.getType() != GroupMemberType_Removed)
@@ -1080,7 +1135,9 @@ public class MemoryMessagesStore implements IMessagesStore {
}
Collection<WFCMessage.GroupMember> members = groupMembers.get(groupId);
if (members == null || members.size() == 0) {
members = loadGroupMemberFromDB(hzInstance, groupId);
}
for (WFCMessage.GroupMember member : members
) {
if (member.getMemberId().equals(memberId)) {
@@ -1104,7 +1161,7 @@ public class MemoryMessagesStore implements IMessagesStore {
}
@Override
public ErrorCode recallMessage(long messageUid, String operatorId) {
public ErrorCode recallMessage(long messageUid, String operatorId, boolean isAdmin) {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
IMap<Long, MessageBundle> mIMap = hzInstance.getMap(MESSAGES_MAP);
@@ -1112,7 +1169,10 @@ public class MemoryMessagesStore implements IMessagesStore {
if (messageBundle != null) {
WFCMessage.Message message = messageBundle.getMessage();
boolean canRecall = false;
if (message.getFromUser().equals(operatorId)) {
if (isAdmin) {
canRecall = true;
}
if (!isAdmin && message.getFromUser().equals(operatorId)) {
canRecall = true;
}
@@ -1129,6 +1189,9 @@ public class MemoryMessagesStore implements IMessagesStore {
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
Collection<WFCMessage.GroupMember> members = groupMembers.get(message.getConversation().getTarget());
if (members == null || members.size() == 0) {
members = loadGroupMemberFromDB(hzInstance, message.getConversation().getTarget());
}
for (WFCMessage.GroupMember member : members) {
if (member.getMemberId().equals(operatorId)) {
if (member.getType() == GroupMemberType_Manager || member.getType() == ProtoConstants.GroupMemberType.GroupMemberType_Owner) {
@@ -1152,6 +1215,8 @@ public class MemoryMessagesStore implements IMessagesStore {
message = message.toBuilder().setContent(message.getContent().toBuilder().setContent(operatorId).setType(80).clearSearchableContent().setData(ByteString.copyFrom(new StringBuffer().append(messageUid).toString().getBytes())).build()).build();
messageBundle.setMessage(message);
databaseStore.deleteMessage(messageUid);
mIMap.put(messageUid, messageBundle, 7, TimeUnit.DAYS);
return ErrorCode.ERROR_CODE_SUCCESS;
} else {
@@ -1357,6 +1422,16 @@ public class MemoryMessagesStore implements IMessagesStore {
return databaseStore.searchUserFromDB(keyword, buzzy, page);
}
@Override
public boolean updateSystemSetting(int id, String value, String desc) {
return databaseStore.updateSystemSetting(id, value, desc);
}
@Override
public SystemSettingPojo getSystemSetting(int id) {
return databaseStore.getSystemSetting(id);
}
@Override
public void createChatroom(String chatroomId, WFCMessage.ChatroomInfo chatroomInfo) {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
@@ -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");
@@ -139,19 +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();
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) {
@@ -233,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("/", 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;
@@ -118,7 +119,7 @@ public interface IMessagesStore {
boolean isMemberInGroup(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);
@@ -135,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);
@@ -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";
+1 -1
View File
@@ -19,7 +19,7 @@
<!--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.24</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>client</artifactId>
<dependencies>
<dependency>
<groupId>cn.wildfirechat</groupId>
<artifactId>common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.fusesource.hawtbuf/hawtbuf -->
<dependency>
<groupId>org.fusesource.hawtbuf</groupId>
<artifactId>hawtbuf</artifactId>
<version>1.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.fusesource.hawtdispatch/hawtdispatch-transport -->
<dependency>
<groupId>org.fusesource.hawtdispatch</groupId>
<artifactId>hawtdispatch-transport</artifactId>
<version>1.22</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.6</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.10</version>
</dependency>
</dependencies>
</project>
@@ -1,424 +0,0 @@
package cn.wildfirechat.client;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.xiaoleilu.loServer.pojos.InputRoute;
import io.moquette.spi.impl.security.AES;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.fusesource.hawtbuf.Buffer;
import org.fusesource.hawtbuf.UTF8Buffer;
import org.fusesource.mqtt.client.*;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import static cn.wildfirechat.client.IMClient.ConnectionStatus.ConnectionStatus_Connected;
import static cn.wildfirechat.client.IMClient.ConnectionStatus.ConnectionStatus_Connecting;
import static cn.wildfirechat.client.IMClient.ConnectionStatus.ConnectionStatus_Unconnected;
public class IMClient implements Listener {
private final String userId;
private final String token;
private final String clientId;
private final String host;
private final int port;
private ConnectionStatusCallback connectionStatusCallback;
private ReceiveMessageCallback receiveMessageCallback;
private ConnectionStatus connectionStatus;
protected String mqttServerIp;
protected long mqttServerPort;
private static byte[] commonSecret= {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F};
protected String privateSecret;
private long messageHead;
private transient MQTT mqtt = new MQTT();
private transient CallbackConnection connection = null;
public interface ReceiveMessageCallback {
void onReceiveMessages(List<WFCMessage.Message> messageList, boolean hasMore);
void onRecallMessage(long messageUid);
}
public interface ConnectionStatusCallback {
void onConnectionStatusChanged(ConnectionStatus newStatus);
}
public interface SendMessageCallback {
void onSuccess(long messageUid, long timestamp);
void onFailure(int errorCode);
}
public enum ConnectionStatus {
ConnectionStatus_Unconnected,
ConnectionStatus_Connecting,
ConnectionStatus_Connected,
}
public IMClient(String userId, String token, String clientId, String host, int port) {
this.userId = userId;
byte[] data = Base64.getDecoder().decode(token);
data = AES.AESDecrypt(data, commonSecret, false);
String s = new String(data);
String[] ss = s.split("\\|");
this.token = ss[0];
this.privateSecret = ss[1];
this.clientId = clientId;
this.host = host;
this.port = port;
AES.init(commonSecret);
}
public void connect() {
if(route(userId, token)) {
try {
mqtt.setHost("tcp://" + mqttServerIp + ":" + mqttServerPort);
mqtt.setVersion("3.1.1");
mqtt.setKeepAlive((short)180);
mqtt.setClientId(clientId);
mqtt.setConnectAttemptsMax(100);
mqtt.setReconnectAttemptsMax(100);
mqtt.setUserName(userId);
byte[] password = AES.AESEncrypt(token, privateSecret);
mqtt.setPassword(new UTF8Buffer(password));
connection = mqtt.callbackConnection();
connection.listener(this);
//connecting
connectionStatus = ConnectionStatus_Connecting;
if(connectionStatusCallback != null) {
connectionStatusCallback.onConnectionStatusChanged(connectionStatus);
}
connection.connect(new Callback<byte[]>() {
@Override
public void onSuccess(byte[] value) {
if (value != null) {
try {
WFCMessage.ConnectAckPayload ackPayload = WFCMessage.ConnectAckPayload.parseFrom(value);
System.out.println(ackPayload.getMsgHead());
messageHead = ackPayload.getMsgHead();
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
System.out.println("on connect success");
//connected
connectionStatus = ConnectionStatus_Connected;
if(connectionStatusCallback != null) {
connectionStatusCallback.onConnectionStatusChanged(connectionStatus);
}
}
@Override
public void onFailure(Throwable value) {
System.out.println("on connect failure");
connectionStatus = ConnectionStatus_Unconnected;
if(connectionStatusCallback != null) {
connectionStatusCallback.onConnectionStatusChanged(connectionStatus);
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void disconnect(boolean clearSession, final Callback<Void> onComplete) {
this.connection.disconnect(clearSession, onComplete);
}
public void sendMessage(WFCMessage.Conversation conversation, WFCMessage.MessageContent messageContent, final SendMessageCallback callback) {
WFCMessage.Message message = WFCMessage.Message.newBuilder().setConversation(conversation).setContent(messageContent).setFromUser(userId).build();
byte[] data = message.toByteArray();
data = AES.AESEncrypt(data, privateSecret);
connection.publish("MS", data, QoS.AT_LEAST_ONCE, false, new Callback<byte[]>() {
@Override
public void onSuccess(byte[] value) {
if (value[0] == 0) {
byte[] data = new byte[value.length-1];
for (int i = 0; i < data.length; i++) {
data[i] = value[i+1];
}
data = AES.AESDecrypt(data, privateSecret, true);
ByteBuffer buffer = ByteBuffer.wrap(data, 0,16);
long messageUid = buffer.getLong();
long timestamp = buffer.getLong();
callback.onSuccess(messageUid, timestamp);
} else {
callback.onFailure(value[0]);
}
}
@Override
public void onFailure(Throwable value) {
callback.onFailure(-1);
}
});
}
protected boolean route(String userId, String token) {
HttpPost httpPost;
try{
HttpClient httpClient = HttpClientBuilder.create().build();
httpPost = new HttpPost("http://" + host + ":" + port + "/route");
InputRoute inputRoute = new InputRoute();
inputRoute.setUserId(userId);
inputRoute.setClientId(clientId);
inputRoute.setToken(token);
WFCMessage.RouteRequest routeRequest = WFCMessage.RouteRequest.newBuilder().setPlatform(0).build();
WFCMessage.IMHttpWrapper request = WFCMessage.IMHttpWrapper.newBuilder().setClientId(clientId).setToken(token).setRequest("ROUTE").setData(ByteString.copyFrom(routeRequest.toByteArray())).build();
byte[] data = AES.AESEncrypt(request.toByteArray(), privateSecret);
data = Base64.getEncoder().encode(data);
StringEntity entity = new StringEntity(new String(data), Charset.forName("UTF-8"));
entity.setContentEncoding("UTF-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
byte[] cidByte = AES.AESEncrypt(clientId.getBytes(), commonSecret);
cidByte = Base64.getEncoder().encode(cidByte);
String cid = new String(cidByte);
httpPost.setHeader("cid", cid);
byte[] uidByte = AES.AESEncrypt(userId.getBytes(), commonSecret);
uidByte = Base64.getEncoder().encode(uidByte);
String uid = new String(uidByte);
httpPost.setHeader("uid", uid);
HttpResponse response = httpClient.execute(httpPost);
if(response != null){
if (response.getStatusLine().getStatusCode() != 200) {
System.out.println("Http response error {" + response.getStatusLine().getStatusCode() + "}");
return false;
}
HttpEntity resEntity = response.getEntity();
if(resEntity != null){
try {
byte[] bytes = new byte[resEntity.getContent().available()];
resEntity.getContent().read(bytes);
if (bytes[0] == 0) {
byte[] bytes1 = new byte[bytes.length -1];
for (int i = 0; i < bytes1.length; i++) {
bytes1[i] = bytes[i+1];
}
byte[] rawData = AES.AESDecrypt(bytes1, privateSecret, true);
WFCMessage.RouteResponse routeResponse = WFCMessage.RouteResponse.parseFrom(rawData);
mqttServerIp = routeResponse.getHost();
mqttServerPort = routeResponse.getLongPort();
routeResponse.getShortPort();
return true;
} else {
System.out.println("the route failure:" + bytes[0]);
return false;
}
} catch (IOException e) {
e.printStackTrace();
} catch (UnsupportedOperationException e) {
e.printStackTrace();
}
}
} else {
System.out.println("Http response nil");
}
}catch(Exception ex){
ex.printStackTrace();
}
return false;
}
@Override
public void onConnected() {
System.out.println("onConnected");
connectionStatus = ConnectionStatus_Connecting;
if(connectionStatusCallback != null) {
connectionStatusCallback.onConnectionStatusChanged(connectionStatus);
}
}
@Override
public void onDisconnected() {
System.out.println("onDisconnected");
connectionStatus = ConnectionStatus_Unconnected;
if(connectionStatusCallback != null) {
connectionStatusCallback.onConnectionStatusChanged(connectionStatus);
}
}
@Override
public void onPublish(UTF8Buffer topic, Buffer body, Runnable ack) {
System.out.println("onPublish" + topic.toString());
ack.run();
if (topic.toString().equals("MN")) {
try {
WFCMessage.NotifyMessage notifyMessage = WFCMessage.NotifyMessage.parseFrom(body.toByteArray());
WFCMessage.PullMessageRequest request = WFCMessage.PullMessageRequest.newBuilder().setId(messageHead).setType(notifyMessage.getType()).build();
byte[] data = request.toByteArray();
data = AES.AESEncrypt(data, privateSecret);
connection.publish("MP", data, QoS.AT_LEAST_ONCE, false, new Callback<byte[]>(){
@Override
public void onSuccess(byte[] value) {
if (value == null || value.length == 0) {
System.out.println("Invalide data");
}
if (value[0] != 0) {
System.out.println("Pull message error with errorCode:" + value[0]);
}
byte[] data = new byte[value.length - 1];
for (int i = 0; i < data.length; i++) {
data[i] = value[i+1];
}
try {
data = AES.AESDecrypt(data, privateSecret, true);
try {
WFCMessage.PullMessageResult result = WFCMessage.PullMessageResult.parseFrom(data);
if (receiveMessageCallback != null && result.getMessageList().size() > 0) {
List<WFCMessage.Message> messages = result.getMessageList();
List<WFCMessage.Message> out = new ArrayList<>();
for (WFCMessage.Message msg : messages) {
if (msg.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Private && msg.getConversation().getTarget().equals(userId)) {
msg = msg.toBuilder().setConversation(msg.getConversation().toBuilder().setTarget(msg.getFromUser())).build();
}
out.add(msg);
}
receiveMessageCallback.onReceiveMessages(out, false);
}
messageHead = result.getHead();
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
System.out.println("onSuccess");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Throwable value) {
System.out.println("onFailure");
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void onFailure(Throwable value) {
System.out.println("onDisconnected" + value.toString());
if(connectionStatusCallback != null) {
connectionStatusCallback.onConnectionStatusChanged(ConnectionStatus_Unconnected);
}
}
public void setConnectionStatusCallback(ConnectionStatusCallback connectionStatusCallback) {
this.connectionStatusCallback = connectionStatusCallback;
}
public void setReceiveMessageCallback(ReceiveMessageCallback receiveMessageCallback) {
this.receiveMessageCallback = receiveMessageCallback;
}
public String getUserId() {
return userId;
}
public String getToken() {
return token;
}
public String getClientId() {
return clientId;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public ConnectionStatus getConnectionStatus() {
return connectionStatus;
}
public static void main(String[] args) {
//token与userid和clientid是绑定的,使用时一定要传入正确的userid和clientid,不然会认为token非法
//clientId唯一代表一个设备,只能有一个登录。如果使用同一个clientId登录多次,会出现不可预料问题。
IMClient client = new IMClient("KNK_K_00", "6I7UTfKSQT7VhsUJfU6UCvDeCyblLfcXPfajAFxgFGOcqvfFVQCvYy8A5vfAGUj2LSYgoM5W02MHh6jtG/otQs6ft8zLT4Efz315UlDLfrz+OSQYfwAuGFw9m0/x4ckrgiooprsKFxPMaNcCDact0qvp1dBhbeGUipdWVhI33b4=", "5B6DEB72-8A3D-45E2-A88A-47DC4E692B00", "192.168.0.193", 80);
client.setReceiveMessageCallback(new ReceiveMessageCallback() {
@Override
public void onReceiveMessages(List<WFCMessage.Message> messageList, boolean hasMore) {
System.out.println("recieved messages");
}
@Override
public void onRecallMessage(long messageUid) {
System.out.println("recalled messages");
}
});
client.setConnectionStatusCallback((ConnectionStatus newStatus) -> {
if (newStatus == ConnectionStatus_Connected) {
WFCMessage.Conversation conversation = WFCMessage.Conversation.newBuilder().setType(0).setTarget("IMININnn").setLine(0).build();
WFCMessage.MessageContent messageContent = WFCMessage.MessageContent.newBuilder().setSearchableContent("helloworld").setType(1).build();
client.sendMessage(conversation, messageContent, new SendMessageCallback() {
@Override
public void onSuccess(long messageUid, long timestamp) {
System.out.println("send success");
}
@Override
public void onFailure(int errorCode) {
System.out.println("send failure");
}
});
}
});
client.connect();
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@@ -1,154 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.client;
import org.fusesource.hawtbuf.Buffer;
import org.fusesource.hawtbuf.UTF8Buffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;
import static org.fusesource.hawtbuf.Buffer.utf8;
/**
* <p>
* A blocking Connection interface to MQTT.
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class BlockingConnection {
private final FutureConnection next;
public BlockingConnection(FutureConnection next) {
this.next = next;
}
public boolean isConnected() {
return next.isConnected();
}
public void connect() throws Exception {
this.next.connect().await();
}
public void disconnect() throws Exception {
this.next.disconnect().await();
}
public void kill() throws Exception {
this.next.kill().await();
}
public byte[] subscribe(final Topic[] topics) throws Exception {
return this.next.subscribe(topics).await();
}
public void unsubscribe(final String[] topics) throws Exception {
this.next.unsubscribe(topics).await();
}
public void unsubscribe(final UTF8Buffer[] topics) throws Exception {
this.next.unsubscribe(topics).await();
}
public void publish(final UTF8Buffer topic, final Buffer payload, final QoS qos, final boolean retain) throws Exception {
this.next.publish(topic, payload, qos, retain).await();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); //To change body of overridden methods use File | Settings | File Templates.
}
public void publish(final String topic, final byte[] payload, final QoS qos, final boolean retain) throws Exception {
publish(utf8(topic), new Buffer(payload), qos, retain);
}
public Message receive() throws Exception {
return this.next.receive().await();
}
/**
* @return null if the receive times out.
*/
public Message receive(long amount, TimeUnit unit) throws Exception {
Future<Message> receive = this.next.receive();
try {
Message message = receive.await(amount, unit);
if( message!=null ) {
message.blocking = true;
}
return message;
} catch (TimeoutException e) {
// Put it back on the queue..
receive.then(new Callback<Message>() {
public void onSuccess(final Message value) {
next.putBackMessage(value);
}
public void onFailure(Throwable value) {
}
});
return null;
}
}
public void setReceiveBuffer(final long receiveBuffer) throws InterruptedException {
final CountDownLatch done = new CountDownLatch(1);
next.getDispatchQueue().execute(new Runnable() {
public void run() {
try {
next.setReceiveBuffer(receiveBuffer);
} finally {
done.countDown();
}
}
});
done.await();
}
public long getReceiveBuffer() throws InterruptedException {
final CountDownLatch done = new CountDownLatch(1);
final AtomicLong result = new AtomicLong();
next.getDispatchQueue().execute(new Runnable() {
public void run() {
try {
result.set(next.getReceiveBuffer());
} finally {
done.countDown();
}
}
});
done.await();
return result.get();
}
public void resume() {
next.resume();
}
public void suspend() {
next.suspend();
}
}
@@ -1,31 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.client;
/**
* <p>
* Function Result that carries one value.
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public interface Callback<T> {
public void onSuccess(T value);
public void onFailure(Throwable value);
}
@@ -1,966 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.client;
import org.fusesource.hawtbuf.Buffer;
import org.fusesource.hawtbuf.HexSupport;
import org.fusesource.hawtbuf.UTF8Buffer;
import org.fusesource.hawtdispatch.Dispatch;
import org.fusesource.hawtdispatch.DispatchQueue;
import org.fusesource.hawtdispatch.Task;
import org.fusesource.hawtdispatch.transport.DefaultTransportListener;
import org.fusesource.hawtdispatch.transport.HeartBeatMonitor;
import org.fusesource.hawtdispatch.transport.SslTransport;
import org.fusesource.hawtdispatch.transport.TcpTransport;
import org.fusesource.hawtdispatch.transport.Transport;
import org.fusesource.mqtt.codec.CONNACK;
import org.fusesource.mqtt.codec.DISCONNECT;
import org.fusesource.mqtt.codec.MQTTFrame;
import org.fusesource.mqtt.codec.MQTTProtocolCodec;
import org.fusesource.mqtt.codec.MessageSupport.Acked;
import org.fusesource.mqtt.codec.PINGREQ;
import org.fusesource.mqtt.codec.PINGRESP;
import org.fusesource.mqtt.codec.PUBACK;
import org.fusesource.mqtt.codec.PUBCOMP;
import org.fusesource.mqtt.codec.PUBLISH;
import org.fusesource.mqtt.codec.PUBREC;
import org.fusesource.mqtt.codec.PUBREL;
import org.fusesource.mqtt.codec.SUBACK;
import org.fusesource.mqtt.codec.SUBSCRIBE;
import org.fusesource.mqtt.codec.UNSUBACK;
import org.fusesource.mqtt.codec.UNSUBSCRIBE;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ProtocolException;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static org.fusesource.hawtbuf.Buffer.utf8;
import static org.fusesource.hawtdispatch.Dispatch.createQueue;
/**
* <p>
* A callback based non/blocking Connection interface to MQTT.
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class CallbackConnection {
private static class Request {
private final MQTTFrame frame;
private final short id;
private final Callback cb;
Request(int id, MQTTFrame frame, Callback cb) {
this.id = (short) id;
this.cb = cb;
this.frame = frame;
}
}
private static final ExtendedListener DEFAULT_LISTENER = new ExtendedListener(){
public void onConnected() {
}
public void onDisconnected() {
}
public void onPublish(UTF8Buffer utf8Buffer, Buffer buffer, Runnable runnable) {
this.onFailure(createListenerNotSetError());
}
public void onPublish(UTF8Buffer topic, Buffer body, Callback<Callback<byte[]>> ack) {
this.onFailure(createListenerNotSetError());
}
public void onFailure(Throwable value) {
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), value);
}
};
private final DispatchQueue queue;
private final MQTT mqtt;
private Transport transport;
private ExtendedListener listener = DEFAULT_LISTENER;
private Runnable refiller;
private Map<Short, Request> requests = new ConcurrentHashMap<Short, Request>();
private LinkedList<Request> overflow = new LinkedList<Request>();
private final HashMap<Short, Callback<byte[]>> processed = new HashMap<Short, Callback<byte[]>>();
private Throwable failure;
private boolean disconnected = false;
private HeartBeatMonitor heartBeatMonitor;
private long pingedAt;
private long reconnects = 0;
private final AtomicInteger suspendCount = new AtomicInteger(0);
private final AtomicInteger suspendChanges = new AtomicInteger(0);
private final HashMap<UTF8Buffer, QoS> activeSubs = new HashMap<UTF8Buffer, QoS>();
public CallbackConnection(MQTT mqtt) {
this.mqtt = mqtt;
if(this.mqtt.dispatchQueue == null) {
this.queue = createQueue("mqtt client");
} else {
this.queue = this.mqtt.dispatchQueue;
}
}
public void connect(final Callback<byte[]> cb) {
assert cb !=null : "Callback should not be null.";
if( transport!=null ) {
cb.onFailure(new IllegalStateException("Already connected"));
return;
}
try {
createTransport(new LoginHandler(cb, true));
} catch (Throwable e) {
// This error happens when the MQTT config is invalid, reattempting
// wont fix this case.
cb.onFailure(e);
}
}
void reconnect() {
try {
// And reconnect.
createTransport(new LoginHandler(new Callback<byte[]>() {
public void onSuccess(byte[] value) {
mqtt.tracer.debug("Restoring MQTT connection state");
// Setup a new overflow so that the replay can be sent out before the original overflow list.
LinkedList<Request> originalOverflow = overflow;
Map<Short, Request> originalRequests = requests;
overflow = new LinkedList<Request>();
requests = new ConcurrentHashMap<Short, Request>();
// Restore any active subscriptions.
if (!activeSubs.isEmpty()) {
ArrayList<Topic> topics = new ArrayList<Topic>(activeSubs.size());
for (Map.Entry<UTF8Buffer, QoS> entry : activeSubs.entrySet()) {
topics.add(new Topic(entry.getKey(), entry.getValue()));
}
send(new SUBSCRIBE().topics(topics.toArray(new Topic[topics.size()])), null);
}
// Replay any un-acked requests..
for (Map.Entry<Short, Request> entry : originalRequests.entrySet()) {
MQTTFrame frame = entry.getValue().frame;
frame.dup(true); // set the dup flag as these frames were previously transmitted.
send(entry.getValue());
}
// Replay the original overflow
for (Request request : originalOverflow) {
// Stuff in the overflow never got sent out.. so no need to set the dup flag
send(request);
}
}
public void onFailure(Throwable value) {
handleFatalFailure(value);
}
}, false));
} catch (Throwable e) {
handleFatalFailure(e);
}
}
void handleSessionFailure(Throwable error) {
// Socket failure, should we try to reconnect?
if( !disconnected && (mqtt.reconnectAttemptsMax<0 || reconnects < mqtt.reconnectAttemptsMax ) ) {
mqtt.tracer.debug("Reconnecting transport");
// Cleanup the previous transport.
if(heartBeatMonitor!=null) {
heartBeatMonitor.stop();
heartBeatMonitor = null;
}
final Transport t = transport;
transport = null;
if(t!=null) {
t.stop(new Task() {
@Override
public void run() {
listener.onDisconnected();
reconnect();
}
});
} else {
reconnect();
}
} else {
// nope.
handleFatalFailure(error);
}
}
void reconnect(final Callback<Transport> onConnect) {
long reconnectDelay = mqtt.reconnectDelay;
if( reconnectDelay> 0 && mqtt.reconnectBackOffMultiplier > 1.0 ) {
reconnectDelay = (long) Math.pow(mqtt.reconnectDelay*reconnects, mqtt.reconnectBackOffMultiplier);
}
reconnectDelay = Math.min(reconnectDelay, mqtt.reconnectDelayMax);
reconnects += 1;
queue.executeAfter(reconnectDelay, TimeUnit.MILLISECONDS, new Task() {
@Override
public void run() {
if(disconnected) {
onConnect.onFailure(createDisconnectedError());
} else {
try {
createTransport(onConnect);
} catch (Exception e) {
onConnect.onFailure(e);
}
}
}
});
}
/**
* Creates and start a transport to the MQTT server. Passes it to the onConnect
* once the transport is connected.
*
* @param onConnect
* @throws Exception
*/
void createTransport(final Callback<Transport> onConnect) throws Exception {
mqtt.tracer.debug("Connecting");
String scheme = mqtt.host.getScheme();
final Transport transport;
if( "tcp".equals(scheme) ) {
transport = new TcpTransport();
} else if( SslTransport.protocol(scheme)!=null ) {
SslTransport ssl = new SslTransport();
if( mqtt.sslContext == null ) {
mqtt.sslContext = SSLContext.getDefault();
}
ssl.setSSLContext(mqtt.sslContext);
transport = ssl;
} else {
throw new Exception("Unsupported URI scheme '"+scheme+"'");
}
if( mqtt.blockingExecutor == null ) {
mqtt.blockingExecutor = MQTT.getBlockingThreadPool();
}
transport.setBlockingExecutor(mqtt.blockingExecutor);
transport.setDispatchQueue(queue);
transport.setProtocolCodec(new MQTTProtocolCodec());
if( transport instanceof TcpTransport ) {
TcpTransport tcp = (TcpTransport)transport;
tcp.setMaxReadRate(mqtt.maxReadRate);
tcp.setMaxWriteRate(mqtt.maxWriteRate);
tcp.setReceiveBufferSize(mqtt.receiveBufferSize);
tcp.setSendBufferSize(mqtt.sendBufferSize);
tcp.setTrafficClass(mqtt.trafficClass);
tcp.setUseLocalHost(mqtt.useLocalHost);
tcp.connecting(mqtt.host, mqtt.localAddress);
}
transport.setTransportListener(new DefaultTransportListener(){
@Override
public void onTransportConnected() {
mqtt.tracer.debug("Transport connected");
if(disconnected) {
onFailure(createDisconnectedError());
} else {
onConnect.onSuccess(transport);
}
}
@Override
public void onTransportFailure(final IOException error) {
mqtt.tracer.debug("Transport failure: %s", error);
onFailure(error);
}
private void onFailure(final Throwable error) {
if(!transport.isClosed()) {
transport.stop(new Task() {
@Override
public void run() {
onConnect.onFailure(error);
}
});
}
}
});
transport.start(NOOP);
}
class LoginHandler implements Callback<Transport> {
private final Callback<byte[]> cb;
private final boolean initialConnect;
LoginHandler(Callback<byte[]> cb, boolean initialConnect) {
this.cb = cb;
this.initialConnect = initialConnect;
}
public void onSuccess(final Transport transport) {
transport.setTransportListener(new DefaultTransportListener() {
@Override
public void onTransportFailure(IOException error) {
mqtt.tracer.debug("Transport failure: %s", error);
transport.stop(NOOP);
onFailure(error);
}
@Override
public void onTransportCommand(Object command) {
MQTTFrame response = (MQTTFrame) command;
mqtt.tracer.onReceive(response);
try {
switch (response.messageType()) {
case CONNACK.TYPE:
CONNACK connack = new CONNACK().decode(response);
switch (connack.code()) {
case CONNECTION_ACCEPTED:
mqtt.tracer.debug("MQTT login accepted");
onSessionEstablished(transport);
listener.onConnected();
cb.onSuccess(connack.payload);
queue.execute(new Task() {
@Override
public void run() {
drainOverflow();
}
});
break;
default:
mqtt.tracer.debug("MQTT login rejected");
// Bad creds or something. No point in reconnecting.
transport.stop(NOOP);
cb.onFailure(new MQTTException("Could not connect: " + connack.code(), connack));
}
break;
default:
mqtt.tracer.debug("Received unexpected MQTT frame: %d", response.messageType());
// Naughty MQTT server? No point in reconnecting.
transport.stop(NOOP);
cb.onFailure(new IOException("Could not connect. Received unexpected command: " + response.messageType()));
}
} catch (ProtocolException e) {
mqtt.tracer.debug("Protocol error: %s", e);
transport.stop(NOOP);
cb.onFailure(e);
}
}
});
transport.resumeRead();
if( mqtt.connect.clientId() == null ) {
String id = hex(transport.getLocalAddress())+Long.toHexString(System.currentTimeMillis()/1000);
if(id.length() > 23) {
id = id.substring(0,23);
}
mqtt.connect.clientId(utf8(id));
}
MQTTFrame encoded = mqtt.connect.encode();
boolean accepted = transport.offer(encoded);
mqtt.tracer.onSend(encoded);
mqtt.tracer.debug("Logging in");
assert accepted: "First frame should always be accepted by the transport";
}
private boolean tryReconnect() {
if(initialConnect) {
return mqtt.connectAttemptsMax<0 || reconnects < mqtt.connectAttemptsMax;
}
return mqtt.reconnectAttemptsMax<0 || reconnects < mqtt.reconnectAttemptsMax;
}
public void onFailure(Throwable value) {
// Socket failure, should we try to reconnect?
if( !disconnected && tryReconnect() ) {
reconnect(this);
} else {
// nope.
cb.onFailure(value);
}
}
}
private boolean onRefillCalled =false;
public void onSessionEstablished(Transport transport) {
this.transport = transport;
if( suspendCount.get() > 0 ) {
this.transport.suspendRead();
}
this.transport.setTransportListener(new DefaultTransportListener() {
@Override
public void onTransportCommand(Object command) {
MQTTFrame frame = (MQTTFrame) command;
mqtt.tracer.onReceive(frame);
processFrame(frame);
}
@Override
public void onRefill() {
onRefillCalled =true;
drainOverflow();
}
@Override
public void onTransportFailure(IOException error) {
handleSessionFailure(error);
}
});
pingedAt = 0;
if(mqtt.getKeepAlive()>0) {
heartBeatMonitor = new HeartBeatMonitor();
heartBeatMonitor.setWriteInterval((mqtt.getKeepAlive() * 1000) / 2);
heartBeatMonitor.setTransport(this.transport);
heartBeatMonitor.suspendRead(); // to match the suspended state of the transport.
heartBeatMonitor.setOnKeepAlive(new Task() {
@Override
public void run() {
// Don't care if the offer is rejected, just means we have data outbound.
if(!disconnected && pingedAt==0) {
MQTTFrame encoded = new PINGREQ().encode();
if(CallbackConnection.this.transport.offer(encoded)) {
mqtt.tracer.onSend(encoded);
final long now = System.currentTimeMillis();
final long suspends = suspendChanges.get();
pingedAt = now;
queue.executeAfter(CallbackConnection.this.mqtt.getKeepAlive(), TimeUnit.SECONDS, new Task() {
@Override
public void run() {
if (now == pingedAt) {
// if the connection remained suspend we will never get the ping response..
// Looks like the user has forgoton to resume the connection
if (suspends == suspendChanges.get() && suspendCount.get() > 0) {
// Since the connection has been suspended, we can't really
// check to see if we are getting the ping responses.
mqtt.tracer.debug("The connection has remained suspended for an extended period of time so it cannot do proper keep alive processing. Did you forget to resume the connection?");
} else {
mqtt.tracer.debug("Ping timeout");
handleSessionFailure(new ProtocolException("Ping timeout").fillInStackTrace());
}
}
}
});
}
}
}
});
heartBeatMonitor.start();
}
}
public Transport transport() {
return transport;
}
public DispatchQueue getDispatchQueue() {
return queue;
}
public void resume() {
suspendChanges.incrementAndGet();
if( suspendCount.decrementAndGet() == 0 && this.transport!=null ) {
this.transport.resumeRead();
if(this.heartBeatMonitor!=null){
this.heartBeatMonitor.resumeRead();
}
}
}
public void suspend() {
suspendChanges.incrementAndGet();
if( suspendCount.incrementAndGet() == 1 && this.transport!=null ) {
this.transport.suspendRead();
if(this.heartBeatMonitor!=null){
this.heartBeatMonitor.suspendRead();
}
}
}
public CallbackConnection refiller(Runnable refiller) {
queue.assertExecuting();
this.refiller = refiller;
return this;
}
public CallbackConnection listener(final Listener original) {
if( original instanceof ExtendedListener ) {
this.listener = (ExtendedListener) original;
} else {
this.listener = new ExtendedListener() {
public void onPublish(UTF8Buffer topic, Buffer body, final Callback<Callback<byte[]>> ack) {
original.onPublish(topic, body, new Runnable() {
public void run() {
ack.onSuccess(null);
}
});
}
public void onPublish(UTF8Buffer topic, Buffer body, Runnable ack) {
original.onPublish(topic, body, ack);
}
public void onConnected() {
original.onConnected();
}
public void onDisconnected() {
original.onDisconnected();
}
public void onFailure(Throwable value) {
original.onFailure(value);
}
};
}
return this;
}
public boolean full() {
queue.assertExecuting();
return this.transport.full();
}
public Throwable failure() {
queue.assertExecuting();
return failure;
}
public void disconnect(boolean clearSession, final Callback<Void> onComplete) {
if( disconnected ) {
if(onComplete!=null){
onComplete.onSuccess(null);
}
return;
}
disconnected = true;
final short requestId = getNextMessageId();
final Runnable stop = new Runnable() {
private boolean executed = false;
public void run() {
if(!executed) {
executed = true;
requests.remove(requestId);
if(heartBeatMonitor!=null) {
heartBeatMonitor.stop();
heartBeatMonitor = null;
}
transport.stop(new Task() {
@Override
public void run() {
listener.onDisconnected();
if (onComplete != null) {
onComplete.onSuccess(null);
}
}
});
}
}
};
Callback<Void> cb = new Callback<Void>() {
public void onSuccess(Void v) {
// To make sure DISCONNECT has been flushed out to the socket
onRefillCalled = false;
refiller = new Runnable() {
public void run() {
if(onRefillCalled) {
stop.run();
}
}
};
if(transport != null){
transport.flush();
}
}
public void onFailure(Throwable value) {
stop.run();
}
};
// Pop the frame into a request so it we get notified
// of any failures so we continue to stop the transport.
if(transport!=null) {
MQTTFrame frame = new DISCONNECT().encode().dup(clearSession);
send(new Request(getNextMessageId(), frame, cb));
} else {
cb.onSuccess(null);
}
}
/**
* Kills the connection without a graceful disconnect.
* @param onComplete
*/
public void kill(final Callback<Void> onComplete) {
if( disconnected ) {
if(onComplete!=null){
onComplete.onSuccess(null);
}
return;
}
disconnected = true;
if(heartBeatMonitor!=null) {
heartBeatMonitor.stop();
heartBeatMonitor = null;
}
transport.stop(new Task() {
@Override
public void run() {
listener.onDisconnected();
if (onComplete != null) {
onComplete.onSuccess(null);
}
}
});
}
public void publish(String topic, byte[] payload, QoS qos, boolean retain, Callback<byte[]> cb) {
publish(utf8(topic), new Buffer(payload), qos, retain, cb);
}
public void publish(UTF8Buffer topic, Buffer payload, QoS qos, boolean retain, Callback<byte[]> cb) {
queue.assertExecuting();
if( disconnected ) {
cb.onFailure(createDisconnectedError());
return;
}
PUBLISH command = new PUBLISH().qos(qos).retain(retain);
command.topicName(topic).payload(payload);
send(command, cb);
}
public void subscribe(final Topic[] topics, Callback<byte[]> cb) {
if(topics==null) {
throw new IllegalArgumentException("topics must not be null");
}
queue.assertExecuting();
if( disconnected ) {
cb.onFailure(createDisconnectedError());
return;
}
if( listener == DEFAULT_LISTENER ) {
cb.onFailure(createListenerNotSetError());
} else {
send(new SUBSCRIBE().topics(topics), new ProxyCallback<byte[]>(cb){
@Override
public void onSuccess(byte[] value) {
for (Topic topic : topics) {
activeSubs.put(topic.name(), topic.qos());
}
if(next!=null) {
next.onSuccess(value);
}
}
});
}
}
public void unsubscribe(final UTF8Buffer[] topics, Callback<Void> cb) {
queue.assertExecuting();
if( disconnected ) {
cb.onFailure(createDisconnectedError());
return;
}
send(new UNSUBSCRIBE().topics(topics), new ProxyCallback(cb){
@Override
public void onSuccess(Object value) {
for (UTF8Buffer topic : topics) {
activeSubs.remove(topic);
}
if(next!=null) {
next.onSuccess(value);
}
}
});
}
private void send(Acked command, Callback cb) {
short id = 0;
if(command.qos() != QoS.AT_MOST_ONCE) {
id = getNextMessageId();
command.messageId(id);
}
send(new Request(id, command.encode(), cb));
}
private void send(Request request) {
if( failure !=null ) {
if( request.cb!=null ) {
request.cb.onFailure(failure);
}
} else {
// Put the request in the map before sending it over the wire.
if(request.id!=0) {
this.requests.put(request.id, request);
}
if( overflow.isEmpty() && transport!=null && transport.offer(request.frame) ) {
mqtt.tracer.onSend(request.frame);
if(request.id==0) {
if( request.cb!=null ) {
((Callback<Void>)request.cb).onSuccess(null);
}
}
} else {
// Remove it from the request.
this.requests.remove(request.id);
overflow.addLast(request);
}
}
}
private short nextMessageId = 1;
private short getNextMessageId() {
short rc = nextMessageId;
nextMessageId++;
if(nextMessageId==0) {
nextMessageId=1;
}
return rc;
}
private void drainOverflow() {
queue.assertExecuting();
if( overflow.isEmpty() || transport==null ){
return;
}
Request request;
while((request=overflow.peek())!=null) {
if( this.transport.offer(request.frame) ) {
mqtt.tracer.onSend(request.frame);
overflow.removeFirst();
if(request.id==0) {
if( request.cb!=null ) {
((Callback<Void>)request.cb).onSuccess(null);
}
} else {
this.requests.put(request.id, request);
}
} else {
break;
}
}
if( overflow.isEmpty() ) {
if( refiller!=null ) {
try {
refiller.run();
} catch (Throwable e) {
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
}
}
}
}
private void completeRequest(short id, byte originalType, Object arg) {
Request request = requests.remove(id);
if( request!=null ) {
assert originalType==request.frame.messageType();
if(request.cb!=null) {
if( arg==null ) {
((Callback<Void>)request.cb).onSuccess(null);
} else {
((Callback<Object>)request.cb).onSuccess(arg);
}
}
} else {
handleFatalFailure(new ProtocolException("Command from server contained an invalid message id: " + id));
}
}
private void processFrame(MQTTFrame frame) {
try {
switch(frame.messageType()) {
case PUBLISH.TYPE: {
PUBLISH publish = new PUBLISH().decode(frame);
toReceiver(publish);
break;
}
case PUBREL.TYPE:{
PUBREL ack = new PUBREL().decode(frame);
Callback<byte[]> onRel = processed.remove(ack.messageId());
PUBCOMP response = new PUBCOMP();
response.messageId(ack.messageId());
send(new Request(0, response.encode(), null));
if( onRel!=null ) {
onRel.onSuccess(null);
}
break;
}
case PUBACK.TYPE:{
PUBACK ack = new PUBACK().decode(frame);
completeRequest(ack.messageId(), PUBLISH.TYPE, ack.payload);
break;
}
case PUBREC.TYPE:{
PUBREC ack = new PUBREC().decode(frame);
PUBREL response = new PUBREL();
response.messageId(ack.messageId());
send(new Request(0, response.encode(), null));
break;
}
case PUBCOMP.TYPE:{
PUBCOMP ack = new PUBCOMP().decode(frame);
completeRequest(ack.messageId(), PUBLISH.TYPE, null);
break;
}
case SUBACK.TYPE: {
SUBACK ack = new SUBACK().decode(frame);
completeRequest(ack.messageId(), SUBSCRIBE.TYPE, ack.grantedQos());
break;
}
case UNSUBACK.TYPE: {
UNSUBACK ack = new UNSUBACK().decode(frame);
completeRequest(ack.messageId(), UNSUBSCRIBE.TYPE, null);
break;
}
case PINGRESP.TYPE: {
pingedAt = 0;
break;
}
default:
throw new ProtocolException("Unexpected MQTT command type: "+frame.messageType());
}
} catch (Throwable e) {
handleFatalFailure(e);
}
}
static public final Task NOOP = Dispatch.NOOP;
private void toReceiver(final PUBLISH publish) {
if( listener !=null ) {
try {
Callback<Callback<byte[]>> cb = null;
switch( publish.qos() ) {
case AT_LEAST_ONCE:
cb = new Callback<Callback<byte[]>>() {
public void onSuccess(Callback<byte[]> value) {
PUBACK response = new PUBACK();
response.messageId(publish.messageId());
send(new Request(0, response.encode(), null));
if( value !=null ) {
value.onSuccess(null);
}
}
public void onFailure(Throwable value) {
}
};
break;
case EXACTLY_ONCE:
cb = new Callback<Callback<byte[]>>() {
public void onSuccess(Callback<byte[]> value) {
PUBREC response = new PUBREC();
response.messageId(publish.messageId());
processed.put(publish.messageId(), value);
send(new Request(0, response.encode(), null));
}
public void onFailure(Throwable value) {
}
};
// Looks like a dup delivery.. filter it out.
if( processed.get(publish.messageId())!=null ) {
return;
}
break;
case AT_MOST_ONCE:
cb = new Callback<Callback<byte[]>>() {
public void onSuccess(Callback<byte[]> value) {
if (value != null) {
value.onSuccess(null);
}
}
public void onFailure(Throwable value) {
}
};
}
listener.onPublish(publish.topicName(), publish.payload(), cb);
} catch (Throwable e) {
handleFatalFailure(e);
}
}
}
private void handleFatalFailure(Throwable error) {
if( failure == null ) {
failure = error;
mqtt.tracer.debug("Fatal connection failure: %s", error);
// Fail incomplete requests.
ArrayList<Request> values = new ArrayList(requests.values());
requests.clear();
for (Request value : values) {
if( value.cb!= null ) {
value.cb.onFailure(failure);
}
}
ArrayList<Request> overflowEntries = new ArrayList<Request>(overflow);
overflow.clear();
for (Request entry : overflowEntries) {
if( entry.cb !=null ) {
entry.cb.onFailure(failure);
}
}
if( listener !=null && !disconnected ) {
try {
listener.onFailure(failure);
} catch (Exception e) {
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
}
}
}
}
private static IllegalStateException createListenerNotSetError() {
return (IllegalStateException) new IllegalStateException("No connection listener set to handle message received from the server.").fillInStackTrace();
}
private static IllegalStateException createDisconnectedError() {
return (IllegalStateException) new IllegalStateException("Disconnected").fillInStackTrace();
}
static private String hex(SocketAddress address) {
if( address instanceof InetSocketAddress ) {
InetSocketAddress isa = (InetSocketAddress)address;
return HexSupport.toHexFromBuffer(new Buffer(isa.getAddress().getAddress()))+Integer.toHexString(isa.getPort());
}
return "";
}
}
@@ -1,32 +0,0 @@
/**
* Copyright (C) 2016, Red Hat Inc. All rights reserved.
*
* http://redhat.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.client;
import org.fusesource.hawtbuf.Buffer;
import org.fusesource.hawtbuf.UTF8Buffer;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public interface ExtendedListener extends Listener {
public void onPublish(UTF8Buffer topic, Buffer body, Callback<Callback<byte[]>> ack);
}
@@ -1,33 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.client;
import java.util.concurrent.TimeUnit;
/**
* <p>A simplified Future function results interface.</p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public interface Future<T> {
T await() throws Exception;
T await(long amount, TimeUnit unit) throws Exception;
void then(Callback<T> callback);
}
@@ -1,258 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.client;
import org.fusesource.hawtbuf.Buffer;
import org.fusesource.hawtbuf.UTF8Buffer;
import org.fusesource.hawtdispatch.DispatchQueue;
import org.fusesource.hawtdispatch.Task;
import java.util.ArrayList;
import java.util.LinkedList;
import static org.fusesource.hawtbuf.Buffer.utf8;
/**
* <p>
* A Future based optionally-blocking Connection interface to MQTT.
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class FutureConnection {
private final CallbackConnection next;
private final LinkedList<Promise<Message>> receiveFutures = new LinkedList<Promise<Message>>();
private final LinkedList<Message> receivedFrames = new LinkedList<Message>();
private volatile boolean connected;
private long receiveBuffer = 1024*1024;
private long receiveBufferRemaining = receiveBuffer;
private boolean receiveBufferFull = false;
public FutureConnection(CallbackConnection next) {
this.next = next;
this.next.listener(new ExtendedListener() {
public void onConnected() {
connected = true;
}
public void onDisconnected() {
connected = false;
}
public void onPublish(UTF8Buffer topic, final Buffer payload, final Callback<Callback<byte[]>> onComplete) {
getDispatchQueue().assertExecuting();
receiveBufferRemaining -= payload.length();
if( !receiveBufferFull && receiveBufferRemaining <=0 ) {
receiveBufferFull = true;
suspend();
}
deliverMessage(new Message(getDispatchQueue(), topic, payload, new Callback<Callback<byte[]>>() {
public void onSuccess(Callback<byte[]> value) {
processed();
onComplete.onSuccess(value);
}
public void onFailure(Throwable value) {
processed();
onComplete.onFailure(value);
}
private void processed() {
getDispatchQueue().assertExecuting();
receiveBufferRemaining += payload.length();
if( receiveBufferFull && receiveBufferRemaining >0 ) {
receiveBufferFull = false;
resume();
}
}
}));
}
public void onPublish(UTF8Buffer topic, Buffer payload, final Runnable onComplete) {
onPublish(topic, payload, new Callback<Callback<byte[]>>() {
public void onSuccess(Callback<byte[]> value) {
onComplete.run();
}
public void onFailure(Throwable value) {
}
});
}
public void onFailure(Throwable value) {
getDispatchQueue().assertExecuting();
ArrayList<Promise<?>> tmp = new ArrayList<Promise<?>>(receiveFutures);
receiveFutures.clear();
for (Promise<?> future : tmp) {
future.onFailure(value);
}
connected = false;
}
});
}
void deliverMessage(Message msg) {
if( receiveFutures.isEmpty() ) {
receivedFrames.add(msg);
} else {
receiveFutures.removeFirst().onSuccess(msg);
}
}
void putBackMessage(Message msg) {
if( receiveFutures.isEmpty() ) {
receivedFrames.addFirst(msg);
} else {
receiveFutures.removeFirst().onSuccess(msg);
}
}
public boolean isConnected() {
return connected;
}
public DispatchQueue getDispatchQueue() {
return this.next.getDispatchQueue();
}
public Future<byte[]> connect() {
final Promise<byte[]> future = new Promise<byte[]>();
next.getDispatchQueue().execute(new Task() {
public void run() {
next.connect(future);
}
});
return future;
}
public Future<Void> disconnect() {
final Promise<Void> future = new Promise<Void>();
next.getDispatchQueue().execute(new Task() {
public void run() {
next.disconnect(false, future);
}
});
return future;
}
public Future<Void> kill() {
final Promise<Void> future = new Promise<Void>();
next.getDispatchQueue().execute(new Task() {
public void run() {
next.kill(future);
}
});
return future;
}
public Future<byte[]> subscribe(final Topic[] topics) {
final Promise<byte[]> future = new Promise<byte[]>();
next.getDispatchQueue().execute(new Task() {
public void run() {
next.subscribe(topics, future);
}
});
return future;
}
public Future<Void> unsubscribe(final String[] topics) {
UTF8Buffer[] buffers = new UTF8Buffer[topics.length];
for (int i = 0; i < buffers.length; i++) {
buffers[i] = new UTF8Buffer(topics[i]);
}
return unsubscribe(buffers);
}
public Future<Void> unsubscribe(final UTF8Buffer[] topics) {
final Promise<Void> future = new Promise<Void>();
next.getDispatchQueue().execute(new Task() {
public void run() {
next.unsubscribe(topics, future);
}
});
return future;
}
public Future<byte[]> publish(final String topic, final byte[] payload, final QoS qos, final boolean retain) {
return publish(utf8(topic), new Buffer(payload), qos, retain);
}
public Future<byte[]> publish(final UTF8Buffer topic, final Buffer payload, final QoS qos, final boolean retain) {
final Promise<byte[]> future = new Promise<byte[]>();
next.getDispatchQueue().execute(new Task() {
public void run() {
next.publish(topic, payload, qos, retain, future);
}
});
return future;
}
public Future<Message> receive() {
final Promise<Message> future = new Promise<Message>();
getDispatchQueue().execute(new Task(){
public void run() {
if( next.failure()!=null ) {
future.onFailure(next.failure());
} else {
if( receivedFrames.isEmpty() ) {
receiveFutures.add(future);
} else {
future.onSuccess(receivedFrames.removeFirst());
}
}
}
});
return future;
}
public long getReceiveBuffer() {
getDispatchQueue().assertExecuting();
return receiveBuffer;
}
public void setReceiveBuffer(long receiveBuffer) {
getDispatchQueue().assertExecuting();
long prev = this.receiveBuffer;
this.receiveBuffer = receiveBuffer;
long diff = prev-receiveBuffer;
receiveBufferRemaining -= diff;
if( !receiveBufferFull && receiveBufferRemaining <=0 ) {
receiveBufferFull = true;
suspend();
} else if( receiveBufferFull && receiveBufferRemaining >0 ) {
receiveBufferFull = false;
resume();
}
}
public void resume() {
next.resume();
}
public void suspend() {
next.suspend();
}
}
@@ -1,36 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.client;
import org.fusesource.hawtbuf.Buffer;
import org.fusesource.hawtbuf.UTF8Buffer;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
@Deprecated
public interface Listener {
public void onConnected();
public void onDisconnected();
public void onPublish(UTF8Buffer topic, Buffer body, Runnable ack);
public void onFailure(Throwable value);
}
@@ -1,392 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.client;
import org.fusesource.hawtbuf.UTF8Buffer;
import org.fusesource.hawtdispatch.DispatchQueue;
import org.fusesource.hawtdispatch.transport.*;
import org.fusesource.mqtt.codec.CONNECT;
import javax.net.ssl.SSLContext;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;
import static org.fusesource.hawtbuf.Buffer.utf8;
import static org.fusesource.hawtdispatch.Dispatch.createQueue;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class MQTT {
private static final long KEEP_ALIVE = Long.parseLong(System.getProperty("mqtt.thread.keep_alive", Integer.toString(1000)));
private static final long STACK_SIZE = Long.parseLong(System.getProperty("mqtt.thread.stack_size", Integer.toString(1024*512)));
private static ThreadPoolExecutor blockingThreadPool;
public synchronized static ThreadPoolExecutor getBlockingThreadPool() {
if( blockingThreadPool == null ) {
blockingThreadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, KEEP_ALIVE, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(), new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread rc = new Thread(null, r, "MQTT Task", STACK_SIZE);
rc.setDaemon(true);
return rc;
}
}) {
@Override
public void shutdown() {
// we don't ever shutdown since we are shared..
}
@Override
public List<Runnable> shutdownNow() {
// we don't ever shutdown since we are shared..
return Collections.emptyList();
}
};
}
return blockingThreadPool;
}
public synchronized static void setBlockingThreadPool(ThreadPoolExecutor pool) {
blockingThreadPool = pool;
}
private static final URI DEFAULT_HOST = createDefaultHost();
private static URI createDefaultHost() {
try {
return new URI("tcp://127.0.0.1:1883");
} catch (URISyntaxException e) {
return null;
}
}
protected URI host = DEFAULT_HOST;
protected URI localAddress;
protected SSLContext sslContext;
protected DispatchQueue dispatchQueue;
protected Executor blockingExecutor;
protected int maxReadRate;
protected int maxWriteRate;
protected int trafficClass = TcpTransport.IPTOS_THROUGHPUT;
protected int receiveBufferSize = 1024*64;
protected int sendBufferSize = 1024*64;
protected boolean useLocalHost = true;
protected CONNECT connect = new CONNECT();
protected long reconnectDelay = 10;
protected long reconnectDelayMax = 30*1000;
protected double reconnectBackOffMultiplier = 2.0f;
protected long reconnectAttemptsMax = -1;
protected long connectAttemptsMax = -1;
protected Tracer tracer = new Tracer();
public MQTT() {
}
public MQTT(MQTT other) {
this.host = other.host;
this.localAddress = other.localAddress;
this.sslContext = other.sslContext;
this.dispatchQueue = other.dispatchQueue;
this.blockingExecutor = other.blockingExecutor;
this.maxReadRate = other.maxReadRate;
this.maxWriteRate = other.maxWriteRate;
this.trafficClass = other.trafficClass;
this.receiveBufferSize = other.receiveBufferSize;
this.sendBufferSize = other.sendBufferSize;
this.useLocalHost = other.useLocalHost;
this.connect = new CONNECT(other.connect);
this.reconnectDelay = other.reconnectDelay;
this.reconnectDelayMax = other.reconnectDelayMax;
this.reconnectBackOffMultiplier = other.reconnectBackOffMultiplier;
this.reconnectAttemptsMax = other.reconnectAttemptsMax;
this.connectAttemptsMax = other.connectAttemptsMax;
this.tracer = other.tracer;
}
public CallbackConnection callbackConnection() {
if( !isCleanSession() && ( getClientId()==null || getClientId().length==0 )) {
throw new IllegalArgumentException("The client id MUST be configured when clean session is set to false");
}
return new CallbackConnection(new MQTT(this));
}
public FutureConnection futureConnection() {
return new FutureConnection(callbackConnection());
}
public BlockingConnection blockingConnection() {
return new BlockingConnection(futureConnection());
}
public UTF8Buffer getClientId() {
return connect.clientId();
}
public short getKeepAlive() {
return connect.keepAlive();
}
public UTF8Buffer getPassword() {
return connect.password();
}
public byte getType() {
return connect.messageType();
}
public UTF8Buffer getUserName() {
return connect.userName();
}
public UTF8Buffer getWillMessage() {
return connect.willMessage();
}
public QoS getWillQos() {
return connect.willQos();
}
public UTF8Buffer getWillTopic() {
return connect.willTopic();
}
public boolean isCleanSession() {
return connect.cleanSession();
}
public boolean isWillRetain() {
return connect.willRetain();
}
public void setCleanSession(boolean cleanSession) {
connect.cleanSession(cleanSession);
}
public void setClientId(String clientId) {
this.setClientId(utf8(clientId));
}
public void setClientId(UTF8Buffer clientId) {
connect.clientId(clientId);
}
public void setKeepAlive(short keepAlive) {
connect.keepAlive(keepAlive);
}
public void setPassword(String password) {
this.setPassword(utf8(password));
}
public void setPassword(UTF8Buffer password) {
connect.password(password);
}
public void setUserName(String userName) {
this.setUserName(utf8(userName));
}
public void setUserName(UTF8Buffer userName) {
connect.userName(userName);
}
public void setWillMessage(String willMessage) {
connect.willMessage(utf8(willMessage));
}
public void setWillMessage(UTF8Buffer willMessage) {
connect.willMessage(willMessage);
}
public void setWillQos(QoS willQos) {
connect.willQos(willQos);
}
public void setVersion(String version) {
if( "3.1".equals(version) ) {
connect.version(3);
} else if( "3.1.1".equals(version) ) {
connect.version(4);
}
}
public String getVersion() {
switch(connect.version()) {
case 3: return "3.1";
case 4: return "3.1.1";
default: return "unknown";
}
}
public void setWillRetain(boolean willRetain) {
connect.willRetain(willRetain);
}
public void setWillTopic(String willTopic) {
this.setWillTopic(utf8(willTopic));
}
public void setWillTopic(UTF8Buffer willTopic) {
connect.willTopic(willTopic);
}
public Executor getBlockingExecutor() {
return blockingExecutor;
}
public void setBlockingExecutor(Executor blockingExecutor) {
this.blockingExecutor = blockingExecutor;
}
public DispatchQueue getDispatchQueue() {
return dispatchQueue;
}
public void setDispatchQueue(DispatchQueue dispatchQueue) {
this.dispatchQueue = dispatchQueue;
}
public URI getLocalAddress() {
return localAddress;
}
public void setLocalAddress(String localAddress) throws URISyntaxException {
this.setLocalAddress(new URI(localAddress));
}
public void setLocalAddress(URI localAddress) {
this.localAddress = localAddress;
}
public int getMaxReadRate() {
return maxReadRate;
}
public void setMaxReadRate(int maxReadRate) {
this.maxReadRate = maxReadRate;
}
public int getMaxWriteRate() {
return maxWriteRate;
}
public void setMaxWriteRate(int maxWriteRate) {
this.maxWriteRate = maxWriteRate;
}
public int getReceiveBufferSize() {
return receiveBufferSize;
}
public void setReceiveBufferSize(int receiveBufferSize) {
this.receiveBufferSize = receiveBufferSize;
}
public URI getHost() {
return host;
}
public void setHost(String host, int port) throws URISyntaxException {
this.setHost(new URI("tcp://"+host+":"+port));
}
public void setHost(String host) throws URISyntaxException {
this.setHost(new URI(host));
}
public void setHost(URI host) {
this.host = host;
}
public int getSendBufferSize() {
return sendBufferSize;
}
public void setSendBufferSize(int sendBufferSize) {
this.sendBufferSize = sendBufferSize;
}
public SSLContext getSslContext() {
return sslContext;
}
public void setSslContext(SSLContext sslContext) {
this.sslContext = sslContext;
}
public int getTrafficClass() {
return trafficClass;
}
public void setTrafficClass(int trafficClass) {
this.trafficClass = trafficClass;
}
public boolean isUseLocalHost() {
return useLocalHost;
}
public void setUseLocalHost(boolean useLocalHost) {
this.useLocalHost = useLocalHost;
}
public long getConnectAttemptsMax() {
return connectAttemptsMax;
}
public void setConnectAttemptsMax(long connectAttemptsMax) {
this.connectAttemptsMax = connectAttemptsMax;
}
public long getReconnectAttemptsMax() {
return reconnectAttemptsMax;
}
public void setReconnectAttemptsMax(long reconnectAttemptsMax) {
this.reconnectAttemptsMax = reconnectAttemptsMax;
}
public double getReconnectBackOffMultiplier() {
return reconnectBackOffMultiplier;
}
public void setReconnectBackOffMultiplier(double reconnectBackOffMultiplier) {
this.reconnectBackOffMultiplier = reconnectBackOffMultiplier;
}
public long getReconnectDelay() {
return reconnectDelay;
}
public void setReconnectDelay(long reconnectDelay) {
this.reconnectDelay = reconnectDelay;
}
public long getReconnectDelayMax() {
return reconnectDelayMax;
}
public void setReconnectDelayMax(long reconnectDelayMax) {
this.reconnectDelayMax = reconnectDelayMax;
}
public Tracer getTracer() {
return tracer;
}
public void setTracer(Tracer tracer) {
this.tracer = tracer;
}
}
@@ -1,14 +0,0 @@
package org.fusesource.mqtt.client;
import java.io.IOException;
import org.fusesource.mqtt.codec.CONNACK;
public class MQTTException extends IOException {
public final CONNACK connack;
public MQTTException(String msg, CONNACK connack) {
super(msg);
this.connack = connack;
}
}
@@ -1,104 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.client;
import org.fusesource.hawtbuf.Buffer;
import org.fusesource.hawtbuf.UTF8Buffer;
import org.fusesource.hawtdispatch.DispatchQueue;
import org.fusesource.hawtdispatch.Task;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class Message {
private final UTF8Buffer topic;
private final Buffer payload;
private Callback<Callback<byte[]>> onComplete;
private final DispatchQueue queue;
boolean blocking = false;
public Message(DispatchQueue queue, UTF8Buffer topic, Buffer payload, Callback<Callback<byte[]>> onComplete) {
this.queue = queue;
this.payload = payload;
this.topic = topic;
this.onComplete = onComplete;
}
public byte[] getPayload() {
return payload.toByteArray();
}
/**
* Using getPayloadBuffer() is lower overhead version of getPayload()
* since it avoids a byte array copy.
* @return
*/
public Buffer getPayloadBuffer() {
return payload;
}
public String getTopic() {
return topic.toString();
}
/**
* Using getTopicBuffer is lower overhead version of getTopic()
* since it avoid doing UTF-8 decode.
* @return
*/
public UTF8Buffer getTopicBuffer() {
return topic;
}
public void ack() {
if( blocking ) {
final Promise<byte[]> future = new Promise<byte[]>();
ack(future);
try {
future.await();
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
ack(null);
}
}
public void ack(final Callback<byte[]> onAcked) {
if(onComplete!=null) {
queue.execute(new Task() {
Callback<Callback<byte[]>> onCompleteCopy = onComplete;
@Override
public void run() {
onCompleteCopy.onSuccess(onAcked);
}
});
onComplete = null;
} else {
if( onAcked!=null ) {
onAcked.onSuccess(null);
}
}
}
}
@@ -1,47 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.client;
/**
* <p>
* Function Result that carries one value.
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class NoopCallback<T> implements Callback<T> {
public final Callback<T> next;
public NoopCallback(Callback<T> next) {
this.next = next;
}
public void onSuccess(T value) {
if( next!=null ) {
next.onSuccess(value);
}
}
public void onFailure(Throwable value) {
if( next!=null ) {
next.onFailure(value);
}
}
}
@@ -1,109 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.client;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class Promise<T> implements Callback<T>, Future<T> {
private final CountDownLatch latch = new CountDownLatch(1);
private Callback<T> next;
private Throwable error;
private T value;
public void onFailure(Throwable value) {
Callback<T> callback = null;
synchronized(this) {
error = value;
latch.countDown();
callback = next;
}
if( callback!=null ) {
callback.onFailure(value);
}
}
public void onSuccess(T value) {
Callback<T> callback = null;
synchronized(this) {
this.value = value;
latch.countDown();
callback = next;
}
if( callback!=null ) {
callback.onSuccess(value);
}
}
public void then(Callback<T> callback) {
boolean fire = false;
synchronized(this) {
next = callback;
if( latch.getCount() == 0 ) {
fire = true;
}
}
if( fire ) {
if( error!=null ) {
callback.onFailure(error);
} else {
callback.onSuccess(value);
}
}
}
public T await(long amount, TimeUnit unit) throws Exception {
if( latch.await(amount, unit) ) {
return get();
} else {
throw new TimeoutException();
}
}
public T await() throws Exception {
latch.await();
return get();
}
private T get() throws Exception {
Throwable e = error;
if( e !=null ) {
if( e instanceof RuntimeException ) {
throw (RuntimeException) e;
} else if( e instanceof Exception) {
throw (Exception) e;
} else if( e instanceof Error) {
throw (Error) e;
} else {
// don't expect to hit this case.
throw new RuntimeException(e);
}
}
return value;
}
}
@@ -1,47 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.client;
/**
* <p>
* Function Result that carries one value.
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class ProxyCallback<T> implements Callback<T> {
public final Callback<T> next;
public ProxyCallback(Callback<T> next) {
this.next = next;
}
public void onSuccess(T value) {
if( next!=null ) {
next.onSuccess(value);
}
}
public void onFailure(Throwable value) {
if( next!=null ) {
next.onFailure(value);
}
}
}
@@ -1,31 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.client;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public enum QoS {
AT_MOST_ONCE,
AT_LEAST_ONCE,
EXACTLY_ONCE
}
@@ -1,78 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.client;
import org.fusesource.hawtbuf.UTF8Buffer;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class Topic {
private final UTF8Buffer name;
private final QoS qos;
public Topic(String name, QoS qos) {
this(new UTF8Buffer(name), qos);
}
public Topic(UTF8Buffer name, QoS qos) {
this.name = name;
this.qos = qos;
}
public UTF8Buffer name() {
return name;
}
public QoS qos() {
return qos;
}
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (o.getClass() != this.getClass()) return false;
if (this == o) return true;
Topic topic = (Topic) o;
if (name != null ? !name.equals(topic.name) : topic.name != null) return false;
if (qos != topic.qos) return false;
return true;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (qos != null ? qos.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "{ name=" + name +
", qos=" + qos +
" }";
}
}
@@ -1,50 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.client;
import org.fusesource.mqtt.codec.MQTTFrame;
/**
* A subclass of this can be configured on an MQTT connection to
* get more insight into what it's doing.
*/
public class Tracer {
/**
* Override to log/capture debug level messages
* @param message
* @param args
*/
public void debug(String message, Object...args) {
}
/**
* Called when a MQTTFrame sent to the remote peer.
* @param frame
*/
public void onSend(MQTTFrame frame) {
}
/**
* Called when a MQTTFrame is received from the remote peer.
* @param frame
*/
public void onReceive(MQTTFrame frame) {
}
}
@@ -1,103 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.codec;
import org.fusesource.hawtbuf.*;
import java.io.IOException;
import java.net.ProtocolException;
import static org.fusesource.mqtt.codec.MessageSupport.*;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class CONNACK implements Message {
public static final byte TYPE = 2;
public byte[] payload;
public static enum Code {
CONNECTION_ACCEPTED,
CONNECTION_REFUSED_UNACCEPTED_PROTOCOL_VERSION,
CONNECTION_REFUSED_IDENTIFIER_REJECTED,
CONNECTION_REFUSED_SERVER_UNAVAILABLE,
CONNECTION_REFUSED_BAD_USERNAME_OR_PASSWORD,
CONNECTION_REFUSED_NOT_AUTHORIZED;
}
private Code code = Code.CONNECTION_ACCEPTED;
public byte messageType() {
return TYPE;
}
public CONNACK decode(MQTTFrame frame) throws ProtocolException {
assert(frame.buffers.length == 1);
DataByteArrayInputStream is = new DataByteArrayInputStream(frame.buffers[0]);
is.skip(1);
byte c = is.readByte();
if( c >= Code.values().length ) {
throw new ProtocolException("Invalid CONNACK encoding");
}
code = Code.values()[c];
payload = new byte[is.available()];
try {
is.read(payload);
} catch (IOException e) {
e.printStackTrace();
}
return this;
}
public MQTTFrame encode() {
try {
DataByteArrayOutputStream os = new DataByteArrayOutputStream(2);
os.writeByte(0);
os.writeByte(code.ordinal());
MQTTFrame frame = new MQTTFrame();
frame.commandType(TYPE);
return frame.buffer(os.toBuffer());
} catch (IOException e) {
throw new RuntimeException("The impossible happened");
}
}
public Code code() {
return code;
}
public CONNACK code(Code code) {
this.code = code;
return this;
}
@Override
public String toString() {
return "CONNACK{" +
"code=" + code +
'}';
}
}
@@ -1,284 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.codec;
import java.io.IOException;
import java.net.ProtocolException;
import org.fusesource.mqtt.client.QoS;
import org.fusesource.hawtbuf.DataByteArrayInputStream;
import org.fusesource.hawtbuf.DataByteArrayOutputStream;
import org.fusesource.hawtbuf.UTF8Buffer;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class CONNECT implements MessageSupport.Message {
public static final byte TYPE = 1;
private static final UTF8Buffer V3_PROTOCOL_NAME = new UTF8Buffer("MQIsdp");
private static final UTF8Buffer V4_PROTOCOL_NAME = new UTF8Buffer("MQTT");
private short keepAlive = 30;
private UTF8Buffer clientId;
private UTF8Buffer willTopic;
private UTF8Buffer willMessage = new UTF8Buffer("");
private boolean willRetain;
private byte willQos;
private boolean cleanSession = true;
private UTF8Buffer userName;
private UTF8Buffer password;
private int version = 3;
public CONNECT(){
}
public CONNECT(CONNECT other) {
this.keepAlive = other.keepAlive;
this.clientId = other.clientId;
this.willTopic = other.willTopic;
this.willMessage = other.willMessage;
this.willRetain = other.willRetain;
this.willQos = other.willQos;
this.cleanSession = other.cleanSession;
this.userName = other.userName;
this.password = other.password;
this.version = other.version;
}
public byte messageType() {
return TYPE;
}
public CONNECT decode(MQTTFrame frame) throws ProtocolException {
assert(frame.buffers.length == 1);
DataByteArrayInputStream is = new DataByteArrayInputStream(frame.buffers[0]);
UTF8Buffer protocolName = MessageSupport.readUTF(is);
if (V4_PROTOCOL_NAME.equals(protocolName)) {
version = is.readByte() & 0xFF;
if( version < 4 ) {
throw new ProtocolException("Invalid CONNECT frame: protocol name/version mismatch");
}
} else if( V3_PROTOCOL_NAME.equals(protocolName) ) {
version = is.readByte() & 0xFF;
if( version != 3 ) {
throw new ProtocolException("Invalid CONNECT frame: protocol name/version mismatch");
}
} else {
throw new ProtocolException("Invalid CONNECT frame");
}
byte flags = is.readByte();
boolean username_flag = (flags & 0x80) > 0;
boolean password_flag = (flags & 0x40) > 0;
willRetain = (flags & 0x20) > 0;
willQos = (byte) ((flags & 0x18) >>> 3);
boolean will_flag = (flags & 0x04) > 0;
cleanSession = (flags & 0x02) > 0;
keepAlive = is.readShort();
clientId = MessageSupport.readUTF(is);
if( clientId.length == 0 ) {
clientId = null;
}
if(will_flag) {
willTopic = MessageSupport.readUTF(is);
willMessage = MessageSupport.readUTF(is);
}
if( username_flag ) {
userName = MessageSupport.readUTF(is);
}
if( password_flag ) {
password = MessageSupport.readUTF(is);
}
return this;
}
public MQTTFrame encode() {
try {
if( (clientId==null || clientId.length == 0) && !cleanSession ) {
throw new IllegalArgumentException("A clean session must be used when no clientId is specified");
}
DataByteArrayOutputStream os = new DataByteArrayOutputStream(500);
if(version==3) {
MessageSupport.writeUTF(os, V3_PROTOCOL_NAME);
os.writeByte(version);
} else if(version >= 4) {
MessageSupport.writeUTF(os, V4_PROTOCOL_NAME);
os.writeByte(version);
} else {
throw new IllegalArgumentException("Invalid version: "+version);
}
int flags = 0;
if(userName!=null) {
flags |= 0x80;
}
if(password!=null) {
flags |= 0x40;
}
if(willTopic!=null && willMessage!=null) {
flags |= 0x04;
if(willRetain) {
flags |= 0x20;
}
flags |= (willQos << 3) & 0x18;
}
if(cleanSession) {
flags |= 0x02;
}
os.writeByte(flags);
os.writeShort(keepAlive);
MessageSupport.writeUTF(os, clientId);
if(willTopic!=null && willMessage!=null) {
MessageSupport.writeUTF(os, willTopic);
MessageSupport.writeUTF(os, willMessage);
}
if(userName!=null) {
MessageSupport.writeUTF(os, userName);
}
if(password!=null) {
MessageSupport.writeUTF(os, password);
}
MQTTFrame frame = new MQTTFrame();
frame.commandType(TYPE);
return frame.buffer(os.toBuffer());
} catch (IOException e) {
throw new RuntimeException("The impossible happened");
}
}
public boolean cleanSession() {
return cleanSession;
}
public CONNECT cleanSession(boolean cleanSession) {
this.cleanSession = cleanSession;
return this;
}
public UTF8Buffer clientId() {
return clientId;
}
public CONNECT clientId(UTF8Buffer clientId) {
this.clientId = clientId;
return this;
}
public short keepAlive() {
return keepAlive;
}
public CONNECT keepAlive(short keepAlive) {
this.keepAlive = keepAlive;
return this;
}
public UTF8Buffer password() {
return password;
}
public CONNECT password(UTF8Buffer password) {
this.password = password;
return this;
}
public UTF8Buffer userName() {
return userName;
}
public CONNECT userName(UTF8Buffer userName) {
this.userName = userName;
return this;
}
public UTF8Buffer willMessage() {
return willMessage;
}
public CONNECT willMessage(UTF8Buffer willMessage) {
this.willMessage = willMessage;
return this;
}
public QoS willQos() {
return QoS.values()[willQos];
}
public CONNECT willQos(QoS willQos) {
this.willQos = (byte) willQos.ordinal();
return this;
}
public boolean willRetain() {
return willRetain;
}
public CONNECT willRetain(boolean willRetain) {
this.willRetain = willRetain;
return this;
}
public UTF8Buffer willTopic() {
return willTopic;
}
public CONNECT willTopic(UTF8Buffer willTopic) {
this.willTopic = willTopic;
return this;
}
public int version() {
return version;
}
public CONNECT version(int version) {
if(version==3) {
this.version = version;
} else if(version >= 4) {
this.version = version;
} else {
throw new IllegalArgumentException("Invalid version: "+version);
}
return this;
}
@Override
public String toString() {
return "CONNECT{" +
"cleanSession=" + cleanSession +
", keepAlive=" + keepAlive +
", clientId=" + clientId +
", willTopic=" + willTopic +
", willMessage=" + willMessage +
", willRetain=" + willRetain +
", willQos=" + willQos +
", userName=" + userName +
", password=" + password +
'}';
}
}
@@ -1,47 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.codec;
import java.net.ProtocolException;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class DISCONNECT extends MessageSupport.EmptyBase implements MessageSupport.Message {
public static final byte TYPE = 14;
public byte messageType() {
return TYPE;
}
@Override
public DISCONNECT decode(MQTTFrame frame) throws ProtocolException {
return (DISCONNECT) super.decode(frame);
}
@Override
public String toString() {
return "DISCONNECT";
}
}
@@ -1,159 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.codec;
import org.fusesource.mqtt.client.QoS;
import org.fusesource.hawtbuf.Buffer;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class MQTTFrame extends MessageSupport.HeaderBase {
private static final Buffer[] NO_BUFFERS = new Buffer[0];
public Buffer[] buffers = NO_BUFFERS;
public MQTTFrame() {
}
public MQTTFrame( Buffer buffer) {
this(new Buffer[]{buffer});
}
public MQTTFrame( Buffer[] buffers) {
this.buffers = buffers;
}
public Buffer[] buffers() {
return buffers;
}
public MQTTFrame buffers(Buffer...buffers) {
this.buffers = buffers;
return this;
}
public MQTTFrame buffer(Buffer buffer) {
this.buffers = new Buffer[]{buffer};
return this;
}
@Override
public byte header() {
return super.header();
}
@Override
public MQTTFrame header(byte header) {
return (MQTTFrame)super.header(header);
}
@Override
public byte messageType() {
return super.messageType();
}
@Override
public MQTTFrame commandType(int type) {
return (MQTTFrame)super.commandType(type);
}
@Override
public boolean dup() {
return super.dup();
}
@Override
public MQTTFrame dup(boolean dup) {
return (MQTTFrame) super.dup(dup);
}
@Override
public QoS qos() {
return super.qos();
}
@Override
public MQTTFrame qos(QoS qos) {
return (MQTTFrame) super.qos(qos);
}
@Override
public boolean retain() {
return super.retain();
}
@Override
public MQTTFrame retain(boolean retain) {
return (MQTTFrame) super.retain(retain);
}
@Override
public String toString() {
String type = "unknown";
switch(messageType()) {
case CONNECT.TYPE:
type = "CONNECT";
break;
case CONNACK.TYPE:
type = "CONNACK";
break;
case DISCONNECT.TYPE:
type = "DISCONNECT";
break;
case PINGREQ.TYPE:
type = "PINGREQ";
break;
case PINGRESP.TYPE:
type = "PINGRESP";
break;
case SUBSCRIBE.TYPE:
type = "SUBSCRIBE";
break;
case UNSUBSCRIBE.TYPE:
type = "UNSUBSCRIBE";
break;
case UNSUBACK.TYPE:
type = "UNSUBACK";
break;
case PUBLISH.TYPE:
type = "PUBLISH";
break;
case SUBACK.TYPE:
type = "SUBACK";
break;
case PUBACK.TYPE:
type = "PUBACK";
break;
case PUBREC.TYPE:
type = "PUBREC";
break;
case PUBREL.TYPE:
type = "PUBREL";
break;
case PUBCOMP.TYPE:
type = "PUBCOMP";
break;
default:
}
return "MQTTFrame { type: "+type+", qos: "+qos()+", dup:"+dup()+" }";
}
}
@@ -1,135 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.codec;
import org.fusesource.hawtbuf.Buffer;
import org.fusesource.hawtdispatch.transport.AbstractProtocolCodec;
import org.fusesource.hawtdispatch.util.BufferPools;
import java.io.IOException;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class MQTTProtocolCodec extends AbstractProtocolCodec {
private static final BufferPools BUFFER_POOLS = new BufferPools();
private int maxMessageLength = 1024*1024*100;
public MQTTProtocolCodec() {
this.bufferPools = BUFFER_POOLS;
}
public int getMaxMessageLength() {
return maxMessageLength;
}
public void setMaxMessageLength(int maxMessageLength) {
this.maxMessageLength = maxMessageLength;
}
@Override
protected void encode(Object value) throws IOException {
MQTTFrame frame = (MQTTFrame) value;
nextWriteBuffer.write(frame.header());
int remaining = 0;
for(Buffer buffer : frame.buffers) {
remaining += buffer.length;
}
do {
byte digit = (byte) (remaining & 0x7F);
remaining >>>= 7;
if (remaining > 0) {
digit |= 0x80;
}
nextWriteBuffer.write(digit);
} while (remaining > 0);
for(Buffer buffer : frame.buffers) {
nextWriteBuffer.write(buffer.data, buffer.offset, buffer.length);
}
}
@Override
protected Action initialDecodeAction() {
return readHeader;
}
private final Action readHeader = new Action() {
public MQTTFrame apply() throws IOException {
int length = readLength();
if( length >= 0 ) {
if( length > maxMessageLength) {
throw new IOException("The maximum message length was exceeded");
}
byte header = readBuffer.get(readStart);
readStart = readEnd;
if( length > 0 ) {
nextDecodeAction = readBody(header, length);
} else {
return new MQTTFrame().header(header);
}
}
return null;
}
};
private int readLength() throws IOException {
readEnd = readStart+2; // Header is at least 2 bytes..
int limit = readBuffer.position();
int length = 0;
int multiplier = 1;
byte digit;
while (readEnd-1 < limit) {
// last byte is part of the encoded length..
digit = readBuffer.get(readEnd-1);
length += (digit & 0x7F) * multiplier;
if( (digit & 0x80) == 0 ) {
return length;
}
// length extends out one more byte..
multiplier <<= 7;
readEnd++;
}
return -1;
}
Action readBody(final byte header, final int length) {
return new Action() {
public MQTTFrame apply() throws IOException {
int limit = readBuffer.position();
if ((limit - readStart) < length) {
readEnd = limit;
return null;
} else {
Buffer body = new Buffer(readBuffer.array(), readStart, length);
readEnd = readStart = readStart + length;
nextDecodeAction = readHeader;
return new MQTTFrame(body).header(header);
}
}
};
}
}
@@ -1,202 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.codec;
import java.io.IOException;
import java.net.ProtocolException;
import org.fusesource.mqtt.client.QoS;
import org.fusesource.hawtbuf.Buffer;
import org.fusesource.hawtbuf.DataByteArrayInputStream;
import org.fusesource.hawtbuf.DataByteArrayOutputStream;
import org.fusesource.hawtbuf.UTF8Buffer;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public final class MessageSupport {
private MessageSupport() throws InstantiationException {
throw new InstantiationException("This class is not for instantiation");
}
/**
* All command objects implement this interface.
*/
static public interface Message {
public byte messageType();
public Message decode(MQTTFrame frame) throws ProtocolException;
public MQTTFrame encode();
}
/**
* All command objects that can get acked implement this interface.
*/
static public interface Acked extends Message {
public boolean dup();
public Acked dup(boolean dup);
public QoS qos();
public short messageId();
public Acked messageId(short messageId);
}
static protected UTF8Buffer readUTF(DataByteArrayInputStream is) throws ProtocolException {
int size = is.readUnsignedShort();
Buffer buffer = is.readBuffer(size);
if (buffer == null || buffer.length != size) {
throw new ProtocolException("Invalid message encoding");
}
return buffer.utf8();
}
static protected void writeUTF(DataByteArrayOutputStream os, Buffer buffer) throws IOException {
os.writeShort(buffer.length);
os.write(buffer);
}
static abstract public class AckBase {
private short messageId;
public byte[] payload;
abstract byte messageType();
protected AckBase decode(MQTTFrame frame) throws ProtocolException {
assert(frame.buffers.length == 1);
DataByteArrayInputStream is = new DataByteArrayInputStream(frame.buffers[0]);
messageId = is.readShort();
payload = new byte[is.available()];
try {
is.read(payload);
} catch (IOException e) {
e.printStackTrace();
}
return this;
}
public MQTTFrame encode() {
try {
DataByteArrayOutputStream os = new DataByteArrayOutputStream(2);
os.writeShort(messageId);
MQTTFrame frame = new MQTTFrame();
frame.commandType(messageType());
return frame.buffer(os.toBuffer());
} catch (IOException e) {
throw new RuntimeException("The impossible happened");
}
}
public short messageId() {
return messageId;
}
protected AckBase messageId(short messageId) {
this.messageId = messageId;
return this;
}
@Override
public String toString() {
return getClass().getSimpleName()+"{" +
"messageId=" + messageId +
'}';
}
}
static abstract public class EmptyBase {
abstract byte messageType();
protected EmptyBase decode(MQTTFrame frame) throws ProtocolException {
return this;
}
public MQTTFrame encode() {
return new MQTTFrame().commandType(messageType());
}
}
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
static public class HeaderBase {
protected byte header;
protected byte header() {
return header;
}
protected HeaderBase header(byte header) {
this.header = header;
return this;
}
protected byte messageType() {
return (byte) ((header & 0xF0) >>> 4);
}
protected HeaderBase commandType(int type) {
this.header &= 0x0F;
this.header |= (type << 4) & 0xF0;
return this;
}
protected QoS qos() {
return QoS.values()[((header & 0x06) >>> 1)];
}
protected HeaderBase qos(QoS qos) {
this.header &= 0xF9;
this.header |= (qos.ordinal() << 1) & 0x06;
return this;
}
protected boolean dup() {
return (header & 0x08) > 0;
}
protected HeaderBase dup(boolean dup) {
if(dup) {
this.header |= 0x08;
} else {
this.header &= 0xF7;
}
return this;
}
protected boolean retain() {
return (header & 0x01) > 0;
}
protected HeaderBase retain(boolean retain) {
if(retain) {
this.header |= 0x01;
} else {
this.header &= 0xFE;
}
return this;
}
}
}
@@ -1,48 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.codec;
import java.net.ProtocolException;
import static org.fusesource.mqtt.codec.MessageSupport.*;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class PINGREQ extends MessageSupport.EmptyBase implements Message {
public static final byte TYPE = 12;
public byte messageType() {
return TYPE;
}
@Override
public PINGREQ decode(MQTTFrame frame) throws ProtocolException {
return (PINGREQ) super.decode(frame);
}
@Override
public String toString() {
return "PINGREQ";
}
}
@@ -1,47 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.codec;
import java.net.ProtocolException;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class PINGRESP extends MessageSupport.EmptyBase implements MessageSupport.Message {
public static final byte TYPE = 13;
public byte messageType() {
return TYPE;
}
@Override
public PINGRESP decode(MQTTFrame frame) throws ProtocolException {
return (PINGRESP) super.decode(frame);
}
@Override
public String toString() {
return "PINGRESP";
}
}
@@ -1,46 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.codec;
import java.net.ProtocolException;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class PUBACK extends MessageSupport.AckBase implements MessageSupport.Message {
public static final byte TYPE = 4;
public byte messageType() {
return TYPE;
}
@Override
public PUBACK decode(MQTTFrame frame) throws ProtocolException {
return (PUBACK) super.decode(frame);
}
@Override
public PUBACK messageId(short messageId) {
return (PUBACK) super.messageId(messageId);
}
}
@@ -1,47 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.codec;
import java.net.ProtocolException;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class PUBCOMP extends MessageSupport.AckBase implements MessageSupport.Message {
public static final byte TYPE = 7;
public byte messageType() {
return TYPE;
}
@Override
public PUBCOMP decode(MQTTFrame frame) throws ProtocolException {
return (PUBCOMP) super.decode(frame);
}
@Override
public PUBCOMP messageId(short messageId) {
return (PUBCOMP) super.messageId(messageId);
}
}
@@ -1,159 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.codec;
import org.fusesource.mqtt.client.QoS;
import org.fusesource.hawtbuf.Buffer;
import org.fusesource.hawtbuf.DataByteArrayInputStream;
import org.fusesource.hawtbuf.DataByteArrayOutputStream;
import org.fusesource.hawtbuf.UTF8Buffer;
import java.io.IOException;
import java.net.ProtocolException;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class PUBLISH extends MessageSupport.HeaderBase implements MessageSupport.Message, MessageSupport.Acked {
public static final byte TYPE = 3;
private UTF8Buffer topicName;
private short messageId;
private Buffer payload;
public PUBLISH() {
qos(QoS.AT_LEAST_ONCE);
}
public byte messageType() {
return TYPE;
}
public PUBLISH decode(MQTTFrame frame) throws ProtocolException {
assert(frame.buffers.length == 1);
header(frame.header());
DataByteArrayInputStream is = new DataByteArrayInputStream(frame.buffers[0]);
topicName = MessageSupport.readUTF(is);
QoS qos = qos();
if(qos != QoS.AT_MOST_ONCE) {
messageId = is.readShort();
}
payload = is.readBuffer(is.available());
if( payload == null ) {
payload = new Buffer(0);
}
return this;
}
public MQTTFrame encode() {
try {
DataByteArrayOutputStream os = new DataByteArrayOutputStream();
MessageSupport.writeUTF(os, topicName);
QoS qos = qos();
if(qos != QoS.AT_MOST_ONCE) {
os.writeShort(messageId);
}
MQTTFrame frame = new MQTTFrame();
frame.header(header());
frame.commandType(TYPE);
if(payload!=null && payload.length!=0) {
os.write(payload);
}
frame.buffer(os.toBuffer());
return frame;
} catch (IOException e) {
throw new RuntimeException("The impossible happened");
}
}
@Override
public boolean dup() {
return super.dup();
}
@Override
public PUBLISH dup(boolean dup) {
return (PUBLISH) super.dup(dup);
}
@Override
public QoS qos() {
return super.qos();
}
@Override
public PUBLISH qos(QoS qos) {
return (PUBLISH) super.qos(qos);
}
@Override
public boolean retain() {
return super.retain();
}
@Override
public PUBLISH retain(boolean retain) {
return (PUBLISH) super.retain(retain);
}
public short messageId() {
return messageId;
}
public PUBLISH messageId(short messageId) {
this.messageId = messageId;
return this;
}
public Buffer payload() {
return payload;
}
public PUBLISH payload(Buffer payload) {
this.payload = payload;
return this;
}
public UTF8Buffer topicName() {
return topicName;
}
public PUBLISH topicName(UTF8Buffer topicName) {
this.topicName = topicName;
return this;
}
@Override
public String toString() {
return "PUBLISH{" +
"dup=" + dup() +
", qos=" + qos() +
", retain=" + retain() +
", messageId=" + messageId +
", topicName=" + topicName +
", payload=" + payload +
'}';
}
}
@@ -1,47 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.codec;
import java.net.ProtocolException;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class PUBREC extends MessageSupport.AckBase implements MessageSupport.Message {
public static final byte TYPE = 5;
public byte messageType() {
return TYPE;
}
@Override
public PUBREC decode(MQTTFrame frame) throws ProtocolException {
return (PUBREC) super.decode(frame);
}
@Override
public PUBREC messageId(short messageId) {
return (PUBREC) super.messageId(messageId);
}
}
@@ -1,103 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.codec;
import org.fusesource.mqtt.client.QoS;
import org.fusesource.hawtbuf.DataByteArrayInputStream;
import org.fusesource.hawtbuf.DataByteArrayOutputStream;
import java.io.IOException;
import java.net.ProtocolException;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class PUBREL extends MessageSupport.HeaderBase implements MessageSupport.Message, MessageSupport.Acked {
public static final byte TYPE = 6;
private short messageId;
public byte messageType() {
return TYPE;
}
public PUBREL() {
qos(QoS.AT_LEAST_ONCE);
}
public PUBREL decode(MQTTFrame frame) throws ProtocolException {
assert(frame.buffers.length == 1);
header(frame.header());
DataByteArrayInputStream is = new DataByteArrayInputStream(frame.buffers[0]);
messageId = is.readShort();
return this;
}
public MQTTFrame encode() {
try {
DataByteArrayOutputStream os = new DataByteArrayOutputStream(2);
os.writeShort(messageId);
MQTTFrame frame = new MQTTFrame();
frame.header(header());
frame.commandType(TYPE);
return frame.buffer(os.toBuffer());
} catch (IOException e) {
throw new RuntimeException("The impossible happened");
}
}
@Override
public boolean dup() {
return super.dup();
}
@Override
public PUBREL dup(boolean dup) {
return (PUBREL) super.dup(dup);
}
@Override
public QoS qos() {
return super.qos();
}
public short messageId() {
return messageId;
}
public PUBREL messageId(short messageId) {
this.messageId = messageId;
return this;
}
@Override
public String toString() {
return "PUBREL{" +
"dup=" + dup() +
", qos=" + qos() +
", messageId=" + messageId +
'}';
}
}
@@ -1,94 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.codec;
import org.fusesource.hawtbuf.DataByteArrayInputStream;
import org.fusesource.hawtbuf.DataByteArrayOutputStream;
import java.io.IOException;
import java.net.ProtocolException;
import java.util.Arrays;
import static org.fusesource.mqtt.codec.MessageSupport.*;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class SUBACK implements Message {
public static final byte[] NO_GRANTED_QOS = new byte[0];
public static final byte TYPE = 9;
private short messageId;
private byte[] grantedQos = NO_GRANTED_QOS;
public byte messageType() {
return TYPE;
}
public SUBACK decode(MQTTFrame frame) throws ProtocolException {
assert(frame.buffers.length == 1);
DataByteArrayInputStream is = new DataByteArrayInputStream(frame.buffers[0]);
messageId = is.readShort();
grantedQos = is.readBuffer(is.available()).toByteArray();
return this;
}
public MQTTFrame encode() {
try {
DataByteArrayOutputStream os = new DataByteArrayOutputStream(2+grantedQos.length);
os.writeShort(messageId);
os.write(grantedQos);
MQTTFrame frame = new MQTTFrame();
frame.commandType(TYPE);
return frame.buffer(os.toBuffer());
} catch (IOException e) {
throw new RuntimeException("The impossible happened");
}
}
public byte[] grantedQos() {
return grantedQos;
}
public SUBACK grantedQos(byte[] grantedQos) {
this.grantedQos = grantedQos;
return this;
}
public short messageId() {
return messageId;
}
public SUBACK messageId(short messageId) {
this.messageId = messageId;
return this;
}
@Override
public String toString() {
return "SUBACK{" +
"grantedQos=" + Arrays.toString(grantedQos) +
", messageId=" +messageId +
'}';
}
}
@@ -1,134 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.codec;
import org.fusesource.mqtt.client.QoS;
import org.fusesource.mqtt.client.Topic;
import org.fusesource.hawtbuf.DataByteArrayInputStream;
import org.fusesource.hawtbuf.DataByteArrayOutputStream;
import java.io.IOException;
import java.net.ProtocolException;
import java.util.ArrayList;
import java.util.Arrays;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class SUBSCRIBE extends MessageSupport.HeaderBase implements MessageSupport.Message, MessageSupport.Acked {
public static final byte TYPE = 8;
public static final Topic[] NO_TOPICS = new Topic[0];
private short messageId;
private Topic[] topics = NO_TOPICS;
public SUBSCRIBE() {
qos(QoS.AT_LEAST_ONCE);
}
public byte messageType() {
return TYPE;
}
public SUBSCRIBE decode(MQTTFrame frame) throws ProtocolException {
assert(frame.buffers.length == 1);
header(frame.header());
DataByteArrayInputStream is = new DataByteArrayInputStream(frame.buffers[0]);
QoS qos = qos();
if(qos != QoS.AT_MOST_ONCE) {
messageId = is.readShort();
}
ArrayList<Topic> list = new ArrayList<Topic>();
while(is.available() > 0) {
Topic topic = new Topic(MessageSupport.readUTF(is), QoS.values()[is.readByte()]);
list.add(topic);
}
topics = list.toArray(new Topic[list.size()]);
return this;
}
public MQTTFrame encode() {
try {
DataByteArrayOutputStream os = new DataByteArrayOutputStream();
QoS qos = qos();
if(qos != QoS.AT_MOST_ONCE) {
os.writeShort(messageId);
}
for(Topic topic: topics) {
MessageSupport.writeUTF(os, topic.name());
os.writeByte(topic.qos().ordinal());
}
MQTTFrame frame = new MQTTFrame();
frame.header(header());
frame.commandType(TYPE);
return frame.buffer(os.toBuffer());
} catch (IOException e) {
throw new RuntimeException("The impossible happened");
}
}
@Override
public boolean dup() {
return super.dup();
}
@Override
public SUBSCRIBE dup(boolean dup) {
return (SUBSCRIBE) super.dup(dup);
}
@Override
public QoS qos() {
return super.qos();
}
public short messageId() {
return messageId;
}
public SUBSCRIBE messageId(short messageId) {
this.messageId = messageId;
return this;
}
public Topic[] topics() {
return topics;
}
public SUBSCRIBE topics(Topic[] topics) {
this.topics = topics;
return this;
}
@Override
public String toString() {
return "SUBSCRIBE{" +
"dup=" + dup() +
", qos=" + qos() +
", messageId=" + messageId +
", topics=" + (topics == null ? null : Arrays.asList(topics)) +
'}';
}
}
@@ -1,49 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.codec;
import java.net.ProtocolException;
import static org.fusesource.mqtt.codec.MessageSupport.AckBase;
import static org.fusesource.mqtt.codec.MessageSupport.*;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class UNSUBACK extends AckBase implements Message {
public static final byte TYPE = 11;
public byte messageType() {
return TYPE;
}
@Override
public UNSUBACK decode(MQTTFrame frame) throws ProtocolException {
return (UNSUBACK) super.decode(frame);
}
@Override
public UNSUBACK messageId(short messageId) {
return (UNSUBACK) super.messageId(messageId);
}
}
@@ -1,130 +0,0 @@
/**
* Copyright (C) 2010-2012, FuseSource Corp. All rights reserved.
*
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.mqtt.codec;
import org.fusesource.mqtt.client.QoS;
import org.fusesource.hawtbuf.DataByteArrayInputStream;
import org.fusesource.hawtbuf.DataByteArrayOutputStream;
import org.fusesource.hawtbuf.UTF8Buffer;
import java.io.IOException;
import java.net.ProtocolException;
import java.util.ArrayList;
import java.util.Arrays;
/**
* <p>
* </p>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
public class UNSUBSCRIBE extends MessageSupport.HeaderBase implements MessageSupport.Message, MessageSupport.Acked {
public static final byte TYPE = 10;
public static final UTF8Buffer[] NO_TOPICS = new UTF8Buffer[0];
private short messageId;
private UTF8Buffer[] topics = NO_TOPICS;
public UNSUBSCRIBE() {
qos(QoS.AT_LEAST_ONCE);
}
public byte messageType() {
return TYPE;
}
public UNSUBSCRIBE decode(MQTTFrame frame) throws ProtocolException {
assert(frame.buffers.length == 1);
header(frame.header());
DataByteArrayInputStream is = new DataByteArrayInputStream(frame.buffers[0]);
messageId = is.readShort();
ArrayList<UTF8Buffer> list = new ArrayList<UTF8Buffer>();
while(is.available() > 0) {
list.add(MessageSupport.readUTF(is));
}
topics = list.toArray(new UTF8Buffer[list.size()]);
return this;
}
public MQTTFrame encode() {
try {
DataByteArrayOutputStream os = new DataByteArrayOutputStream();
QoS qos = qos();
if(qos != QoS.AT_MOST_ONCE) {
os.writeShort(messageId);
}
for(UTF8Buffer topic: topics) {
MessageSupport.writeUTF(os, topic);
}
MQTTFrame frame = new MQTTFrame();
frame.header(header());
frame.commandType(TYPE);
return frame.buffer(os.toBuffer());
} catch (IOException e) {
throw new RuntimeException("The impossible happened");
}
}
@Override
public boolean dup() {
return super.dup();
}
@Override
public UNSUBSCRIBE dup(boolean dup) {
return (UNSUBSCRIBE) super.dup(dup);
}
@Override
public QoS qos() {
return super.qos();
}
public short messageId() {
return messageId;
}
public UNSUBSCRIBE messageId(short messageId) {
this.messageId = messageId;
return this;
}
public UTF8Buffer[] topics() {
return topics;
}
public UNSUBSCRIBE topics(UTF8Buffer[] topics) {
this.topics = topics;
return this;
}
@Override
public String toString() {
return "UNSUBSCRIBE{" +
"dup=" + dup() +
", qos=" + qos() +
", messageId=" + messageId +
", topics=" + (topics == null ? null : Arrays.asList(topics)) +
'}';
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.24</version>
<version>0.28</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -0,0 +1,142 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package cn.wildfirechat.pojos;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
import io.netty.util.internal.StringUtil;
public class InputCreateChannel {
private String owner;
private String name;
private String targetId;
private String callback;
private String portrait;
private int auto;
private String secret;
private String desc;
private int status;
private String extra;
private long updateDt;
public WFCMessage.ChannelInfo toProtoChannelInfo() {
WFCMessage.ChannelInfo.Builder builder = WFCMessage.ChannelInfo.newBuilder().setOwner(owner);
if (!StringUtil.isNullOrEmpty(name))
builder = builder.setName(name);
if (!StringUtil.isNullOrEmpty(targetId))
builder = builder.setTargetId(targetId);
if (!StringUtil.isNullOrEmpty(callback))
builder = builder.setCallback(callback);
if (!StringUtil.isNullOrEmpty(portrait))
builder = builder.setPortrait(portrait);
builder = builder.setAutomatic(auto);
if (!StringUtil.isNullOrEmpty(secret))
builder = builder.setSecret(secret);
if (!StringUtil.isNullOrEmpty(desc))
builder = builder.setDesc(desc);
builder = builder.setStatus(status);
if (!StringUtil.isNullOrEmpty(extra))
builder = builder.setExtra(extra);
if (!StringUtil.isNullOrEmpty(name))
builder = builder.setUpdateDt(updateDt);
else
builder = builder.setUpdateDt(System.currentTimeMillis());
return builder.build();
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTargetId() {
return targetId;
}
public void setTargetId(String targetId) {
this.targetId = targetId;
}
public String getCallback() {
return callback;
}
public void setCallback(String callback) {
this.callback = callback;
}
public String getPortrait() {
return portrait;
}
public void setPortrait(String portrait) {
this.portrait = portrait;
}
public int getAuto() {
return auto;
}
public void setAuto(int auto) {
this.auto = auto;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getExtra() {
return extra;
}
public void setExtra(String extra) {
this.extra = extra;
}
public long getUpdateDt() {
return updateDt;
}
public void setUpdateDt(long updateDt) {
this.updateDt = updateDt;
}
}
@@ -0,0 +1,59 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package cn.wildfirechat.pojos;
import cn.wildfirechat.proto.WFCMessage;
import io.netty.util.internal.StringUtil;
import java.util.Arrays;
import java.util.List;
public class InputModifyGroupInfo extends InputGroupBase {
private String group_id;
//ModifyGroupInfoType
private int type;
private String value;
public boolean isValide() {
return !StringUtil.isNullOrEmpty(group_id);
}
public String getGroup_id() {
return group_id;
}
public void setGroup_id(String group_id) {
this.group_id = group_id;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public WFCMessage.ModifyGroupInfoRequest toProtoGroupRequest() {
return WFCMessage.ModifyGroupInfoRequest.newBuilder()
.setGroupId(group_id)
.setType(type)
.setValue(value)
.addAllToLine(to_lines == null || to_lines.isEmpty() ? Arrays.asList(0) : to_lines)
.build();
}
}
@@ -129,8 +129,8 @@ public class MessagePayload {
builder.setSearchableContent(searchableContent);
if (!StringUtil.isNullOrEmpty(pushContent))
builder.setPushContent(pushContent);
if (!StringUtil.isNullOrEmpty(searchableContent))
builder.setContent(searchableContent);
if (!StringUtil.isNullOrEmpty(content))
builder.setContent(content);
if (!StringUtil.isNullOrEmpty(base64edData))
builder.setData(ByteString.copyFrom(Base64.getDecoder().decode(base64edData)));
if (!StringUtil.isNullOrEmpty(remoteMediaUrl))
@@ -0,0 +1,38 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <heavyrain.lee@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package cn.wildfirechat.pojos;
public class MultiMessageResult {
private long messageUid;
private long timestamp;
public MultiMessageResult() {
}
public MultiMessageResult(long messageUid, long timestamp) {
this.messageUid = messageUid;
this.timestamp = timestamp;
}
public long getMessageUid() {
return messageUid;
}
public void setMessageUid(long messageUid) {
this.messageUid = messageUid;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
}
@@ -0,0 +1,71 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <heavyrain.lee@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package cn.wildfirechat.pojos;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
import io.netty.util.internal.StringUtil;
import java.util.List;
public class MulticastMessageData {
private String sender;
private int line;
private MessagePayload payload;
private List<String> targets;
public String getSender() {
return sender;
}
public void setSender(String sender) {
this.sender = sender;
}
public int getLine() {
return line;
}
public void setLine(int line) {
this.line = line;
}
public MessagePayload getPayload() {
return payload;
}
public void setPayload(MessagePayload payload) {
this.payload = payload;
}
public static boolean isValide(MulticastMessageData sendMessageData) {
if(sendMessageData == null ||
StringUtil.isNullOrEmpty(sendMessageData.getSender()) ||
sendMessageData.getPayload() == null) {
return false;
}
return true;
}
public WFCMessage.MultiCastMessage toProtoMessage() {
return WFCMessage.MultiCastMessage.newBuilder().setFromUser(sender)
.setLine(line)
.setContent(payload.toProtoMessageContent())
.addAllTo(targets)
.build();
}
public List<String> getTargets() {
return targets;
}
public void setTargets(List<String> targets) {
this.targets = targets;
}
}
@@ -0,0 +1,28 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <heavyrain.lee@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package cn.wildfirechat.pojos;
import cn.wildfirechat.proto.WFCMessage;
public class OutputCreateChannel {
private String targetId;
public OutputCreateChannel(String targetId) {
this.targetId = targetId;
}
public String getTargetId() {
return targetId;
}
public void setTargetId(String targetId) {
this.targetId = targetId;
}
}
@@ -0,0 +1,7 @@
package cn.wildfirechat.pojos;
public class SystemSettingPojo {
public int id;
public String value;
public String desc;
}
@@ -26,16 +26,23 @@ public interface APIPath {
String Msg_Send = "/admin/message/send";
String Msg_Recall = "/admin/message/recall";
String Msg_Broadcast = "/admin/message/broadcast";
String Msg_Multicast = "/admin/message/multicast";
String Create_Group = "/admin/group/create";
String Group_Dismiss = "/admin/group/del";
String Group_Transfer = "/admin/group/transfer";
String Group_Get_Info = "/admin/group/get_info";
String Group_Modify_Info = "/admin/group/modify";
String Group_Member_List = "/admin/group/member/list";
String Group_Member_Add = "/admin/group/member/add";
String Group_Member_Kickoff = "/admin/group/member/del";
String Create_Channel = "/admin/channel/create";
String Get_System_Setting = "/admin/system/get_setting";
String Put_System_Setting = "/admin/system/put_setting";
String Channel_Message_Send = "/channel/message/send";
String Robot_User_Info = "/robot/user_info";
@@ -128,6 +128,9 @@ public class ProtoConstants {
}
public interface SystemSettingType {
int Group_Max_Member_Count = 1;
}
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -6,7 +6,7 @@
<!--<relativePath>../pom.xml</relativePath>-->
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.24</version>
<version>0.28</version>
</parent>
<artifactId>distribution</artifactId>
@@ -19,11 +19,6 @@
<directory>../broker/migrate/h2/</directory>
<outputDirectory>/migrate/h2</outputDirectory>
</fileSet>
<fileSet>
<directory>../wildfire_jmeter/test_plan/</directory>
<outputDirectory>/tools/jmeter</outputDirectory>
</fileSet>
</fileSets>
<files>
@@ -68,23 +63,12 @@
<outputDirectory>config/</outputDirectory>
</file>
<file>
<source>../wildfire_jmeter/target/wildfire-xmeter-jar-with-dependencies.jar</source>
<outputDirectory>tools/jmeter</outputDirectory>
</file>
<file>
<source>../broker/target/moquette-broker-${project.version}.jar</source>
<outputDirectory>lib/</outputDirectory>
<destName>moquette-broker-${project.version}.jar</destName>
</file>
<file>
<source>../client/target/client-${project.version}.jar</source>
<outputDirectory>java_client/</outputDirectory>
<destName>client-${project.version}.jar</destName>
</file>
<file>
<source>../common/target/common-${project.version}.jar</source>
<outputDirectory>java_client/</outputDirectory>
@@ -26,7 +26,7 @@ host 0.0.0.0
##本地绑定端口
local_port 80
##是否使用内置DB。0使用mysql;1使用h2db
##是否使用内置DB。0使用mysql;1使用h2db;2使用mysql+mongodb。社区版只支持0和1,专业版支持2
embed.db 1
##服务器管理接口密钥
@@ -42,6 +42,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
#*********************************************************************
@@ -96,6 +102,15 @@ 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
#*********************************************************************
# Netty Configuration
@@ -17,9 +17,9 @@ goto end
rem Set JavaHome if it exists
if exist { "%JAVA_HOME%\bin\java" } (
set "JAVA="%JAVA_HOME%\bin\java""
) else {
) else (
set "JAVA="java""
}
)
echo Using JAVA_HOME: "%JAVA_HOME%"
echo Using WILDFIRECHAT_HOME: "%WILDFIRECHAT_HOME%"
+1 -4
View File
@@ -12,7 +12,7 @@
<artifactId>wildfirechat-parent</artifactId>
<packaging>pom</packaging>
<version>0.24</version>
<version>0.28</version>
<name>Wildfire Chat</name>
<description>Wildfire Chat Server</description>
<inceptionYear>2019</inceptionYear>
@@ -30,11 +30,8 @@
</licenses>
<modules>
<module>broker</module>
<module>wildfire_jmeter</module>
<module>client</module>
<module>common</module>
<module>sdk</module>
<module>distribution</module>
-35
View File
@@ -1,35 +0,0 @@
## Run follow command by sequence:
To build protobuf from source, the following tools are needed:
On Ubuntu, you can install them with:
$ sudo apt-get install autoconf automake libtool curl make g++ unzip
On Mac, you can install them with:
$ brew install autoconf automake libtool
Then run the cmd.
$ ./autogen.sh
$ ./configure
$ make
$ make check
$ sudo make install
$ sudo ldconfig # refresh shared library cache.
## Javascript 生成方法
1. 安装nodejs
2. 安装protobufjs
```
sudo npm install protobufjs@5 -g
```
3. 生成JS
```
pbjs -t static-module -w commonjs -o protocol.js wfcmessage.proto
```
> [参考1](http://dcode.io/protobuf.js/#pbjs-for-javascript)
> [参考2](https://blog.csdn.net/yyf1990cs/article/details/78942157)
-528
View File
@@ -1,528 +0,0 @@
2013-02-27 version 2.5.0:
General
* New notion "import public" that allows a proto file to forward the content
it imports to its importers. For example,
// foo.proto
import public "bar.proto";
import "baz.proto";
// qux.proto
import "foo.proto";
// Stuff defined in bar.proto may be used in this file, but stuff from
// baz.proto may NOT be used without importing it explicitly.
This is useful for moving proto files. To move a proto file, just leave
a single "import public" in the old proto file.
* New enum option "allow_alias" that specifies whether different symbols can
be assigned the same numeric value. Default value is "true". Setting it to
false causes the compiler to reject enum definitions where multiple symbols
have the same numeric value.
C++
* New generated method set_allocated_foo(Type* foo) for message and string
fields. This method allows you to set the field to a pre-allocated object
and the containing message takes the ownership of that object.
* Added SetAllocatedExtension() and ReleaseExtension() to extensions API.
* Custom options are now formatted correctly when descriptors are printed in
text format.
* Various speed optimizations.
Java
* Comments in proto files are now collected and put into generated code as
comments for corresponding classes and data members.
* Added Parser to parse directly into messages without a Builder. For
example,
Foo foo = Foo.getParser().ParseFrom(input);
Using Parser is ~25% faster than using Builder to parse messages.
* Added getters/setters to access the underlying ByteString of a string field
directly.
* ByteString now supports more operations: substring(), prepend(), and
append(). The implementation of ByteString uses a binary tree structure
to support these operations efficiently.
* New method findInitializationErrors() that lists all missing required
fields.
* Various code size and speed optimizations.
Python
* Added support for dynamic message creation. DescriptorDatabase,
DescriptorPool, and MessageFactory work like their C++ couterparts to
simplify Descriptor construction from *DescriptorProtos, and MessageFactory
provides a message instance from a Descriptor.
* Added pickle support for protobuf messages.
* Unknown fields are now preserved after parsing.
* Fixed bug where custom options were not correctly populated. Custom
options can be accessed now.
* Added EnumTypeWrapper that provides better accessibility to enum types.
* Added ParseMessage(descriptor, bytes) to generate a new Message instance
from a descriptor and a byte string.
2011-05-01 version 2.4.1:
C++
* Fixed the frendship problem for old compilers to make the library now gcc 3
compatible again.
* Fixed vcprojects/extract_includes.bat to extract compiler/plugin.h.
Java
* Removed usages of JDK 1.6 only features to make the library now JDK 1.5
compatible again.
* Fixed a bug about negative enum values.
* serialVersionUID is now defined in generated messages for java serializing.
* Fixed protoc to use java.lang.Object, which makes "Object" now a valid
message name again.
Python
* Experimental C++ implementation now requires C++ protobuf library installed.
See the README.txt in the python directory for details.
2011-02-02 version 2.4.0:
General
* The RPC (cc|java|py)_generic_services default value is now false instead of
true.
* Custom options can have aggregate types. For example,
message MyOption {
optional string comment = 1;
optional string author = 2;
}
extend google.protobuf.FieldOptions {
optional MyOption myoption = 12345;
}
This option can now be set as follows:
message SomeType {
optional int32 field = 1 [(myoption) = { comment:'x' author:'y' }];
}
C++
* Various speed and code size optimizations.
* Added a release_foo() method on string and message fields.
* Fixed gzip_output_stream sub-stream handling.
Java
* Builders now maintain sub-builders for sub-messages. Use getFooBuilder() to
get the builder for the sub-message "foo". This allows you to repeatedly
modify deeply-nested sub-messages without rebuilding them.
* Builder.build() no longer invalidates the Builder for generated messages
(You may continue to modify it and then build another message).
* Code generator will generate efficient equals() and hashCode()
implementations if new option java_generate_equals_and_hash is enabled.
(Otherwise, reflection-based implementations are used.)
* Generated messages now implement Serializable.
* Fields with [deprecated=true] will be marked with @Deprecated in Java.
* Added lazy conversion of UTF-8 encoded strings to String objects to improve
performance.
* Various optimizations.
* Enum value can be accessed directly, instead of calling getNumber() on the
enum member.
* For each enum value, an integer constant is also generated with the suffix
_VALUE.
Python
* Added an experimental C++ implementation for Python messages via a Python
extension. Implementation type is controlled by an environment variable
PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION (valid values: "cpp" and "python")
The default value is currently "python" but will be changed to "cpp" in
future release.
* Improved performance on message instantiation significantly.
Most of the work on message instantiation is done just once per message
class, instead of once per message instance.
* Improved performance on text message parsing.
* Allow add() to forward keyword arguments to the concrete class.
E.g. instead of
item = repeated_field.add()
item.foo = bar
item.baz = quux
You can do:
repeated_field.add(foo=bar, baz=quux)
* Added a sort() interface to the BaseContainer.
* Added an extend() method to repeated composite fields.
* Added UTF8 debug string support.
2010-01-08 version 2.3.0:
General
* Parsers for repeated numeric fields now always accept both packed and
unpacked input. The [packed=true] option only affects serializers.
Therefore, it is possible to switch a field to packed format without
breaking backwards-compatibility -- as long as all parties are using
protobuf 2.3.0 or above, at least.
* The generic RPC service code generated by the C++, Java, and Python
generators can be disabled via file options:
option cc_generic_services = false;
option java_generic_services = false;
option py_generic_services = false;
This allows plugins to generate alternative code, possibly specific to some
particular RPC implementation.
protoc
* Now supports a plugin system for code generators. Plugins can generate
code for new languages or inject additional code into the output of other
code generators. Plugins are just binaries which accept a protocol buffer
on stdin and write a protocol buffer to stdout, so they may be written in
any language. See src/google/protobuf/compiler/plugin.proto.
**WARNING**: Plugins are experimental. The interface may change in a
future version.
* If the output location ends in .zip or .jar, protoc will write its output
to a zip/jar archive instead of a directory. For example:
protoc --java_out=myproto_srcs.jar --python_out=myproto.zip myproto.proto
Currently the archive contents are not compressed, though this could change
in the future.
* inf, -inf, and nan can now be used as default values for float and double
fields.
C++
* Various speed and code size optimizations.
* DynamicMessageFactory is now fully thread-safe.
* Message::Utf8DebugString() method is like DebugString() but avoids escaping
UTF-8 bytes.
* Compiled-in message types can now contain dynamic extensions, through use
of CodedInputStream::SetExtensionRegistry().
* Now compiles shared libraries (DLLs) by default on Cygwin and MinGW, to
match other platforms. Use --disable-shared to avoid this.
Java
* parseDelimitedFrom() and mergeDelimitedFrom() now detect EOF and return
false/null instead of throwing an exception.
* Fixed some initialization ordering bugs.
* Fixes for OpenJDK 7.
Python
* 10-25 times faster than 2.2.0, still pure-Python.
* Calling a mutating method on a sub-message always instantiates the message
in its parent even if the mutating method doesn't actually mutate anything
(e.g. parsing from an empty string).
* Expanded descriptors a bit.
2009-08-11 version 2.2.0:
C++
* Lite mode: The "optimize_for = LITE_RUNTIME" option causes the compiler
to generate code which only depends libprotobuf-lite, which is much smaller
than libprotobuf but lacks descriptors, reflection, and some other features.
* Fixed bug where Message.Swap(Message) was only implemented for
optimize_for_speed. Swap now properly implemented in both modes
(Issue 91).
* Added RemoveLast and SwapElements(index1, index2) to Reflection
interface for repeated elements.
* Added Swap(Message) to Reflection interface.
* Floating-point literals in generated code that are intended to be
single-precision now explicitly have 'f' suffix to avoid pedantic warnings
produced by some compilers.
* The [deprecated=true] option now causes the C++ code generator to generate
a GCC-style deprecation annotation (no-op on other compilers).
* google::protobuf::GetEnumDescriptor<SomeGeneratedEnumType>() returns the
EnumDescriptor for that type -- useful for templates which cannot call
SomeGeneratedEnumType_descriptor().
* Various optimizations and obscure bug fixes.
Java
* Lite mode: The "optimize_for = LITE_RUNTIME" option causes the compiler
to generate code which only depends libprotobuf-lite, which is much smaller
than libprotobuf but lacks descriptors, reflection, and some other features.
* Lots of style cleanups.
Python
* Fixed endianness bug with floats and doubles.
* Text format parsing support.
* Fix bug with parsing packed repeated fields in embedded messages.
* Ability to initialize fields by passing keyword args to constructor.
* Support iterators in extend and __setslice__ for containers.
2009-05-13 version 2.1.0:
General
* Repeated fields of primitive types (types other that string, group, and
nested messages) may now use the option [packed = true] to get a more
efficient encoding. In the new encoding, the entire list is written
as a single byte blob using the "length-delimited" wire type. Within
this blob, the individual values are encoded the same way they would
be normally except without a tag before each value (thus, they are
tightly "packed").
* For each field, the generated code contains an integer constant assigned
to the field number. For example, the .proto file:
message Foo { optional int bar_baz = 123; }
would generate the following constants, all with the integer value 123:
C++: Foo::kBarBazFieldNumber
Java: Foo.BAR_BAZ_FIELD_NUMBER
Python: Foo.BAR_BAZ_FIELD_NUMBER
Constants are also generated for extensions, with the same naming scheme.
These constants may be used as switch cases.
* Updated bundled Google Test to version 1.3.0. Google Test is now bundled
in its verbatim form as a nested autoconf package, so you can drop in any
other version of Google Test if needed.
* optimize_for = SPEED is now the default, by popular demand. Use
optimize_for = CODE_SIZE if code size is more important in your app.
* It is now an error to define a default value for a repeated field.
Previously, this was silently ignored (it had no effect on the generated
code).
* Fields can now be marked deprecated like:
optional int32 foo = 1 [deprecated = true];
Currently this does not have any actual effect, but in the future the code
generators may generate deprecation annotations in each language.
* Cross-compiling should now be possible using the --with-protoc option to
configure. See README.txt for more info.
protoc
* --error_format=msvs option causes errors to be printed in Visual Studio
format, which should allow them to be clicked on in the build log to go
directly to the error location.
* The type name resolver will no longer resolve type names to fields. For
example, this now works:
message Foo {}
message Bar {
optional int32 Foo = 1;
optional Foo baz = 2;
}
Previously, the type of "baz" would resolve to "Bar.Foo", and you'd get
an error because Bar.Foo is a field, not a type. Now the type of "baz"
resolves to the message type Foo. This change is unlikely to make a
difference to anyone who follows the Protocol Buffers style guide.
C++
* Several optimizations, including but not limited to:
- Serialization, especially to flat arrays, is 10%-50% faster, possibly
more for small objects.
- Several descriptor operations which previously required locking no longer
do.
- Descriptors are now constructed lazily on first use, rather than at
process startup time. This should save memory in programs which do not
use descriptors or reflection.
- UnknownFieldSet completely redesigned to be more efficient (especially in
terms of memory usage).
- Various optimizations to reduce code size (though the serialization speed
optimizations increased code size).
* Message interface has method ParseFromBoundedZeroCopyStream() which parses
a limited number of bytes from an input stream rather than parsing until
EOF.
* GzipInputStream and GzipOutputStream support reading/writing gzip- or
zlib-compressed streams if zlib is available.
(google/protobuf/io/gzip_stream.h)
* DescriptorPool::FindAllExtensions() and corresponding
DescriptorDatabase::FindAllExtensions() can be used to enumerate all
extensions of a given type.
* For each enum type Foo, protoc will generate functions:
const string& Foo_Name(Foo value);
bool Foo_Parse(const string& name, Foo* result);
The former returns the name of the enum constant corresponding to the given
value while the latter finds the value corresponding to a name.
* RepeatedField and RepeatedPtrField now have back-insertion iterators.
* String fields now have setters that take a char* and a size, in addition
to the existing ones that took char* or const string&.
* DescriptorPool::AllowUnknownDependencies() may be used to tell
DescriptorPool to create placeholder descriptors for unknown entities
referenced in a FileDescriptorProto. This can allow you to parse a .proto
file without having access to other .proto files that it imports, for
example.
* Updated gtest to latest version. The gtest package is now included as a
nested autoconf package, so it should be able to drop new versions into the
"gtest" subdirectory without modification.
Java
* Fixed bug where Message.mergeFrom(Message) failed to merge extensions.
* Message interface has new method toBuilder() which is equivalent to
newBuilderForType().mergeFrom(this).
* All enums now implement the ProtocolMessageEnum interface.
* Setting a field to null now throws NullPointerException.
* Fixed tendency for TextFormat's parsing to overflow the stack when
parsing large string values. The underlying problem is with Java's
regex implementation (which unfortunately uses recursive backtracking
rather than building an NFA). Worked around by making use of possesive
quantifiers.
* Generated service classes now also generate pure interfaces. For a service
Foo, Foo.Interface is a pure interface containing all of the service's
defined methods. Foo.newReflectiveService() can be called to wrap an
instance of this interface in a class that implements the generic
RpcService interface, which provides reflection support that is usually
needed by RPC server implementations.
* RPC interfaces now support blocking operation in addition to non-blocking.
The protocol compiler generates separate blocking and non-blocking stubs
which operate against separate blocking and non-blocking RPC interfaces.
RPC implementations will have to implement the new interfaces in order to
support blocking mode.
* New I/O methods parseDelimitedFrom(), mergeDelimitedFrom(), and
writeDelimitedTo() read and write "delemited" messages from/to a stream,
meaning that the message size precedes the data. This way, you can write
multiple messages to a stream without having to worry about delimiting
them yourself.
* Throw a more descriptive exception when build() is double-called.
* Add a method to query whether CodedInputStream is at the end of the input
stream.
* Add a method to reset a CodedInputStream's size counter; useful when
reading many messages with the same stream.
* equals() and hashCode() now account for unknown fields.
Python
* Added slicing support for repeated scalar fields. Added slice retrieval and
removal of repeated composite fields.
* Updated RPC interfaces to allow for blocking operation. A client may
now pass None for a callback when making an RPC, in which case the
call will block until the response is received, and the response
object will be returned directly to the caller. This interface change
cannot be used in practice until RPC implementations are updated to
implement it.
* Changes to input_stream.py should make protobuf compatible with appengine.
2008-11-25 version 2.0.3:
protoc
* Enum values may now have custom options, using syntax similar to field
options.
* Fixed bug where .proto files which use custom options but don't actually
define them (i.e. they import another .proto file defining the options)
had to explicitly import descriptor.proto.
* Adjacent string literals in .proto files will now be concatenated, like in
C.
* If an input file is a Windows absolute path (e.g. "C:\foo\bar.proto") and
the import path only contains "." (or contains "." but does not contain
the file), protoc incorrectly thought that the file was under ".", because
it thought that the path was relative (since it didn't start with a slash).
This has been fixed.
C++
* Generated message classes now have a Swap() method which efficiently swaps
the contents of two objects.
* All message classes now have a SpaceUsed() method which returns an estimate
of the number of bytes of allocated memory currently owned by the object.
This is particularly useful when you are reusing a single message object
to improve performance but want to make sure it doesn't bloat up too large.
* New method Message::SerializeAsString() returns a string containing the
serialized data. May be more convenient than calling
SerializeToString(string*).
* In debug mode, log error messages when string-type fields are found to
contain bytes that are not valid UTF-8.
* Fixed bug where a message with multiple extension ranges couldn't parse
extensions.
* Fixed bug where MergeFrom(const Message&) didn't do anything if invoked on
a message that contained no fields (but possibly contained extensions).
* Fixed ShortDebugString() to not be O(n^2). Durr.
* Fixed crash in TextFormat parsing if the first token in the input caused a
tokenization error.
* Fixed obscure bugs in zero_copy_stream_impl.cc.
* Added support for HP C++ on Tru64.
* Only build tests on "make check", not "make".
* Fixed alignment issue that caused crashes when using DynamicMessage on
64-bit Sparc machines.
* Simplify template usage to work with MSVC 2003.
* Work around GCC 4.3.x x86_64 compiler bug that caused crashes on startup.
(This affected Fedora 9 in particular.)
* Now works on "Solaris 10 using recent Sun Studio".
Java
* New overload of mergeFrom() which parses a slice of a byte array instead
of the whole thing.
* New method ByteString.asReadOnlyByteBuffer() does what it sounds like.
* Improved performance of isInitialized() when optimizing for code size.
Python
* Corrected ListFields() signature in Message base class to match what
subclasses actually implement.
* Some minor refactoring.
* Don't pass self as first argument to superclass constructor (no longer
allowed in Python 2.6).
2008-09-29 version 2.0.2:
General
* License changed from Apache 2.0 to New BSD.
* It is now possible to define custom "options", which are basically
annotations which may be placed on definitions in a .proto file.
For example, you might define a field option called "foo" like so:
import "google/protobuf/descriptor.proto"
extend google.protobuf.FieldOptions {
optional string foo = 12345;
}
Then you annotate a field using the "foo" option:
message MyMessage {
optional int32 some_field = 1 [(foo) = "bar"]
}
The value of this option is then visible via the message's
Descriptor:
const FieldDescriptor* field =
MyMessage::descriptor()->FindFieldByName("some_field");
assert(field->options().GetExtension(foo) == "bar");
This feature has been implemented and tested in C++ and Java.
Other languages may or may not need to do extra work to support
custom options, depending on how they construct descriptors.
C++
* Fixed some GCC warnings that only occur when using -pedantic.
* Improved static initialization code, making ordering more
predictable among other things.
* TextFormat will no longer accept messages which contain multiple
instances of a singular field. Previously, the latter instance
would overwrite the former.
* Now works on systems that don't have hash_map.
Java
* Print @Override annotation in generated code where appropriate.
Python
* Strings now use the "unicode" type rather than the "str" type.
String fields may still be assigned ASCII "str" values; they will
automatically be converted.
* Adding a property to an object representing a repeated field now
raises an exception. For example:
# No longer works (and never should have).
message.some_repeated_field.foo = 1
Windows
* We now build static libraries rather than DLLs by default on MSVC.
See vsprojects/readme.txt for more information.
2008-08-15 version 2.0.1:
protoc
* New flags --encode and --decode can be used to convert between protobuf text
format and binary format from the command-line.
* New flag --descriptor_set_out can be used to write FileDescriptorProtos for
all parsed files directly into a single output file. This is particularly
useful if you wish to parse .proto files from programs written in languages
other than C++: just run protoc as a background process and have it output
a FileDescriptorList, then parse that natively.
* Improved error message when an enum value's name conflicts with another
symbol defined in the enum type's scope, e.g. if two enum types declared
in the same scope have values with the same name. This is disallowed for
compatibility with C++, but this wasn't clear from the error.
* Fixed absolute output paths on Windows.
* Allow trailing slashes in --proto_path mappings.
C++
* Reflection objects are now per-class rather than per-instance. To make this
possible, the Reflection interface had to be changed such that all methods
take the Message instance as a parameter. This change improves performance
significantly in memory-bandwidth-limited use cases, since it makes the
message objects smaller. Note that source-incompatible interface changes
like this will not be made again after the library leaves beta.
* Heuristically detect sub-messages when printing unknown fields.
* Fix static initialization ordering bug that caused crashes at startup when
compiling on Mac with static linking.
* Fixed TokenizerTest when compiling with -DNDEBUG on Linux.
* Fixed incorrect definition of kint32min.
* Fix bytes type setter to work with byte sequences with embedded NULLs.
* Other irrelevant tweaks.
Java
* Fixed UnknownFieldSet's parsing of varints larger than 32 bits.
* Fixed TextFormat's parsing of "inf" and "nan".
* Fixed TextFormat's parsing of comments.
* Added info to Java POM that will be required when we upload the
package to a Maven repo.
Python
* MergeFrom(message) and CopyFrom(message) are now implemented.
* SerializeToString() raises an exception if the message is missing required
fields.
* Code organization improvements.
* Fixed doc comments for RpcController and RpcChannel, which had somehow been
swapped.
* Fixed text_format_test on Windows where floating-point exponents sometimes
contain extra zeros.
* Fix Python service CallMethod() implementation.
Other
* Improved readmes.
* VIM syntax highlighting improvements.
2008-07-07 version 2.0.0:
* First public release.
-90
View File
@@ -1,90 +0,0 @@
This file contains a list of people who have made large contributions
to the public version of Protocol Buffers.
Original Protocol Buffers design and implementation:
Sanjay Ghemawat <[email protected]>
Jeff Dean <[email protected]>
Daniel Dulitz <[email protected]>
Craig Silverstein
Paul Haahr <[email protected]>
Corey Anderson <[email protected]>
(and many others)
Proto2 C++ and Java primary author:
Kenton Varda <[email protected]>
Proto2 Python primary authors:
Will Robinson <[email protected]>
Petar Petrov <[email protected]>
Large code contributions:
Jason Hsueh <[email protected]>
Joseph Schorr <[email protected]>
Wenbo Zhu <[email protected]>
Large quantity of code reviews:
Scott Bruce <[email protected]>
Frank Yellin
Neal Norwitz <[email protected]>
Jeffrey Yasskin <[email protected]>
Ambrose Feinstein <[email protected]>
Documentation:
Lisa Carey <[email protected]>
Maven packaging:
Gregory Kick <[email protected]>
Patch contributors:
Kevin Ko <[email protected]>
* Small patch to handle trailing slashes in --proto_path flag.
Johan Euphrosine <[email protected]>
* Small patch to fix Python CallMethod().
Ulrich Kunitz <[email protected]>
* Small optimizations to Python serialization.
Leandro Lucarella <[email protected]>
* VI syntax highlighting tweaks.
* Fix compiler to not make output executable.
Dilip Joseph <[email protected]>
* Heuristic detection of sub-messages when printing unknown fields in
text format.
Brian Atkinson <[email protected]>
* Added @Override annotation to generated Java code where appropriate.
Vincent Choinière <[email protected]>
* Tru64 support.
Monty Taylor <[email protected]>
* Solaris 10 + Sun Studio fixes.
Alek Storm <[email protected]>
* Slicing support for repeated scalar fields for the Python API.
Oleg Smolsky <[email protected]>
* MS Visual Studio error format option.
* Detect unordered_map in stl_hash.m4.
Brian Olson <[email protected]>
* gzip/zlib I/O support.
Michael Poole <[email protected]>
* Fixed warnings about generated constructors not explicitly initializing
all fields (only present with certain compiler settings).
* Added generation of field number constants.
Wink Saville <[email protected]>
* Fixed initialization ordering problem in logging code.
Will Pierce <[email protected]>
* Small patch improving performance of in Python serialization.
Alexandre Vassalotti <[email protected]>
* Emacs mode for Protocol Buffers (editors/protobuf-mode.el).
Scott Stafford <[email protected]>
* Added Swap(), SwapElements(), and RemoveLast() to Reflection interface.
Alexander Melnikov <[email protected]>
* HPUX support.
Oliver Jowett <[email protected]>
* Detect whether zlib is new enough in configure script.
* Fixes for Solaris 10 32/64-bit confusion.
Evan Jones <[email protected]>
* Optimize Java serialization code when writing a small message to a stream.
* Optimize Java serialization of strings so that UTF-8 encoding happens only
once per string per serialization call.
* Clean up some Java warnings.
* Fix bug with permanent callbacks that delete themselves when run.
Michael Kucharski <[email protected]>
* Added CodedInputStream.getTotalBytesRead().
Kacper Kowalik <[email protected]>
* Fixed m4/acx_pthread.m4 problem for some Linux distributions.
-33
View File
@@ -1,33 +0,0 @@
Copyright 2008, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Code generated by the Protocol Buffer compiler is owned by the owner
of the input file used when generating it. This code is not
standalone and requires a support library to be linked with it. This
support library is itself covered by the above license.
-237
View File
@@ -1,237 +0,0 @@
This file contains detailed but generic information on building and
installing the C++ part of this project. For shorter instructions,
as well as instructions for compiling and installing the Java or
Python parts, see README.
======================================================================
Copyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
Foundation, Inc.
This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.
Basic Installation
==================
These are generic installation instructions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. (Caching is
disabled by default to prevent problems with accidental use of stale
cache files.)
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You only need
`configure.ac' if you want to change it or regenerate `configure' using
a newer version of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system. If you're
using `csh' on an old version of System V, you might need to type
`sh ./configure' instead to prevent `csh' from trying to execute
`configure' itself.
Running `configure' takes awhile. While running, it prints some
messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the `configure' script does not know about. Run `./configure --help'
for details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you must use a version of `make' that
supports the `VPATH' variable, such as GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
If you have to use a `make' that does not support the `VPATH'
variable, you have to compile the package for one architecture at a
time in the source code directory. After you have installed the
package for one architecture, use `make distclean' before reconfiguring
for another architecture.
Installation Names
==================
By default, `make install' will install the package's files in
`/usr/local/bin', `/usr/local/man', etc. You can specify an
installation prefix other than `/usr/local' by giving `configure' the
option `--prefix=PATH'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
give `configure' the option `--exec-prefix=PATH', the package will use
PATH as the prefix for installing programs and libraries.
Documentation and other data files will still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=PATH' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' cannot figure out
automatically, but needs to determine by the type of machine the package
will run on. Usually, assuming the package is built to be run on the
_same_ architectures, `configure' can figure that out, but if it prints
a message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the `--target=TYPE' option to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
will cause the specified gcc to be used as the C compiler (unless it is
overridden in the site shell script).
`configure' Invocation
======================
`configure' recognizes the following options to control how it
operates.
`--help'
`-h'
Print a summary of the options to `configure', and exit.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.
-225
View File
@@ -1,225 +0,0 @@
## Process this file with automake to produce Makefile.in
ACLOCAL_AMFLAGS = -I m4
AUTOMAKE_OPTIONS = foreign
# Build . before src so that our all-local and clean-local hooks kicks in at
# the right time.
SUBDIRS = . src
# Always include gtest in distributions.
DIST_SUBDIRS = $(subdirs) src
# Build gtest before we build protobuf tests. We don't add gtest to SUBDIRS
# because then "make check" would also build and run all of gtest's own tests,
# which takes a lot of time and is generally not useful to us. Also, we don't
# want "make install" to recurse into gtest since we don't want to overwrite
# the installed version of gtest if there is one.
check-local:
@echo "Making lib/libgtest.a lib/libgtest_main.a in gtest"
@cd gtest && $(MAKE) $(AM_MAKEFLAGS) lib/libgtest.la lib/libgtest_main.la
# We would like to clean gtest when "make clean" is invoked. But we have to
# be careful because clean-local is also invoked during "make distclean", but
# "make distclean" already recurses into gtest because it's listed among the
# DIST_SUBDIRS. distclean will delete gtest/Makefile, so if we then try to
# cd to the directory again and "make clean" it will fail. So, check that the
# Makefile exists before recursing.
clean-local:
@if test -e gtest/Makefile; then \
echo "Making clean in gtest"; \
cd gtest && $(MAKE) $(AM_MAKEFLAGS) clean; \
fi
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = protobuf.pc protobuf-lite.pc
EXTRA_DIST = \
autogen.sh \
generate_descriptor_proto.sh \
README.txt \
INSTALL.txt \
COPYING.txt \
CONTRIBUTORS.txt \
CHANGES.txt \
editors/README.txt \
editors/proto.vim \
editors/protobuf-mode.el \
vsprojects/config.h \
vsprojects/extract_includes.bat \
vsprojects/libprotobuf.vcproj \
vsprojects/libprotobuf-lite.vcproj \
vsprojects/libprotoc.vcproj \
vsprojects/protobuf.sln \
vsprojects/protoc.vcproj \
vsprojects/readme.txt \
vsprojects/test_plugin.vcproj \
vsprojects/tests.vcproj \
vsprojects/lite-test.vcproj \
vsprojects/convert2008to2005.sh \
examples/README.txt \
examples/Makefile \
examples/addressbook.proto \
examples/add_person.cc \
examples/list_people.cc \
examples/AddPerson.java \
examples/ListPeople.java \
examples/add_person.py \
examples/list_people.py \
java/src/main/java/com/google/protobuf/AbstractMessage.java \
java/src/main/java/com/google/protobuf/AbstractMessageLite.java \
java/src/main/java/com/google/protobuf/AbstractParser.java \
java/src/main/java/com/google/protobuf/BlockingRpcChannel.java \
java/src/main/java/com/google/protobuf/BlockingService.java \
java/src/main/java/com/google/protobuf/BoundedByteString.java \
java/src/main/java/com/google/protobuf/ByteString.java \
java/src/main/java/com/google/protobuf/CodedInputStream.java \
java/src/main/java/com/google/protobuf/CodedOutputStream.java \
java/src/main/java/com/google/protobuf/Descriptors.java \
java/src/main/java/com/google/protobuf/DynamicMessage.java \
java/src/main/java/com/google/protobuf/ExtensionRegistry.java \
java/src/main/java/com/google/protobuf/ExtensionRegistryLite.java \
java/src/main/java/com/google/protobuf/FieldSet.java \
java/src/main/java/com/google/protobuf/GeneratedMessage.java \
java/src/main/java/com/google/protobuf/GeneratedMessageLite.java \
java/src/main/java/com/google/protobuf/Internal.java \
java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java \
java/src/main/java/com/google/protobuf/LazyField.java \
java/src/main/java/com/google/protobuf/LazyStringArrayList.java \
java/src/main/java/com/google/protobuf/LazyStringList.java \
java/src/main/java/com/google/protobuf/LiteralByteString.java \
java/src/main/java/com/google/protobuf/Message.java \
java/src/main/java/com/google/protobuf/MessageLite.java \
java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java \
java/src/main/java/com/google/protobuf/MessageOrBuilder.java \
java/src/main/java/com/google/protobuf/Parser.java \
java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java \
java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java \
java/src/main/java/com/google/protobuf/RopeByteString.java \
java/src/main/java/com/google/protobuf/RpcCallback.java \
java/src/main/java/com/google/protobuf/RpcChannel.java \
java/src/main/java/com/google/protobuf/RpcController.java \
java/src/main/java/com/google/protobuf/RpcUtil.java \
java/src/main/java/com/google/protobuf/ServiceException.java \
java/src/main/java/com/google/protobuf/Service.java \
java/src/main/java/com/google/protobuf/SingleFieldBuilder.java \
java/src/main/java/com/google/protobuf/SmallSortedMap.java \
java/src/main/java/com/google/protobuf/TextFormat.java \
java/src/main/java/com/google/protobuf/UninitializedMessageException.java \
java/src/main/java/com/google/protobuf/UnknownFieldSet.java \
java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java \
java/src/main/java/com/google/protobuf/Utf8.java \
java/src/main/java/com/google/protobuf/WireFormat.java \
java/src/test/java/com/google/protobuf/AbstractMessageTest.java \
java/src/test/java/com/google/protobuf/BoundedByteStringTest.java \
java/src/test/java/com/google/protobuf/ByteStringTest.java \
java/src/test/java/com/google/protobuf/CodedInputStreamTest.java \
java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java \
java/src/test/java/com/google/protobuf/DeprecatedFieldTest.java \
java/src/test/java/com/google/protobuf/DescriptorsTest.java \
java/src/test/java/com/google/protobuf/DynamicMessageTest.java \
java/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java \
java/src/test/java/com/google/protobuf/GeneratedMessageTest.java \
java/src/test/java/com/google/protobuf/IsValidUtf8Test.java \
java/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java \
java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java \
java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java \
java/src/test/java/com/google/protobuf/LiteralByteStringTest.java \
java/src/test/java/com/google/protobuf/LiteTest.java \
java/src/test/java/com/google/protobuf/MessageTest.java \
java/src/test/java/com/google/protobuf/NestedBuildersTest.java \
java/src/test/java/com/google/protobuf/ParserTest.java \
java/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java \
java/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java \
java/src/test/java/com/google/protobuf/RopeByteStringTest.java \
java/src/test/java/com/google/protobuf/ServiceTest.java \
java/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java \
java/src/test/java/com/google/protobuf/SmallSortedMapTest.java \
java/src/test/java/com/google/protobuf/TestBadIdentifiers.java \
java/src/test/java/com/google/protobuf/TestUtil.java \
java/src/test/java/com/google/protobuf/TextFormatTest.java \
java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java \
java/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java \
java/src/test/java/com/google/protobuf/WireFormatTest.java \
java/src/test/java/com/google/protobuf/multiple_files_test.proto \
java/src/test/java/com/google/protobuf/nested_builders_test.proto \
java/src/test/java/com/google/protobuf/nested_extension_lite.proto \
java/src/test/java/com/google/protobuf/nested_extension.proto \
java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto \
java/src/test/java/com/google/protobuf/non_nested_extension.proto \
java/src/test/java/com/google/protobuf/test_bad_identifiers.proto \
java/pom.xml \
java/README.txt \
python/google/protobuf/internal/generator_test.py \
python/google/protobuf/internal/containers.py \
python/google/protobuf/internal/decoder.py \
python/google/protobuf/internal/descriptor_database_test.py \
python/google/protobuf/internal/descriptor_pool_test.py \
python/google/protobuf/internal/descriptor_test.py \
python/google/protobuf/internal/encoder.py \
python/google/protobuf/internal/enum_type_wrapper.py \
python/google/protobuf/internal/factory_test1.proto \
python/google/protobuf/internal/factory_test2.proto \
python/google/protobuf/internal/message_cpp_test.py \
python/google/protobuf/internal/message_factory_test.py \
python/google/protobuf/internal/message_listener.py \
python/google/protobuf/internal/message_test.py \
python/google/protobuf/internal/more_extensions.proto \
python/google/protobuf/internal/more_extensions_dynamic.proto \
python/google/protobuf/internal/more_messages.proto \
python/google/protobuf/internal/python_message.py \
python/google/protobuf/internal/cpp_message.py \
python/google/protobuf/internal/api_implementation.py \
python/google/protobuf/internal/reflection_test.py \
python/google/protobuf/internal/reflection_cpp_generated_test.py \
python/google/protobuf/internal/service_reflection_test.py \
python/google/protobuf/internal/test_bad_identifiers.proto \
python/google/protobuf/internal/test_util.py \
python/google/protobuf/internal/text_format_test.py \
python/google/protobuf/internal/type_checkers.py \
python/google/protobuf/internal/unknown_fields_test.py \
python/google/protobuf/internal/wire_format.py \
python/google/protobuf/internal/wire_format_test.py \
python/google/protobuf/internal/__init__.py \
python/google/protobuf/pyext/python-proto2.cc \
python/google/protobuf/pyext/python_descriptor.cc \
python/google/protobuf/pyext/python_descriptor.h \
python/google/protobuf/pyext/python_protobuf.cc \
python/google/protobuf/pyext/python_protobuf.h \
python/google/protobuf/descriptor.py \
python/google/protobuf/descriptor_database.py \
python/google/protobuf/descriptor_pool.py \
python/google/protobuf/message.py \
python/google/protobuf/message_factory.py \
python/google/protobuf/reflection.py \
python/google/protobuf/service.py \
python/google/protobuf/service_reflection.py \
python/google/protobuf/text_format.py \
python/google/protobuf/__init__.py \
python/google/__init__.py \
python/ez_setup.py \
python/setup.py \
python/mox.py \
python/stubout.py \
python/README.txt
# Deletes all the files generated by autogen.sh.
MAINTAINERCLEANFILES = \
aclocal.m4 \
config.guess \
config.sub \
configure \
depcomp \
install-sh \
ltmain.sh \
Makefile.in \
missing \
mkinstalldirs \
config.h.in \
stamp.h.in \
m4/ltsugar.m4 \
m4/libtool.m4 \
m4/ltversion.m4 \
m4/lt~obsolete.m4 \
m4/ltoptions.m4
File diff suppressed because it is too large Load Diff
-152
View File
@@ -1,152 +0,0 @@
Protocol Buffers - Google's data interchange format
Copyright 2008 Google Inc.
http://code.google.com/apis/protocolbuffers/
C++ Installation - Unix
=======================
To build and install the C++ Protocol Buffer runtime and the Protocol
Buffer compiler (protoc) execute the following:
$ ./configure
$ make
$ make check
$ make install
If "make check" fails, you can still install, but it is likely that
some features of this library will not work correctly on your system.
Proceed at your own risk.
"make install" may require superuser privileges.
For advanced usage information on configure and make, see INSTALL.txt.
** Hint on install location **
By default, the package will be installed to /usr/local. However,
on many platforms, /usr/local/lib is not part of LD_LIBRARY_PATH.
You can add it, but it may be easier to just install to /usr
instead. To do this, invoke configure as follows:
./configure --prefix=/usr
If you already built the package with a different prefix, make sure
to run "make clean" before building again.
** Compiling dependent packages **
To compile a package that uses Protocol Buffers, you need to pass
various flags to your compiler and linker. As of version 2.2.0,
Protocol Buffers integrates with pkg-config to manage this. If you
have pkg-config installed, then you can invoke it to get a list of
flags like so:
pkg-config --cflags protobuf # print compiler flags
pkg-config --libs protobuf # print linker flags
pkg-config --cflags --libs protobuf # print both
For example:
c++ my_program.cc my_proto.pb.cc `pkg-config --cflags --libs protobuf`
Note that packages written prior to the 2.2.0 release of Protocol
Buffers may not yet integrate with pkg-config to get flags, and may
not pass the correct set of flags to correctly link against
libprotobuf. If the package in question uses autoconf, you can
often fix the problem by invoking its configure script like:
configure CXXFLAGS="$(pkg-config --cflags protobuf)" \
LIBS="$(pkg-config --libs protobuf)"
This will force it to use the correct flags.
If you are writing an autoconf-based package that uses Protocol
Buffers, you should probably use the PKG_CHECK_MODULES macro in your
configure script like:
PKG_CHECK_MODULES([protobuf], [protobuf])
See the pkg-config man page for more info.
If you only want protobuf-lite, substitute "protobuf-lite" in place
of "protobuf" in these examples.
** Note for cross-compiling **
The makefiles normally invoke the protoc executable that they just
built in order to build tests. When cross-compiling, the protoc
executable may not be executable on the host machine. In this case,
you must build a copy of protoc for the host machine first, then use
the --with-protoc option to tell configure to use it instead. For
example:
./configure --with-protoc=protoc
This will use the installed protoc (found in your $PATH) instead of
trying to execute the one built during the build process. You can
also use an executable that hasn't been installed. For example, if
you built the protobuf package for your host machine in ../host,
you might do:
./configure --with-protoc=../host/src/protoc
Either way, you must make sure that the protoc executable you use
has the same version as the protobuf source code you are trying to
use it with.
** Note for Solaris users **
Solaris 10 x86 has a bug that will make linking fail, complaining
about libstdc++.la being invalid. We have included a work-around
in this package. To use the work-around, run configure as follows:
./configure LDFLAGS=-L$PWD/src/solaris
See src/solaris/libstdc++.la for more info on this bug.
** Note for HP C++ Tru64 users **
To compile invoke configure as follows:
./configure CXXFLAGS="-O -std ansi -ieee -D__USE_STD_IOSTREAM"
Also, you will need to use gmake instead of make.
C++ Installation - Windows
==========================
If you are using Microsoft Visual C++, see vsprojects/readme.txt.
If you are using Cygwin or MinGW, follow the Unix installation
instructions, above.
Binary Compatibility Warning
============================
Due to the nature of C++, it is unlikely that any two versions of the
Protocol Buffers C++ runtime libraries will have compatible ABIs.
That is, if you linked an executable against an older version of
libprotobuf, it is unlikely to work with a newer version without
re-compiling. This problem, when it occurs, will normally be detected
immediately on startup of your app. Still, you may want to consider
using static linkage. You can configure this package to install
static libraries only using:
./configure --disable-shared
Java and Python Installation
============================
The Java and Python runtime libraries for Protocol Buffers are located
in the java and python directories. See the README file in each
directory for more information on how to compile and install them.
Note that both of them require you to first install the Protocol
Buffer compiler (protoc), which is part of the C++ package.
Usage
=====
The complete documentation for Protocol Buffers is available via the
web at:
http://code.google.com/apis/protocolbuffers/
-1020
View File
File diff suppressed because it is too large Load Diff
-41
View File
@@ -1,41 +0,0 @@
#!/bin/sh
# Run this script to generate the configure script and other files that will
# be included in the distribution. These files are not checked in because they
# are automatically generated.
set -e
# Check that we're being run from the right directory.
if test ! -f src/google/protobuf/stubs/common.h; then
cat >&2 << __EOF__
Could not find source code. Make sure you are running this script from the
root of the distribution tree.
__EOF__
exit 1
fi
# Check that gtest is present. Usually it is already there since the
# directory is set up as an SVN external.
if test ! -e gtest; then
echo "Google Test not present. Fetching gtest-1.5.0 from the web..."
curl http://googletest.googlecode.com/files/gtest-1.5.0.tar.bz2 | tar jx
mv gtest-1.5.0 gtest
fi
set -ex
# Temporary hack: Must change C runtime library to "multi-threaded DLL",
# otherwise it will be set to "multi-threaded static" when MSVC upgrades
# the project file to MSVC 2005/2008. vladl of Google Test says gtest will
# probably change their default to match, then this will be unnecessary.
# One of these mappings converts the debug configuration and the other
# converts the release configuration. I don't know which is which.
sed -i -e 's/RuntimeLibrary="5"/RuntimeLibrary="3"/g;
s/RuntimeLibrary="4"/RuntimeLibrary="2"/g;' gtest/msvc/*.vcproj
# TODO(kenton): Remove the ",no-obsolete" part and fix the resulting warnings.
autoreconf -f -i -Wall,no-obsolete
rm -rf autom4te.cache config.h.in~
exit 0
File diff suppressed because it is too large Load Diff

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