wildfire chat server source initial submit

This commit is contained in:
heavyrain2012
2019-03-16 17:04:23 +08:00
parent e59217bd10
commit cab3ca0916
1466 changed files with 528862 additions and 15919 deletions
+253
View File
@@ -0,0 +1,253 @@
/*
* 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 win.liyufan.im;
import java.beans.PropertyVetoException;
import java.io.BufferedReader;
import java.io.FileReader;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.hazelcast.util.StringUtil;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import io.moquette.BrokerConstants;
import io.moquette.server.config.IConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.PreparedStatement;
import java.sql.Connection;
import java.util.concurrent.ConcurrentHashMap;
public class DBUtil {
private static final Logger LOG = LoggerFactory.getLogger(DBUtil.class);
private static ComboPooledDataSource comboPooledDataSource = null;
private static ConcurrentHashMap<Long, String>map = new ConcurrentHashMap<>();
private static ThreadLocal<Connection> transactionConnection = new ThreadLocal<Connection>() {
@Override
protected Connection initialValue() {
super.initialValue();
return null;
}
};
public static boolean IsEmbedDB = false;
public static void init(IConfig config) {
String embedDB = config.getProperty(BrokerConstants.EMBED_DB_PROPERTY_NAME);
if (embedDB != null && embedDB.equals("1")) {
IsEmbedDB = true;
LOG.info("Use h2 database");
} else {
IsEmbedDB = false;
LOG.info("Use mysql database");
}
if (comboPooledDataSource == null) {
if (IsEmbedDB) {
comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setJdbcUrl( "jdbc:h2:./h2db/wfchat;AUTO_SERVER=TRUE;MODE=MySQL" );
comboPooledDataSource.setUser("SA");
comboPooledDataSource.setPassword("SA");
comboPooledDataSource.setMinPoolSize(5);
comboPooledDataSource.setAcquireIncrement(5);
comboPooledDataSource.setMaxPoolSize(20);
comboPooledDataSource.setIdleConnectionTestPeriod(60 * 5);
comboPooledDataSource.setMinPoolSize(3);
comboPooledDataSource.setInitialPoolSize(3);
try {
comboPooledDataSource.setDriverClass( "org.h2.Driver" ); //loads the jdbc driver
} catch (PropertyVetoException e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
}
ResultSet rs = null;
Connection connection = null;
try {
connection = comboPooledDataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
}
Statement stmt = null;
try {
connection = DBUtil.getConnection();
rs = connection.getMetaData().getTables(null, null, "t_user_setting", null );
if (!rs.next()) {
List<String> creatsqls = getCreateSql();
for (String createsql : creatsqls) {
if (StringUtil.isNullOrEmpty(createsql)) {
break;
}
stmt = connection.createStatement();
if (0 != stmt.executeLargeUpdate(createsql)) {
LOG.error("创建表失败! {}", createsql);
// System.exit(-1);
}
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
}
}
}
} catch (SQLException e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
} finally {
DBUtil.closeDB(connection, null, rs);
}
} else {
comboPooledDataSource = new ComboPooledDataSource("mysql");
}
}
}
private static List<String> getCreateSql() {
List<String> out = new ArrayList<>();
try{
BufferedReader br = new BufferedReader(new FileReader("h2/create_table.sql"));//构造一个BufferedReader类来读取文件
String s = null;
StringBuilder result = new StringBuilder();
while((s = br.readLine())!=null) {
result.append(s);
if (s.contains(";")) {
out.add(result.toString());
result = new StringBuilder();
}
}
br.close();
}catch(Exception e){
e.printStackTrace();
Utility.printExecption(LOG, e);
}
return out;
}
//从数据源中获取数据库的连接
public static Connection getConnection() throws SQLException {
long threadId = Thread.currentThread().getId();
if (map.get(threadId) != null) {
LOG.error("error here!!!! DB connection not close correctly");
}
map.put(threadId, Thread.currentThread().getStackTrace().toString());
Connection connection = transactionConnection.get();
if (connection != null) {
LOG.debug("Thread {} get db connection {}", threadId, connection);
return connection;
}
connection = comboPooledDataSource.getConnection();
LOG.debug("Thread {} get db connection {}", threadId, connection);
return connection;
}
public static void beginTransaction() {
try {
Connection connection = getConnection();
connection.setAutoCommit(false);
transactionConnection.set(connection);
} catch (SQLException e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
}
}
public static void commit() {
try {
Connection connection = transactionConnection.get();
if (connection != null) {
connection.commit();
connection.setAutoCommit(true);
transactionConnection.remove();
}
} catch (SQLException e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
}
}
public static void roolback() {
try {
Connection connection = transactionConnection.get();
if (connection != null) {
connection.rollback();
connection.setAutoCommit(true);
transactionConnection.remove();
};
} catch (SQLException e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
}
}
//释放资源,将数据库连接还给数据库连接池
public static void closeDB(Connection conn,PreparedStatement ps,ResultSet rs) {
LOG.debug("Thread {} release db connection {}", Thread.currentThread().getId(), conn);
try {
if (rs!=null) {
rs.close();
}
} catch (Exception e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
}
try {
if (ps!=null) {
ps.close();
}
} catch (Exception e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
}
try {
if (conn!=null && transactionConnection.get() != conn) {
conn.close();
map.remove(Thread.currentThread().getId());
}
} catch (Exception e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
}
}
//释放资源,将数据库连接还给数据库连接池
public static void closeDB(Connection conn, PreparedStatement ps) {
LOG.debug("Thread {} release db connection {}", Thread.currentThread().getId(), conn);
try {
if (ps!=null) {
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
}
try {
if (conn!=null && transactionConnection.get() != conn) {
conn.close();
map.remove(Thread.currentThread().getId());
}
} catch (SQLException e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
}
}
}
+84
View File
@@ -0,0 +1,84 @@
/*
* 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 win.liyufan.im;
public enum ErrorCode {
//General error
ERROR_CODE_SUCCESS(0, "success"),
ERROR_CODE_SECRECT_KEY_MISMATCH(1, "secrect key mismatch"),
ERROR_CODE_INVALID_DATA(2, "invalid data"),
ERROR_CODE_NODE_NOT_EXIST(3, "node not exist"),
ERROR_CODE_SERVER_ERROR(4, "server error"),
ERROR_CODE_NOT_MODIFIED(5, "not modified"),
//Auth error
ERROR_CODE_TOKEN_ERROR(6, "token error"),
ERROR_CODE_USER_FORBIDDEN(8, "user forbidden"),
//Message error
ERROR_CODE_NOT_IN_GROUP(9, "not in group"),
ERROR_CODE_INVALID_MESSAGE(10, "invalid message"),
//Group error
ERROR_CODE_GROUP_ALREADY_EXIST(11, "group aleady exist"),
//user error
ERROR_CODE_PASSWORD_INCORRECT(15, "password incorrect"),
//user error
ERROR_CODE_FRIEND_ALREADY_REQUEST(16, "already send request"),
ERROR_CODE_FRIEND_REQUEST_BLOCKED(18, "friend request blocked"),
ERROR_CODE_FRIEND_REQUEST_OVERTIME(19, "friend request overtime"),
ERROR_CODE_NOT_IN_CHATROOM(20, "not in chatroom"),
ERROR_CODE_NOT_IN_CHANNEL(21, "not in channel"),
ERROR_CODE_SENSITIVE_MATCHED(242, "sensitive matched"),
ERROR_CODE_SIGN_EXPIRED(243, "sign expired"),
ERROR_CODE_AUTH_FAILURE(244, "auth failure"),
ERROR_CODE_CLIENT_COUNT_OUT_OF_LIMIT(245, "client count out of limit"),
ERROR_CODE_IN_BLACK_LIST(246, "user in balck list"),
ERROR_CODE_FORBIDDEN_SEND_MSG(247, "forbidden send msg globally"),
ERROR_CODE_NOT_RIGHT(248, "no right to operate"),
ERROR_CODE_TIMEOUT(249, "timeout"),
ERROR_CODE_OVER_FREQUENCY(250, "over frequency"),
INVALID_PARAMETER(251, "Invalid parameter"),
ERROR_CODE_NOT_EXIST(253, "not exist"),
ERROR_CODE_NOT_IMPLEMENT(254, "not implement"),
ERROR_CODE_ASYNC_HANDLER(255, "异步执行,服务器内部逻辑需要此代码,为正常情况,不能返回客户端"),;
public int code;
public String msg;
ErrorCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
public static ErrorCode fromCode(int code) {
for (ErrorCode errorCode : ErrorCode.values()) {
if(errorCode.code == code) {
return errorCode;
}
}
return ERROR_CODE_SERVER_ERROR;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
@@ -0,0 +1,140 @@
package win.liyufan.im;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 频率限制计数器,由于线程并发问题,并不一定严格准确,但总体可用
*/
public class FrequencyLimistCounter {
private final Map<String, Integer> CACHE_MAP = new HashMap<>();
private final Map<String, Long> CACHE_TIME_MAP = new HashMap<>();
private long CACHE_HOLD_TIME = 5 * 1000;
private int FREQUENCY_LIMIT = 100;
private final SpinLock mLock = new SpinLock();
private long lastFullScanTime = System.currentTimeMillis();
//2个小时全部扫描一次
private static final long fullScanDuration = 2*60*60*1000;
public FrequencyLimistCounter(int limitTimeSec, int limitCount) {
this.CACHE_HOLD_TIME = limitTimeSec * 1000;
this.FREQUENCY_LIMIT = limitCount;
}
public FrequencyLimistCounter() {
}
/**
* 增加对象计数并返回是否超频
* @param cacheName
* @return ture 超频; false 不超频
*/
public boolean increaseAndCheck(String cacheName) {
mLock.lock();
try {
tryFullScan();
Long cacheHoldTime = CACHE_TIME_MAP.get(cacheName);
if (cacheHoldTime != null && cacheHoldTime < System.currentTimeMillis()) {
CACHE_MAP.remove(cacheName);
CACHE_TIME_MAP.remove(cacheName);
}
Integer count = CACHE_MAP.get(cacheName);
if (count != null) {
CACHE_MAP.put(cacheName, ++count);
} else {
count = 0;
CACHE_MAP.put(cacheName, ++count);
CACHE_TIME_MAP.put(cacheName, (System.currentTimeMillis() + CACHE_HOLD_TIME));//缓存失效时间
}
if (count >= FREQUENCY_LIMIT) {
return true;
}
return false;
} finally {
mLock.unLock();
}
}
//去掉超时的数据,如果在一个低频使用的系统中,会残留大量超时无用的信息
private void tryFullScan() {
if (System.currentTimeMillis() - lastFullScanTime > fullScanDuration) {
lastFullScanTime = System.currentTimeMillis();
for (Map.Entry<String, Long> entry : CACHE_TIME_MAP.entrySet()
) {
if (entry.getValue() < lastFullScanTime) {
CACHE_MAP.remove(entry.getKey());
CACHE_TIME_MAP.remove(entry.getKey());
}
}
}
}
public static void main(String[] args) {
FrequencyLimistCounter counter = new FrequencyLimistCounter(5, 100);
String name = "test";
for (int i = 0; i < 50; i++) {
if(counter.increaseAndCheck(name)) {
System.out.println("test failure 1");
System.exit(-1);
}
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 49; i++) {
if(counter.increaseAndCheck(name)) {
System.out.println("test failure 2");
System.exit(-1);
}
}
if(!counter.increaseAndCheck(name)) {
System.out.println("test failure 3");
System.exit(-1);
}
try {
Thread.sleep(5*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 99; i++) {
if(counter.increaseAndCheck(name)) {
System.out.println("test failure 4");
System.exit(-1);
}
}
try {
Thread.sleep(5*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 99; i++) {
if(counter.increaseAndCheck(name)) {
System.out.println("test failure 5");
System.exit(-1);
}
}
}
}
@@ -0,0 +1,71 @@
/*
* 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 win.liyufan.im;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
public class GitRepositoryState {
public final String tags;
public final String branch;
public final String dirty;
public final String remoteOriginUrl;
public final String commitId;
public final String commitIdAbbrev;
public final String describe;
public final String describeShort;
public final String commitUserName;
public final String commitUserEmail;
public final String commitMessageFull;
public final String commitMessageShort;
public final String commitTime;
public final String closestTagName;
public final String closestTagCommitCount;
public final String buildUserName;
public final String buildUserEmail;
public final String buildTime;
public final String buildHost;
public final String buildVersion;
private GitRepositoryState(Properties properties) {
this.tags = String.valueOf(properties.get("git.tags"));
this.branch = String.valueOf(properties.get("git.branch"));
this.dirty = String.valueOf(properties.get("git.dirty"));
this.remoteOriginUrl = String.valueOf(properties.get("git.remote.origin.url"));
this.commitId = String.valueOf(properties.get("git.commit.id.full")); // OR properties.get("git.commit.id") depending on your configuration
this.commitIdAbbrev = String.valueOf(properties.get("git.commit.id.abbrev"));
this.describe = String.valueOf(properties.get("git.commit.id.describe"));
this.describeShort = String.valueOf(properties.get("git.commit.id.describe-short"));
this.commitUserName = String.valueOf(properties.get("git.commit.user.name"));
this.commitUserEmail = String.valueOf(properties.get("git.commit.user.email"));
this.commitMessageFull = String.valueOf(properties.get("git.commit.message.full"));
this.commitMessageShort = String.valueOf(properties.get("git.commit.message.short"));
this.commitTime = String.valueOf(properties.get("git.commit.time"));
this.closestTagName = String.valueOf(properties.get("git.closest.tag.name"));
this.closestTagCommitCount = String.valueOf(properties.get("git.closest.tag.commit.count"));
this.buildUserName = String.valueOf(properties.get("git.build.user.name"));
this.buildUserEmail = String.valueOf(properties.get("git.build.user.email"));
this.buildTime = String.valueOf(properties.get("git.build.time"));
this.buildHost = String.valueOf(properties.get("git.build.host"));
this.buildVersion = String.valueOf(properties.get("git.build.version"));
}
public static GitRepositoryState getGitRepositoryState() throws IOException {
Properties properties = new Properties();
properties.load(new FileInputStream("config/git.properties"));
// properties.load(GitRepositoryState.class.getClassLoader().getResourceAsStream("config/git.properties"));
GitRepositoryState gitRepositoryState = new GitRepositoryState(properties);
return gitRepositoryState;
}
}
@@ -0,0 +1,92 @@
/*
* 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 win.liyufan.im;
import io.netty.util.internal.StringUtil;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.params.CoreConnectionPNames;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
public class HttpUtils {
private static final Logger LOG = LoggerFactory.getLogger(HttpUtils.class);
public static boolean httpJsonPost(String url, String jsonStr){
boolean isSuccess = false;
LOG.info("POST to {} with data {}", url, jsonStr);
if (StringUtil.isNullOrEmpty(url)) {
LOG.error("http post failure with empty url");
return false;
}
HttpPost post = null;
try {
HttpClient httpClient = HttpClientBuilder.create().build();
// // 设置超时时间
// httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 2000);
// httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 2000);
post = new HttpPost(url);
// 构造消息头
post.setHeader("Content-type", "application/json; charset=utf-8");
post.setHeader("Connection", "Keep-Alive");
// 构建消息实体
StringEntity entity = new StringEntity(jsonStr, Charset.forName("UTF-8"));
entity.setContentEncoding("UTF-8");
// 发送Json格式的数据请求
entity.setContentType("application/json");
post.setEntity(entity);
HttpResponse response = httpClient.execute(post);
// 检验返回码
int statusCode = response.getStatusLine().getStatusCode();
if(statusCode != HttpStatus.SC_OK){
LOG.info("请求出错: "+statusCode);
isSuccess = false;
}else{
BufferedReader in = new BufferedReader(new InputStreamReader(response.getEntity()
.getContent(),"utf-8"));
StringBuffer sb = new StringBuffer("");
String line = "";
String NL = System.getProperty("line.separator");
while ((line = in.readLine()) != null) {
sb.append(line + NL);
}
in.close();
String content = sb.toString();
LOG.info("http request response content: {}", content);
}
} catch (Exception e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
isSuccess = false;
}finally{
if(post != null){
post.releaseConnection();
}
}
return isSuccess;
}
}
@@ -0,0 +1,114 @@
/*
* 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 win.liyufan.im;
public class IDUtils {
private static char getChar(int number) {
if (number < 0 || number >= 64) {
number = number % 64;
}
if (number < 26) {
return (char)('A' + number);
} else if(number < 52) {
return (char)('a' + number - 26);
} else if(number < 62) {
return (char)('0' + number - 52);
} else if(number == 62) {
return '_';
}
return '-';
}
private static int getNumber(char ch) {
if (ch >= 'A' && ch <= 'Z') {
return ch - 'A';
} else if(ch >= 'a' && ch <= 'z') {
return ch - 'a' + 26;
} else if(ch >= '0' && ch <= '9') {
return ch - '0' + 52;
} else if(ch == '_') {
return 62;
}
return 63;
}
public static String toUid(int id) {
StringBuilder sb = new StringBuilder();
int rand0 = 0;
int rand3 = 0;
int rand6 = 0;
for (int i = 0; i < 8; i++) {
if (i == 0 || i == 3 || i == 6) {
int rand = (int)(Math.random()*64+0.5);
sb.append(getChar(rand));
if (i == 0) {
rand0 = rand;
} else if (i == 3) {
rand3 = rand;
} else {
rand6 = rand;
}
} else {
if (i < 5) {
sb.append(getChar((id + rand0) % 64));
} else if(i == 5) {
sb.append(getChar((id + rand3) % 64));
} else {
sb.append(getChar((id + rand6) % 64));
}
id = id / 64;
}
}
return sb.toString();
}
public static int fromUid(String uid) {
int id = 0;
int rand0 = 0;
int rand3 = 0;
int rand6 = 0;
for (int i = 0; i < 8; i++) {
if (i == 0 || i == 3 || i == 6) {
int rand = getNumber(uid.charAt(i));
if (i == 0) {
rand0 = rand;
} else if (i == 3) {
rand3 = rand;
} else {
rand6 = rand;
}
} else {
int c = getNumber(uid.charAt(i));
if (i < 5) {
c = (c+64-rand0)%64;
} else if(i == 5) {
c = (c+64-rand3)%64;
} else {
c = (c+64-rand6)%64;
}
if (i < 3)
id = id + (c << (i-1)*6);
else if(i < 6)
id = id + (c << (i-2)*6);
else
id = id + (c << (i-3)*6);
}
}
return id;
}
public static void main(String[] args) {
for (int i = 0; i < 100000000; i++) {
String uid = toUid(i);
System.out.println(i + ", " + uid + ", " + fromUid(uid));
}
}
}
+70
View File
@@ -0,0 +1,70 @@
/*
* 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 win.liyufan.im;
public interface IMTopic {
String SendMessageTopic = "MS";
String RecallMessageTopic = "MR";
String PullMessageTopic = "MP";
String NotifyMessageTopic = "MN";
String NotifyRecallMessageTopic = "RMN";
String GetUserSettingTopic = "UG";
String PutUserSettingTopic = "UP";
String NotifyUserSettingTopic = "UN";
String CreateGroupTopic = "GC";
String AddGroupMemberTopic = "GAM";
String KickoffGroupMemberTopic = "GKM";
String QuitGroupTopic = "GQ";
String DismissGroupTopic = "GD";
String ModifyGroupInfoTopic = "GMI";
String ModifyGroupAliasTopic = "GMA";
String GetGroupInfoTopic = "GPGI";
String GetGroupMemberTopic = "GPGM";
String TransferGroupTopic = "GTG";
String GetUserInfoTopic = "UPUI";
String ModifyMyInfoTopic = "MMI";
String GetQiniuUploadTokenTopic = "GQNUT";
String AddFriendRequestTopic = "FAR";
String HandleFriendRequestTopic = "FHR";
String FriendRequestPullTopic = "FRP";
String NotifyFriendRequestTopic = "FRN";
String RriendRequestUnreadSyncTopic = "FRUS";
String DeleteFriendTopic = "FDL";
String FriendPullTopic = "FP";
String NotifyFriendTopic = "FN";
String BlackListUserTopic = "BLU";
String UploadDeviceTokenTopic = "UDT";
String UserSearchTopic = "US";
String JoinChatroomTopic = "CRJ";
String QuitChatroomTopic = "CRQ";
String GetChatroomInfoTopic = "CRI";
String GetChatroomMemberTopic = "CRMI";
String RouteTopic = "ROUTE";
String CreateChannelTopic = "CHC";
String ModifyChannelInfoTopic = "CHMI";
String TransferChannelInfoTopic = "CHT";
String DestoryChannelInfoTopic = "CHD";
String ChannelSearchTopic = "CHS";
String ChannelListenTopic = "CHL";
String ChannelPullTopic = "CHP";
String GetTokenTopic = "GETTOKEN";
}
+70
View File
@@ -0,0 +1,70 @@
/*
* 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 win.liyufan.im;
import cn.wildfirechat.proto.WFCMessage;
import java.io.Serializable;
public class MessageBundle implements Serializable {
/**
*
*/
private static final long serialVersionUID = -8959293027687263752L;
private String fromUser;
private String fromClientId;
private int type;
private int line;
private String targetId;
private WFCMessage.Message message;
private long messageId;
public MessageBundle() {
}
public MessageBundle(long messageId, String fromUser, String fromClientId, WFCMessage.Message message) {
super();
this.fromUser = fromUser;
this.fromClientId = fromClientId;
this.type = message.getConversation().getType();
this.targetId = message.getConversation().getTarget();
this.line = message.getConversation().getLine();
this.message = message;
this.messageId = messageId;
}
public int getLine() {
return line;
}
public long getMessageId() {
return messageId;
}
public String getFromUser() {
return fromUser;
}
public String getFromClientId() {
return fromClientId;
}
public int getType() {
return type;
}
public String getTargetId() {
return targetId;
}
public WFCMessage.Message getMessage() {
return message;
}
public void setMessage(WFCMessage.Message message) {
this.message = message;
}
}
@@ -0,0 +1,63 @@
/*
* 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 win.liyufan.im;
import java.util.Date;
public class MessageShardingUtil {
private static SpinLock mLock = new SpinLock();
private static int rotateId = 0;
private static int nodeId = 0;
private static int rotateIdWidth = 15;
private static int rotateIdMask = 0x7FFF;
private static int nodeIdWidth = 6;
private static int nodeIdMask = 0x3F;
private static final long T201801010000 = 1514736000000L;
public static void setNodeId(int nodeId) {
MessageShardingUtil.nodeId = nodeId;
}
/**
* ID = timestamp(43) + nodeId(6) + rotateId(15)
* 所以时间限制是到2157/5/15(2的42次幂代表的时间 + (2018-1970))。节点数限制是小于64,每台服务器每毫秒最多发送32768条消息
* @return
*/
public static long generateId() {
mLock.lock();
rotateId = (rotateId + 1)&rotateIdMask;
mLock.unLock();
long id = System.currentTimeMillis() - T201801010000;
id <<= nodeIdWidth;
id += (nodeId & nodeIdMask);
id <<= rotateIdWidth;
id += rotateId;
return id;
}
public static String getMessageTable(long mid) {
if (DBUtil.IsEmbedDB) {
return "t_messages";
}
mid >>= (nodeIdWidth + rotateIdWidth);
Date date = new Date(mid + T201801010000);
int month = date.getMonth();
int year = date.getYear();
year %= 3;
return "t_messages_" + (year * 12 + month);
}
}
+21
View File
@@ -0,0 +1,21 @@
/*
* 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 win.liyufan.im;
public interface MyInfoType {
int Modify_DisplayName = 0;
int Modify_Portrait = 1;
int Modify_Gender = 2;
int Modify_Mobile = 3;
int Modify_Email = 4;
int Modify_Address = 5;
int Modify_Company = 6;
int Modify_Social = 7;
int Modify_Extra = 8;
}
@@ -0,0 +1,119 @@
package win.liyufan.im;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* 漏桶算法
* capacity * 1000是为了更精确, 漏水的小洞更小^~^
*/
public class RateLimiter {
private static final int DEFAULT_LIMIT_TIME_SECOND = 5;
private static final int DEFAULT_LIMIT_COUNT = 100;
private static final long expire = 2 * 60 * 60 * 1000;
private double rate = (double) DEFAULT_LIMIT_COUNT / (DEFAULT_LIMIT_TIME_SECOND);
private long capacity = DEFAULT_LIMIT_COUNT * 1000;
private long lastCleanTime;
private Map<String, Long> requestCountMap = new HashMap<>();
private Map<String, Long> requestTimeMap = new HashMap<>();
private SpinLock lock = new SpinLock();
public RateLimiter() {
}
public RateLimiter(int limitTimeSecond, int limitCount) {
if (limitTimeSecond <= 0 || limitCount <= 0) {
throw new IllegalArgumentException();
}
this.capacity = limitCount * 1000;
this.rate = (double) limitCount / limitTimeSecond;
}
/**
* 漏桶算法,https://en.wikipedia.org/wiki/Leaky_bucket
*/
public boolean isGranted(String userId) {
try {
lock.lock();
long current = System.currentTimeMillis();
cleanUp(current);
Long lastRequestTime = requestTimeMap.get(userId);
long count = 0;
if (lastRequestTime == null) {
count += 1000;
requestTimeMap.put(userId, current);
requestCountMap.put(userId, count);
return true;
} else {
count = requestCountMap.get(userId);
lastRequestTime = requestTimeMap.get(userId);
count -= (current - lastRequestTime) * rate;
count = count > 0 ? count : 0;
requestTimeMap.put(userId, current);
if (count < capacity) {
count += 1000;
requestCountMap.put(userId, count);
return true;
} else {
requestCountMap.put(userId, count);
return false;
}
}
} finally {
lock.unLock();
}
}
private void cleanUp(long current) {
if (current - lastCleanTime > expire) {
for (Iterator<Map.Entry<String, Long>> it = requestTimeMap.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, Long> entry = it.next();
if (entry.getValue() < current - expire) {
it.remove();
requestCountMap.remove(entry.getKey());
}
}
lastCleanTime = current;
}
}
public static void main(String[] args) throws InterruptedException {
RateLimiter limiter = new RateLimiter(1, 10);
long start = System.currentTimeMillis();
for (int i = 0; i < 53; i++) {
if (!limiter.isGranted("test")) {
System.out.println("1 too frequency " + i);
}
}
Thread.sleep(1 * 1000);
System.out.println("sleep 1 s");
for (int i = 0; i < 53; i++) {
if (!limiter.isGranted("test")) {
System.out.println("2 too frequency " + i);
}
}
Thread.sleep(5 * 1000);
System.out.println("sleep 5 s");
for (int i = 0; i < 53; i++) {
if (!limiter.isGranted("test")) {
System.out.println("3 too frequency " + i);
}
}
Thread.sleep(5 * 1000);
System.out.println("sleep 5 s");
long second = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
if (!limiter.isGranted("test")) {
System.out.println("4 too frequency " + i);
}
Thread.sleep(50);
}
System.out.println("second: " + (System.currentTimeMillis() - second));
System.out.println("end: " + (System.currentTimeMillis() - start));
}
}
@@ -0,0 +1,146 @@
package win.liyufan.im;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class SensitiveFilter {
public enum MatchType {
MIN_MATCH("最小匹配规则"),MAX_MATCH("最大匹配规则");
String desc;
MatchType(String desc) {
this.desc = desc;
}
}
private Map<Object,Object> sensitiveWordsMap;
private static final String END_FLAG="end";
public static void main(String[] args) {
Set<String> sensitiveWords=new HashSet<>();
sensitiveWords.add("你是傻逼");
sensitiveWords.add("你是傻逼啊");
sensitiveWords.add("你是坏蛋");
sensitiveWords.add("你个大笨蛋");
sensitiveWords.add("我去年买了个表");
sensitiveWords.add("shit");
SensitiveFilter textFilter=new SensitiveFilter(sensitiveWords);
String text="你你你你是傻逼啊你,说你呢,你个大笨蛋。";
System.out.println(textFilter.getSensitiveWords(text,MatchType.MAX_MATCH));
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(textFilter.getSensitiveWords(text,MatchType.MAX_MATCH));
}
}).start();
}
try {
Thread.sleep(1000 * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public boolean isContainSensitiveWord(String text){
Set<String> sensitiveWords = getSensitiveWords(text, MatchType.MIN_MATCH);
if(sensitiveWords!=null&&sensitiveWords.size()>0){
return true;
}
return false;
}
public Set<String> getSensitiveWords(String text,MatchType matchType){
Set<String> sensitiveWords=new HashSet<>();
if(text==null||text.trim().length()==0){
return sensitiveWords;
}
for(int i=0;i<text.length();i++){
int sensitiveWordLength = getSensitiveWordLength(text, i, matchType);
if(sensitiveWordLength>0){
String sensitiveWord = text.substring(i, i + sensitiveWordLength);
sensitiveWords.add(sensitiveWord);
if(matchType==MatchType.MIN_MATCH){
break;
}
i=i+sensitiveWordLength-1;
}
}
return sensitiveWords;
}
public int getSensitiveWordLength(String text,int startIndex,MatchType matchType){
if(text==null||text.trim().length()==0){
throw new IllegalArgumentException("The input text must not be empty.");
}
char currentChar;
Map<Object,Object> currentMap=sensitiveWordsMap;
int wordLength=0;
boolean endFlag=false;
for(int i=startIndex;i<text.length();i++){
currentChar=text.charAt(i);
Map<Object,Object> subMap=(Map<Object,Object>) currentMap.get(currentChar);
if(subMap==null){
break;
}else {
wordLength++;
if(subMap.containsKey(END_FLAG)){
endFlag=true;
if(matchType==MatchType.MIN_MATCH){
break;
}else {
currentMap=subMap;
}
}else {
currentMap=subMap;
}
}
}
if(!endFlag){
wordLength=0;
}
return wordLength;
}
public SensitiveFilter(Set<String> sensitiveWords){
sensitiveWordsMap=new ConcurrentHashMap<>(sensitiveWords.size());
if(sensitiveWords==null||sensitiveWords.isEmpty()){
//throw new IllegalArgumentException("Senditive words must not be empty!");
return;
}
String currentWord;
Map<Object,Object> currentMap;
Map<Object,Object> subMap;
Iterator<String> iterator = sensitiveWords.iterator();
while (iterator.hasNext()){
currentWord=iterator.next();
if(currentWord==null||currentWord.trim().length()<2){ //敏感词长度必须大于等于2
continue;
}
currentMap=sensitiveWordsMap;
for(int i=0;i<currentWord.length();i++){
char c=currentWord.charAt(i);
subMap=(Map<Object, Object>) currentMap.get(c);
if(subMap==null){
subMap=new HashMap<>();
currentMap.put(c,subMap);
currentMap=subMap;
}else {
currentMap= subMap;
}
if(i==currentWord.length()-1){
//如果是最后一个字符,则put一个结束标志,这里只需要保存key就行了,value为null可以节省空间。
//如果不是最后一个字符,则不需要存这个结束标志,同样也是为了节省空间。
currentMap.put(END_FLAG,null);
}
}
}
}
}
@@ -0,0 +1,21 @@
package win.liyufan.im;
import java.util.concurrent.atomic.AtomicReference;
public class SpinLock {
//java中原子(CAS)操作
AtomicReference<Thread> owner = new AtomicReference<>();//持有自旋锁的线程对象
private int count;
public void lock() {
Thread cur = Thread.currentThread();
//lock函数将owner设置为当前线程,并且预测原来的值为空。unlock函数将owner设置为null,并且预测值为当前线程。当有第二个线程调用lock操作时由于owner值不为空,导致循环
//一直被执行,直至第一个线程调用unlock函数将owner设置为null,第二个线程才能进入临界区。
while (!owner.compareAndSet(null, cur)){
}
}
public void unLock() {
Thread cur = Thread.currentThread();
owner.compareAndSet(cur, null);
}
}
+22
View File
@@ -0,0 +1,22 @@
/*
* 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 win.liyufan.im;
import java.util.UUID;
public class UUIDGenerator {
public static String getUUID() {
UUID uuid = UUID.randomUUID();
String str = uuid.toString();
str = str.replace("-", "");
return str;
}
}
@@ -0,0 +1,16 @@
package win.liyufan.im;
public interface UserSettingScope {
int kUserSettingConversationSilent = 1;
int kUserSettingGlobalSilent = 2;
int kUserSettingConversationTop = 3;
int kUserSettingHiddenNotificationDetail = 4;
int kUserSettinGroupHideNickname = 5;
int kUserSettingFavouriteGroup = 6;
int kUserSettingConversationSync = 7;
int kUserSettingMyChannels = 8;
int kUserSettingListenedChannels = 9;
int kUserSettingCustomBegin = 1000;
}
@@ -0,0 +1,62 @@
/*
* 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 win.liyufan.im;
import org.slf4j.Logger;
import java.net.*;
import java.util.Enumeration;
public class Utility {
public static InetAddress getLocalAddress(){
try {
Enumeration<NetworkInterface> b = NetworkInterface.getNetworkInterfaces();
while( b.hasMoreElements()){
for ( InterfaceAddress f : b.nextElement().getInterfaceAddresses())
if ( f.getAddress().isSiteLocalAddress())
return f.getAddress();
}
} catch (SocketException e) {
e.printStackTrace();
}
return null;
}
public static void printExecption(Logger LOG, Exception e) {
String message = "";
for(StackTraceElement stackTraceElement : e.getStackTrace()) {
message = message + System.lineSeparator() + stackTraceElement.toString();
}
LOG.error("Exception: {}", e.getMessage());
LOG.error(message);
}
public static String getMacAddress() throws UnknownHostException,
SocketException {
InetAddress ipAddress = InetAddress.getLocalHost();
NetworkInterface networkInterface = NetworkInterface
.getByInetAddress(ipAddress);
byte[] macAddressBytes = networkInterface.getHardwareAddress();
StringBuilder macAddressBuilder = new StringBuilder();
for (int macAddressByteIndex = 0; macAddressByteIndex < macAddressBytes.length; macAddressByteIndex++) {
String macAddressHexByte = String.format("%02X",
macAddressBytes[macAddressByteIndex]);
macAddressBuilder.append(macAddressHexByte);
if (macAddressByteIndex != macAddressBytes.length - 1)
{
macAddressBuilder.append(":");
}
}
return macAddressBuilder.toString();
}
}
@@ -0,0 +1,17 @@
package win.liyufan.im.extended.mqttmessage;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.mqtt.MqttFixedHeader;
import io.netty.handler.codec.mqtt.MqttMessage;
import io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader;
public class ModifiedMqttPubAckMessage extends MqttMessage {
public ModifiedMqttPubAckMessage(MqttFixedHeader mqttFixedHeader, MqttMessageIdVariableHeader variableHeader, ByteBuf payload) {
super(mqttFixedHeader, variableHeader, payload);
}
@Override
public MqttMessageIdVariableHeader variableHeader() {
return (MqttMessageIdVariableHeader) super.variableHeader();
}
}