Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
579d3b529f | ||
|
|
065a79adc3 | ||
|
|
5ed45ef414 | ||
|
|
ba671b3370 | ||
|
|
e975f20a6b | ||
|
|
70bdebd4a0 | ||
|
|
409078801e | ||
|
|
06fed7de52 | ||
|
|
da2428a9f9 | ||
|
|
83b6ae5229 | ||
|
|
4b7804cdfd | ||
|
|
d075b5212f | ||
|
|
636c8ce219 | ||
|
|
091d7fa3ec | ||
|
|
59356e5b52 | ||
|
|
882bf29e63 | ||
|
|
f3eef4dd6b | ||
|
|
898983c6e5 | ||
|
|
dae5d032fa | ||
|
|
ff76622796 | ||
|
|
a6fd11a243 | ||
|
|
cf983be44c | ||
|
|
1a72de0e85 | ||
|
|
bf70a1e80e | ||
|
|
512d797d4d | ||
|
|
4a46dd1dde | ||
|
|
7494cdc071 | ||
|
|
608184ef9e | ||
|
|
eb4d8a9b68 | ||
|
|
6b92ae383c | ||
|
|
5fee857c4a | ||
|
|
53b7139c30 | ||
|
|
83e864d667 | ||
|
|
b88297e6de | ||
|
|
4aa2f46011 | ||
|
|
1fb3c757e2 | ||
|
|
3f640b6c14 | ||
|
|
49eb57af00 | ||
|
|
6bd6a80e71 | ||
|
|
38b84f0fa5 | ||
|
|
1f46debd0c | ||
|
|
37998e76d1 | ||
|
|
80b5508533 | ||
|
|
a646cc8227 | ||
|
|
92692adf52 | ||
|
|
b2cfc2b99b | ||
|
|
86e08f6151 | ||
|
|
8d852eb2d1 | ||
|
|
8bef018911 | ||
|
|
5e13419c28 | ||
|
|
503a7ae18f |
@@ -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
|
||||
|
||||
@@ -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. 微信交流群(请先加我们好友,我们再邀请您进群)
|
||||
|
||||
 
|
||||
|
||||
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% />
|
||||
|
||||
|
||||
@@ -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
@@ -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', '最大群成员数量');
|
||||
@@ -0,0 +1 @@
|
||||
alter table `t_sensitiveword` add column `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
alter table `t_user_session` modify column `_voip_token` varchar(240) DEFAULT '';
|
||||
|
||||
|
||||
+4038
File diff suppressed because it is too large
Load Diff
+13
@@ -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", "最大群成员数量");
|
||||
@@ -0,0 +1 @@
|
||||
alter table `t_sensitiveword` add column `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT;
|
||||
@@ -0,0 +1,3 @@
|
||||
alter table `t_user_session` modify column `_voip_token` varchar(240) DEFAULT '';
|
||||
|
||||
|
||||
+8
-26
@@ -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;
|
||||
|
||||
+94
@@ -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;
|
||||
}
|
||||
}
|
||||
+61
@@ -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;
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
|
||||
import cn.wildfirechat.common.APIPath;
|
||||
import cn.wildfirechat.common.ErrorCode;
|
||||
import cn.wildfirechat.pojos.InputAddGroupMember;
|
||||
import cn.wildfirechat.pojos.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;
|
||||
}
|
||||
}
|
||||
+90
@@ -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;
|
||||
}
|
||||
}
|
||||
+64
@@ -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";
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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
@@ -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
@@ -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%"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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/
|
||||
Vendored
-1020
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
Vendored
-1530
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
Reference in New Issue
Block a user