增加jim-client及demo项目

This commit is contained in:
王超 2020-05-04 20:48:34 +08:00
parent cc13b48ac2
commit 6c329ec736
75 changed files with 1313 additions and 582 deletions

6
jim-client-demo/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.settings/
/target/
.idea/
*.iml
*.classpath
*.project

31
jim-client-demo/pom.xml Normal file
View File

@ -0,0 +1,31 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>jim-client-demo</artifactId>
<packaging>jar</packaging>
<name>jim-client-demo</name>
<parent>
<groupId>org.j-im</groupId>
<artifactId>jim</artifactId>
<version>3.0.0.v20200101-RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.j-im</groupId>
<artifactId>jim-client</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin-version}</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,61 @@
package org.jim.client;
import org.jim.client.config.ImClientConfig;
import org.jim.core.ImConst;
import org.jim.core.packets.ChatBody;
import org.jim.core.packets.ChatType;
import org.jim.core.packets.Command;
import org.jim.core.packets.LoginReqBody;
import org.jim.core.tcp.TcpPacket;
import org.tio.core.Node;
/**
*
* 版本: [1.0]
* 功能说明:
* 作者: WChao 创建时间: 2017年8月30日 下午1:05:17
*/
public class HelloClientStarter{
public static ImClientChannelContext imClientChannelContext = null;
/**
* 启动程序入口
*/
public static void main(String[] args) throws Exception {
//服务器节点
Node serverNode = new Node("localhost", ImConst.SERVER_PORT);
//构建客户端配置信息
ImClientConfig imClientConfig = ImClientConfig.newBuilder()
//客户端业务回调器,不可以为NULL
.clientHandler(new HelloImClientHandler())
//客户端事件监听器可以为null但建议自己实现该接口
.clientListener(new HelloImClientListener())
//心跳时长不设置就不发送心跳包
//.heartbeatTimeout(5000)
//断链后自动连接的不想自动连接请设为null
//.reConnConf(new ReconnConf(5000L))
.build();
//生成客户端对象;
JimClient jimClient = new JimClient(imClientConfig);
//连接服务端
imClientChannelContext = jimClient.connect(serverNode);
//连上后发条消息玩玩
send();
}
private static void send() throws Exception {
byte[] loginBody = new LoginReqBody("hello_client","123").toByte();
TcpPacket loginPacket = new TcpPacket(Command.COMMAND_LOGIN_REQ,loginBody);
//先登录;
JimClientAPI.send(imClientChannelContext, loginPacket);
ChatBody chatBody = ChatBody.newBuilder()
.from("hello_client")
.to("admin")
.msgType(0)
.chatType(ChatType.CHAT_TYPE_PUBLIC.getNumber())
.groupId("100")
.content("Socket普通客户端消息测试!").build();
TcpPacket chatPacket = new TcpPacket(Command.COMMAND_CHAT_REQ,chatBody.toByte());
JimClientAPI.send(imClientChannelContext, chatPacket);
}
}

View File

@ -0,0 +1,77 @@
package org.jim.client;
import java.nio.ByteBuffer;
import org.jim.client.handler.ImClientHandler;
import org.jim.core.ImChannelContext;
import org.jim.core.ImConst;
import org.jim.core.ImPacket;
import org.jim.core.config.ImConfig;
import org.jim.core.exception.ImDecodeException;
import org.jim.core.packets.Command;
import org.jim.core.tcp.TcpPacket;
import org.jim.core.tcp.TcpServerDecoder;
import org.jim.core.tcp.TcpServerEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* 版本: [1.0]
* 功能说明:
* 作者: WChao 创建时间: 2017年8月30日 下午1:10:28
*/
public class HelloImClientHandler implements ImClientHandler, ImConst
{
private static Logger logger = LoggerFactory.getLogger(HelloImClientHandler.class);
/**
* 处理消息
*/
@Override
public void handler(ImPacket imPacket, ImChannelContext channelContext){
TcpPacket helloPacket = (TcpPacket)imPacket;
byte[] body = helloPacket.getBody();
if (body != null)
{
try {
String str = new String(body, ImConst.CHARSET);
logger.info("demo客户端收到消息:{}", str);
}catch (Exception e){
logger.error(e.getMessage(), e);
}
}
return;
}
/**
* 编码把业务消息包编码为可以发送的ByteBuffer
* 总的消息结构消息头 + 消息体
* 消息头结构 4个字节存储消息体的长度
* 消息体结构 对象的json串的byte[]
*/
@Override
public ByteBuffer encode(ImPacket imPacket, ImConfig imConfig, ImChannelContext imChannelContext)
{
TcpPacket tcpPacket = (TcpPacket)imPacket;
return TcpServerEncoder.encode(tcpPacket, imConfig, imChannelContext);
}
@Override
public TcpPacket decode(ByteBuffer buffer, int limit, int position, int readableLength, ImChannelContext imChannelContext) throws ImDecodeException {
TcpPacket tcpPacket = TcpServerDecoder.decode(buffer, imChannelContext);
return tcpPacket;
}
private static TcpPacket heartbeatPacket = new TcpPacket(Command.COMMAND_HEARTBEAT_REQ,new byte[]{Protocol.HEARTBEAT_BYTE});
/**
* 此方法如果返回null框架层面则不会发心跳如果返回非null框架层面会定时发本方法返回的消息包
*/
@Override
public TcpPacket heartbeatPacket(ImChannelContext imChannelContext)
{
return heartbeatPacket;
}
}

View File

@ -0,0 +1,43 @@
package org.jim.client;
import org.jim.client.listener.ImClientListener;
import org.jim.core.ImChannelContext;
import org.jim.core.ImPacket;
/**
* @author WChao
* @Desc
* @date 2020-05-04 07:33
*/
public class HelloImClientListener implements ImClientListener {
@Override
public void onAfterConnected(ImChannelContext imChannelContext, boolean isConnected, boolean isReconnect) throws Exception {
}
@Override
public void onAfterDecoded(ImChannelContext imChannelContext, ImPacket packet, int packetSize) throws Exception {
}
@Override
public void onAfterReceivedBytes(ImChannelContext imChannelContext, int receivedBytes) throws Exception {
}
@Override
public void onAfterSent(ImChannelContext imChannelContext, ImPacket packet, boolean isSentSuccess) throws Exception {
}
@Override
public void onAfterHandled(ImChannelContext imChannelContext, ImPacket packet, long cost) throws Exception {
}
@Override
public void onBeforeClose(ImChannelContext imChannelContext, Throwable throwable, String remark, boolean isRemove) throws Exception {
}
}

View File

@ -0,0 +1,16 @@
#http://logback.qos.ch/manual/configuration.html
#<include resource="includedConfig.xml"/> resource, file, url (\u88AB\u5305\u542B\u7684\u6587\u4EF6\u9700\u8981\u6EE1\u8DB3\u4E00\u5B9A\u683C\u5F0F)
context.name=jim-client
log.dir=logs
rolling.policy.file.name.pattern=yyyy-MM-dd
max.file.size=100MB
max.history=50
conversion.pattern=%d %-5level %logger{30}[%line]: %m%n
root.level=info

View File

@ -0,0 +1,90 @@
<configuration scan="true" scanPeriod="10 seconds" debug="true">
<property resource="logback.properties" />
<contextName>${context.name}</contextName> <!-- 本项目的名字 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${conversion.pattern}</pattern>
</encoder>
</appender>
<!-- root file 日志 -->
<appender name="root-file-error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.dir}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.dir}/error.%d{${rolling.policy.file.name.pattern}}.%i.log.zip</fileNamePattern>
<maxFileSize>${max.file.size}</maxFileSize>
<maxHistory>${max.history}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${conversion.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="root-file-warn"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.dir}/warn.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.dir}/warn.%d{${rolling.policy.file.name.pattern}}.%i.log.zip</fileNamePattern>
<maxFileSize>${max.file.size}</maxFileSize>
<maxHistory>${max.history}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${conversion.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="root-file-info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.dir}/info.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.dir}/info.%d{${rolling.policy.file.name.pattern}}.%i.log.zip</fileNamePattern>
<maxFileSize>${max.file.size}</maxFileSize>
<maxHistory>${max.history}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${conversion.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="root-file-debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.dir}/debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.dir}/debug.%d{${rolling.policy.file.name.pattern}}.%i.log.zip</fileNamePattern>
<maxFileSize>${max.file.size}</maxFileSize>
<maxHistory>${max.history}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${conversion.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>debug</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<root level="${root.level}">
<appender-ref ref="console" />
<appender-ref ref="root-file-error"/>
<appender-ref ref="root-file-warn"/>
<appender-ref ref="root-file-info"/>
<appender-ref ref="root-file-debug"/>
</root>
</configuration>

View File

@ -23,17 +23,17 @@ public class SslDemoStarter {
NioSslClient client = new NioSslClient("TLSv1.2", "localhost", 9222,"./src/main/resources/keystore.jks","214323428310224");
client.connect();
byte[] loginBody = new LoginReqBody("hello_client","123").toByte();
byte[] loginBody = new LoginReqBody("ssl_hello_client","123").toByte();
TcpPacket loginPacket = new TcpPacket(Command.COMMAND_LOGIN_REQ,loginBody);
ByteBuffer loginByteBuffer = TcpServerEncoder.encode(loginPacket, null, null);
client.write(loginByteBuffer.array());
ChatBody chatBody = ChatBody.newBuilder()
.setFrom("hello_client")
.setTo("admin")
.setMsgType(0)
.setChatType(1)
.setGroup_id("100")
.setContent("Socket普通客户端消息测试!").build();
.from("ssl_hello_client")
.to("admin")
.msgType(0)
.chatType(1)
.groupId("100")
.content("SSL-Socket普通客户端消息测试!").build();
TcpPacket chatPacket = new TcpPacket(Command.COMMAND_CHAT_REQ,chatBody.toByte());
ByteBuffer chatByteBuffer = TcpServerEncoder.encode(chatPacket,null, null);
client.write(chatByteBuffer.array());

View File

@ -11,7 +11,7 @@
<dependencies>
<dependency>
<groupId>org.j-im</groupId>
<artifactId>jim-common</artifactId>
<artifactId>jim-core</artifactId>
</dependency>
</dependencies>
<build>
@ -19,7 +19,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<version>${maven-compiler-plugin-version}</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>

View File

@ -1,69 +0,0 @@
package org.jim.client;
import java.nio.ByteBuffer;
import org.jim.core.ImConst;
import org.tio.client.intf.ClientAioHandler;
import org.tio.core.ChannelContext;
import org.tio.core.GroupContext;
import org.tio.core.exception.ImDecodeException;
import org.tio.core.intf.AioHandler;
import org.tio.core.intf.Packet;
import org.jim.core.packets.Command;
import org.jim.core.tcp.TcpPacket;
import org.jim.core.tcp.TcpServerDecoder;
import org.jim.core.tcp.TcpServerEncoder;
/**
*
* 版本: [1.0]
* 功能说明:
* 作者: WChao 创建时间: 2017年8月30日 下午1:10:28
*/
public class HelloClientAioHandler implements AioHandler,ClientAioHandler
{
/**
* 处理消息
*/
@Override
public void handler(Packet packet, ChannelContext channelContext) throws Exception
{
TcpPacket helloPacket = (TcpPacket)packet;
byte[] body = helloPacket.getBody();
if (body != null)
{
String str = new String(body, ImConst.CHARSET);
System.out.println("收到消息:" + str);
}
return;
}
/**
* 编码把业务消息包编码为可以发送的ByteBuffer
* 总的消息结构消息头 + 消息体
* 消息头结构 4个字节存储消息体的长度
* 消息体结构 对象的json串的byte[]
*/
@Override
public ByteBuffer encode(Packet packet, GroupContext groupContext, ChannelContext channelContext)
{
TcpPacket tcpPacket = (TcpPacket)packet;
return TcpServerEncoder.encode(tcpPacket, groupContext, channelContext);
}
@Override
public TcpPacket decode(ByteBuffer buffer,int limit, int position, int readableLength,ChannelContext channelContext) throws ImDecodeException {
TcpPacket tcpPacket = TcpServerDecoder.decode(buffer, channelContext);
return tcpPacket;
}
private static TcpPacket heartbeatPacket = new TcpPacket(Command.COMMAND_HEARTBEAT_REQ,new byte[]{Protocol.HEARTBEAT_BYTE});
/**
* 此方法如果返回null框架层面则不会发心跳如果返回非null框架层面会定时发本方法返回的消息包
*/
@Override
public TcpPacket heartbeatPacket()
{
return heartbeatPacket;
}
}

View File

@ -1,77 +0,0 @@
package org.jim.client;
import org.jim.core.ImConst;
import org.jim.core.Jim;
import org.jim.core.packets.ChatBody;
import org.jim.core.packets.Command;
import org.jim.core.packets.LoginReqBody;
import org.jim.core.tcp.TcpPacket;
import org.tio.client.AioClient;
import org.tio.client.ClientChannelContext;
import org.tio.client.ClientGroupContext;
import org.tio.client.ReconnConf;
import org.tio.client.intf.ClientAioHandler;
import org.tio.client.intf.ClientAioListener;
import org.tio.core.Node;
/**
*
* 版本: [1.0]
* 功能说明:
* 作者: WChao 创建时间: 2017年8月30日 下午1:05:17
*/
public class HelloClientStarter {
/**
* 服务器节点
*/
public static Node serverNode = new Node("localhost", ImConst.SERVER_PORT);
/**
* handler, 包括编码解码消息处理
*/
public static ClientAioHandler aioClientHandler = new HelloClientAioHandler();
/**
* 事件监听器可以为null但建议自己实现该接口可以参考showcase了解些接口
*/
public static ClientAioListener aioListener = null;
/**
* 断链后自动连接的不想自动连接请设为null
*/
private static ReconnConf reconnConf = new ReconnConf(5000L);
/**
* 一组连接共用的上下文对象
*/
public static ClientGroupContext clientGroupContext = new ClientGroupContext(aioClientHandler, aioListener, reconnConf);
public static AioClient aioClient = null;
public static ClientChannelContext clientChannelContext = null;
/**
* 启动程序入口
*/
public static void main(String[] args) throws Exception {
//clientGroupContext.setHeartbeatTimeout(org.tio.examples.helloworld.common.Const.TIMEOUT);
clientGroupContext.setHeartbeatTimeout(0);
aioClient = new AioClient(clientGroupContext);
clientChannelContext = aioClient.connect(serverNode);
//连上后发条消息玩玩
send();
}
private static void send() throws Exception {
byte[] loginBody = new LoginReqBody("hello_client","123").toByte();
TcpPacket loginPacket = new TcpPacket(Command.COMMAND_LOGIN_REQ,loginBody);
//先登录;
Jim.send(clientChannelContext, loginPacket);
ChatBody chatBody = ChatBody.newBuilder()
.setFrom("hello_client")
.setTo("admin")
.setMsgType(0)
.setChatType(1)
.setGroup_id("100")
.setContent("Socket普通客户端消息测试!").build();
TcpPacket chatPacket = new TcpPacket(Command.COMMAND_CHAT_REQ,chatBody.toByte());
Jim.send(clientChannelContext, chatPacket);
}
}

View File

@ -0,0 +1,20 @@
package org.jim.client;
import org.jim.core.ImChannelContext;
import org.jim.core.config.ImConfig;
import org.tio.core.ChannelContext;
/**
* @ClassName ImClientChannelContext
* @Description 客户端通道上下文
* @Author WChao
* @Date 2020/1/5 23:56
* @Version 1.0
**/
public class ImClientChannelContext extends ImChannelContext {
public ImClientChannelContext(ImConfig imConfig, ChannelContext tioChannelContext) {
super(imConfig, tioChannelContext);
}
}

View File

@ -1,25 +0,0 @@
package org.jim.client;
import org.jim.core.ImHandler;
import org.jim.core.config.ImConfig;
import org.jim.core.listener.ImListener;
/**
* @ClassName ImClientConfig
* @Description TODO
* @Author WChao
* @Date 2020/1/4 10:42
* @Version 1.0
**/
public class ImClientConfig extends ImConfig {
@Override
public ImHandler getImHandler() {
return null;
}
@Override
public ImListener getImListener() {
return null;
}
}

View File

@ -0,0 +1,51 @@
/**
*
*/
package org.jim.client;
import org.jim.client.config.ImClientConfig;
import org.jim.core.ImConst;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.client.ClientChannelContext;
import org.tio.client.ClientTioConfig;
import org.tio.client.TioClient;
import org.tio.core.Node;
import java.util.Objects;
/**
* J-IM客户端连接类
* @author WChao
*
*/
public class JimClient {
private static Logger log = LoggerFactory.getLogger(JimClient.class);
private TioClient tioClient = null;
private ImClientConfig imClientConfig;
public JimClient(ImClientConfig imClientConfig){
this.imClientConfig = imClientConfig;
}
public ImClientChannelContext connect(Node serverNode) throws Exception {
return connect(serverNode, null);
}
public ImClientChannelContext connect(Node serverNode, Integer timeout) throws Exception {
log.warn("J-IM client connect");
tioClient = new TioClient((ClientTioConfig) imClientConfig.getTioConfig());
ClientChannelContext clientChannelContext = tioClient.connect(serverNode, imClientConfig.getBindIp(), imClientConfig.getBindPort(), timeout);
if(Objects.nonNull(clientChannelContext)){
log.warn("J-IM client connected success at serverAddress:[{}], bind localAddress:[{}]", serverNode.toString(), imClientConfig.toBindAddressString());
return (ImClientChannelContext)clientChannelContext.get(ImConst.Key.IM_CHANNEL_CONTEXT_KEY);
}
return null;
}
public void stop(){
tioClient.stop();
}
}

View File

@ -0,0 +1,63 @@
package org.jim.client;
import org.jim.core.ImChannelContext;
import org.jim.core.ImConst;
import org.jim.core.ImPacket;
import org.jim.core.config.ImConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.Tio;
/**
* 版本: [1.0]
* 功能说明: JIM
* @author : WChao 创建时间: 2017年9月22日 上午9:07:18
*/
public class JimClientAPI implements ImConst{
public static ImConfig imConfig = ImConfig.Global.get();
private static Logger log = LoggerFactory.getLogger(JimClientAPI.class);
/**
* 功能描述发送到群组
* @param groupId 群组ID
* @param packet 消息包
*/
public static void sendToGroup(String groupId, ImPacket packet){
Tio.sendToGroup(imConfig.getTioConfig(), groupId, packet);
}
/**
* 发送到指定通道;
* @param imChannelContext IM通道上下文
* @param imPacket 消息包
*/
public static boolean send(ImChannelContext imChannelContext, ImPacket imPacket){
if(imChannelContext == null){
return false;
}
return Tio.send(imChannelContext.getTioChannelContext(),imPacket);
}
/**
* 阻塞发送确认把packet发送到对端后再返回
* @param imChannelContext IM通道上下文
* @param packet 消息包
* @return
*/
public static boolean bSend(ImChannelContext imChannelContext , ImPacket packet){
if(imChannelContext == null){
return false;
}
return Tio.bSend(imChannelContext.getTioChannelContext(), packet);
}
/**
* 关闭连接
* @param imChannelContext
* @param remark
*/
public static void close(ImChannelContext imChannelContext, String remark){
Tio.close(imChannelContext.getTioChannelContext(), remark);
}
}

View File

@ -0,0 +1,137 @@
package org.jim.client.config;
import org.jim.client.handler.DefaultImClientHandler;
import org.jim.client.handler.ImClientHandler;
import org.jim.client.handler.ImClientHandlerAdapter;
import org.jim.client.listener.DefaultImClientListener;
import org.jim.client.listener.ImClientListener;
import org.jim.client.listener.ImClientListenerAdapter;
import org.jim.core.ImHandler;
import org.jim.core.config.ImConfig;
import org.jim.core.listener.ImListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.client.ClientTioConfig;
import org.tio.client.ReconnConf;
import java.util.Objects;
/**
* @ClassName ImServerConfig
* @Description Im客户端配置
* @Author WChao
* @Date 2020/1/4 10:40
* @Version 1.0
**/
public class ImClientConfig extends ImConfig {
private static Logger log = LoggerFactory.getLogger(ImClientConfig.class);
/**
* 客户端消息处理器
*/
private ImClientHandler imClientHandler;
/**
* 客户端消息监听器
*/
private ImClientListener imClientListener;
/**
* 重连配置
*/
protected ReconnConf reconnConf;
private ImClientConfig(ImClientHandler imClientHandler, ImClientListener imClientListener){
setImClientHandler(imClientHandler);
setImClientListener(imClientListener);
this.tioConfig = new ClientTioConfig(new ImClientHandlerAdapter(this.imClientHandler), new ImClientListenerAdapter(this.imClientListener), reconnConf);
Global.set(this);
}
public static Builder newBuilder(){
return new Builder();
}
@Override
public ImHandler getImHandler() {
return getImClientHandler();
}
@Override
public ImListener getImListener() {
return getImClientListener();
}
public static class Builder extends ImConfig.Builder<ImClientConfig, Builder>{
private ImClientListener imClientListener;
private ImClientHandler imClientHandler;
protected ReconnConf reconnConf;
@Override
protected Builder getThis() {
return this;
}
public Builder clientListener(ImClientListener imClientListener){
this.imClientListener = imClientListener;
return getThis();
}
public Builder clientHandler(ImClientHandler imClientHandler){
this.imClientHandler = imClientHandler;
return getThis();
}
public Builder reConnConf(ReconnConf reconnConf){
this.reconnConf = reconnConf;
return getThis();
}
@Override
public ImClientConfig build(){
ImClientConfig imClientConfig = new ImClientConfig(this.imClientHandler, this.imClientListener);
imClientConfig.setBindIp(this.bindIp);
imClientConfig.setBindPort(this.bindPort);
imClientConfig.setReadBufferSize(this.readBufferSize);
imClientConfig.setSslConfig(this.sslConfig);
imClientConfig.setReConnConf(this.reconnConf);
imClientConfig.setHeartbeatTimeout(this.heartbeatTimeout);
imClientConfig.setImGroupListener(this.imGroupListener);
imClientConfig.setImUserListener(this.imUserListener);
return imClientConfig;
}
}
public ImClientHandler getImClientHandler() {
return imClientHandler;
}
public void setImClientHandler(ImClientHandler imClientHandler) {
this.imClientHandler = imClientHandler;
if(Objects.isNull(this.imClientHandler)){
this.imClientHandler = new DefaultImClientHandler();
}
}
public ImClientListener getImClientListener() {
return imClientListener;
}
public void setImClientListener(ImClientListener imClientListener) {
this.imClientListener = imClientListener;
if(Objects.isNull(this.imClientListener)){
this.imClientListener = new DefaultImClientListener();
}
}
public ReconnConf getReConnConf() {
return reconnConf;
}
public void setReConnConf(ReconnConf reconnConf) {
this.reconnConf = reconnConf;
ClientTioConfig clientTioConfig = (ClientTioConfig)this.tioConfig;
clientTioConfig.setReconnConf(reconnConf);
}
}

View File

@ -0,0 +1,39 @@
package org.jim.client.handler;
import org.jim.core.ImChannelContext;
import org.jim.core.ImPacket;
import org.jim.core.config.ImConfig;
import org.jim.core.exception.ImDecodeException;
import org.jim.core.exception.ImException;
import java.nio.ByteBuffer;
/**
* @ClassName DefaultImClientHandler
* @Description 默认的IM客户端回调
* @Author WChao
* @Date 2020/1/6 2:25
* @Version 1.0
**/
public class DefaultImClientHandler implements ImClientHandler {
@Override
public void handler(ImPacket imPacket, ImChannelContext imChannelContext) throws ImException {
}
@Override
public ByteBuffer encode(ImPacket imPacket, ImConfig imConfig, ImChannelContext imChannelContext) {
return null;
}
@Override
public ImPacket decode(ByteBuffer buffer, int limit, int position, int readableLength, ImChannelContext imChannelContext) throws ImDecodeException {
return null;
}
@Override
public ImPacket heartbeatPacket(ImChannelContext imChannelContext) {
return null;
}
}

View File

@ -0,0 +1,20 @@
package org.jim.client.handler;
import org.jim.core.ImChannelContext;
import org.jim.core.ImHandler;
import org.jim.core.ImPacket;
/**
*
* 客户端回调
* @author WChao
*
*/
public interface ImClientHandler extends ImHandler {
/**
* 心跳包接口
* @param imChannelContext
* @return
*/
ImPacket heartbeatPacket(ImChannelContext imChannelContext);
}

View File

@ -0,0 +1,56 @@
package org.jim.client.handler;
import org.jim.client.ImClientChannelContext;
import org.jim.core.ImConst;
import org.jim.core.ImPacket;
import org.jim.core.config.ImConfig;
import org.jim.core.exception.ImDecodeException;
import org.tio.client.intf.ClientAioHandler;
import org.tio.core.ChannelContext;
import org.tio.core.TioConfig;
import org.tio.core.exception.AioDecodeException;
import org.tio.core.intf.Packet;
import java.nio.ByteBuffer;
/**
* @ClassName ImClientHandlerAdapter
* @Description IM客户端回调适配器
* @Author WChao
* @Date 2020/1/6 2:30
* @Version 1.0
**/
public class ImClientHandlerAdapter implements ClientAioHandler, ImConst{
private ImClientHandler imClientHandler;
public ImClientHandlerAdapter(ImClientHandler imClientHandler){
this.imClientHandler = imClientHandler;
}
@Override
public Packet decode(ByteBuffer buffer, int limit, int position, int readableLength, ChannelContext channelContext) throws AioDecodeException {
ImPacket imPacket;
try {
imPacket = this.imClientHandler.decode(buffer, limit, position, readableLength, (ImClientChannelContext)channelContext.get(Key.IM_CHANNEL_CONTEXT_KEY));
}catch (ImDecodeException e) {
throw new AioDecodeException(e);
}
return imPacket;
}
@Override
public ByteBuffer encode(Packet packet, TioConfig tioConfig, ChannelContext channelContext) {
return this.imClientHandler.encode((ImPacket)packet, ImConfig.Global.get(), (ImClientChannelContext)channelContext.get(Key.IM_CHANNEL_CONTEXT_KEY));
}
@Override
public void handler(Packet packet, ChannelContext channelContext) throws Exception {
this.imClientHandler.handler((ImPacket)packet, (ImClientChannelContext)channelContext.get(Key.IM_CHANNEL_CONTEXT_KEY));
}
@Override
public Packet heartbeatPacket(ChannelContext channelContext) {
return this.imClientHandler.heartbeatPacket((ImClientChannelContext)channelContext.get(Key.IM_CHANNEL_CONTEXT_KEY));
}
}

View File

@ -0,0 +1,45 @@
package org.jim.client.listener;
import org.jim.core.ImChannelContext;
import org.jim.core.ImPacket;
/**
* @ClassName DefaultImClientListener
* @Description 默认IM客户端连接监听器
* @Author WChao
* @Date 2020/1/4 11:15
* @Version 1.0
**/
public class DefaultImClientListener implements ImClientListener {
@Override
public void onAfterConnected(ImChannelContext channelContext, boolean isConnected, boolean isReconnect) throws Exception {
}
@Override
public void onAfterDecoded(ImChannelContext channelContext, ImPacket packet, int packetSize) throws Exception {
}
@Override
public void onAfterReceivedBytes(ImChannelContext channelContext, int receivedBytes) throws Exception {
}
@Override
public void onAfterSent(ImChannelContext channelContext, ImPacket packet, boolean isSentSuccess) throws Exception {
}
@Override
public void onAfterHandled(ImChannelContext channelContext, ImPacket packet, long cost) throws Exception {
}
@Override
public void onBeforeClose(ImChannelContext channelContext, Throwable throwable, String remark, boolean isRemove) throws Exception {
}
}

View File

@ -0,0 +1,15 @@
package org.jim.client.listener;
import org.jim.core.ImChannelContext;
import org.jim.core.listener.ImListener;
/**
* @ClassName ImClientListener
* @Description IM客户端连接监听器接口
* @Author WChao
* @Date 2020/1/4 9:35
* @Version 1.0
**/
public interface ImClientListener extends ImListener {
}

View File

@ -0,0 +1,59 @@
package org.jim.client.listener;
import org.jim.client.ImClientChannelContext;
import org.jim.client.config.ImClientConfig;
import org.jim.core.ImConst;
import org.jim.core.ImPacket;
import org.tio.client.intf.ClientAioListener;
import org.tio.core.ChannelContext;
import org.tio.core.intf.Packet;
/**
* @ClassName ImClientListenerAdapter
* @Description IM客户端连接监听适配器
* @Author WChao
* @Date 2020/1/4 9:35
* @Version 1.0
**/
public class ImClientListenerAdapter implements ClientAioListener, ImConst{
/**
* 客户端端监听器
*/
private ImClientListener imClientListener;
public ImClientListenerAdapter(ImClientListener imClientListener) {
this.imClientListener = imClientListener == null ? new DefaultImClientListener(): imClientListener;
}
@Override
public void onAfterConnected(ChannelContext channelContext, boolean isConnected, boolean isReconnect)throws Exception{
ImClientChannelContext imClientChannelContext = new ImClientChannelContext(ImClientConfig.Global.get(), channelContext);
channelContext.set(Key.IM_CHANNEL_CONTEXT_KEY, imClientChannelContext);
imClientListener.onAfterConnected(imClientChannelContext, isConnected, isReconnect);
}
@Override
public void onAfterSent(ChannelContext channelContext, Packet packet, boolean isSentSuccess)throws Exception{
imClientListener.onAfterSent((ImClientChannelContext)channelContext.get(Key.IM_CHANNEL_CONTEXT_KEY), (ImPacket)packet, isSentSuccess);
}
@Override
public void onBeforeClose(ChannelContext channelContext, Throwable throwable, String remark, boolean isRemove)throws Exception{
imClientListener.onBeforeClose((ImClientChannelContext)channelContext.get(Key.IM_CHANNEL_CONTEXT_KEY), throwable, remark, isRemove);
}
@Override
public void onAfterDecoded(ChannelContext channelContext, Packet packet,int packetSize) throws Exception {
imClientListener.onAfterDecoded((ImClientChannelContext)channelContext.get(Key.IM_CHANNEL_CONTEXT_KEY), (ImPacket)packet, packetSize);
}
@Override
public void onAfterReceivedBytes(ChannelContext channelContext,int receivedBytes) throws Exception {
imClientListener.onAfterReceivedBytes((ImClientChannelContext)channelContext.get(Key.IM_CHANNEL_CONTEXT_KEY), receivedBytes);
}
@Override
public void onAfterHandled(ChannelContext channelContext, Packet packet,long cost) throws Exception {
imClientListener.onAfterHandled((ImClientChannelContext)channelContext.get(Key.IM_CHANNEL_CONTEXT_KEY), (ImPacket)packet, cost);
}
}

View File

@ -1,16 +0,0 @@
#http://logback.qos.ch/manual/configuration.html
#<include resource="includedConfig.xml"/> resource, file, url (被包含的文件需要满足一定格式)
context.name=jim-client
log.dir=/logs/jim-client
rolling.policy.file.name.pattern=yyyy-MM-dd HH
max.file.size=100MB
max.history=50
conversion.pattern=%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{30}[%line]: %m%n
root.level=debug

View File

@ -1,129 +0,0 @@
<configuration scan="true" scanPeriod="10 seconds" debug="true">
<property resource="logback.properties" />
<contextName>${context.name}</contextName> <!-- 本项目的名字 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${conversion.pattern}</pattern>
</encoder>
</appender>
<!-- root file 日志 -->
<appender name="root-file-error"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.dir}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.dir}/error.%d{${rolling.policy.file.name.pattern}}%d{mmss}.%i.log.zip</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${max.file.size}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>${max.history}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${conversion.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="root-file-warn"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.dir}/warn.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.dir}/warn.%d{${rolling.policy.file.name.pattern}}%d{mmss}.%i.log.zip</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${max.file.size}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>${max.history}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${conversion.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="root-file-info"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.dir}/info.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.dir}/info.%d{${rolling.policy.file.name.pattern}}%d{mmss}.%i.log.zip</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${max.file.size}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>${max.history}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${conversion.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="root-file-debug"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.dir}/debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.dir}/debug.%d{${rolling.policy.file.name.pattern}}%d{mmss}.%i.log.zip</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${max.file.size}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>${max.history}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${conversion.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>debug</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<root level="${root.level}">
<appender-ref ref="console" />
<appender-ref ref="root-file-error"/>
<appender-ref ref="root-file-warn"/>
<appender-ref ref="root-file-info"/>
<appender-ref ref="root-file-debug"/>
</root>
<!-- 跟踪客户端行为 -->
<appender name="tio-client-trace-log-appender" class="ch.qos.logback.classic.sift.SiftingAppender">
<discriminator>
<Key>tio_client</Key>
<DefaultValue>unknown</DefaultValue>
</discriminator>
<sift>
<appender name="${tio_client}" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.dir}/tio/client-trace/${tio_client}_%d{yyyyMMdd}.log</fileNamePattern>
<maxFileSize>20MB</maxFileSize>
</rollingPolicy>
<Append>false</Append>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%m%n</pattern>
</layout>
</appender>
</sift>
</appender>
<logger name="tio-client-trace-log" additivity="false">
<level value="info"/>
<appender-ref ref="tio-client-trace-log-appender"/>
</logger>
</configuration>

View File

@ -287,10 +287,6 @@ public interface ImConst
String FRIENDS = "friends";
String ONLINE = "online";
String OFFLINE = "offline";
String JIM = "J-IM";
}

View File

@ -24,13 +24,13 @@ public interface ImHandler {
* @return
* @throws ImDecodeException
*/
ImPacket decode(ByteBuffer buffer, int limit, int position, int readableLength, ImChannelContext imChannelContext) throws ImDecodeException;
ImPacket decode(ByteBuffer buffer, int limit, int position, int readableLength, ImChannelContext imChannelContext) throws ImDecodeException, ImDecodeException;
/**
* 编码
* @param imPacket
* @param imConfig
* @param imChannelContext
* 将业务包编码为网络byte字节传输
* @param imPacket 业务消息包
* @param imConfig IM配置
* @param imChannelContext 通道上下文
* @return
* @author: WChao
*/
@ -38,8 +38,8 @@ public interface ImHandler {
/**
* 处理消息包
* @param imPacket
* @param imChannelContext
* @param imPacket 业务消息包
* @param imChannelContext 通道上下文
* @throws ImException
* @author: WChao
*/

View File

@ -8,7 +8,7 @@ import java.io.InputStream;
import java.net.URL;
import java.util.Properties;
import org.jim.core.Jim;
import org.jim.core.JimVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
@ -87,7 +87,7 @@ public class RedisConfigurationFactory {
url = standardClassloader.getResource(DEFAULT_CLASSPATH_CONFIGURATION_FILE);
}
if (url == null) {
url = Jim.class.getResource(DEFAULT_CLASSPATH_CONFIGURATION_FILE);
url = JimVersion.class.getResource(DEFAULT_CLASSPATH_CONFIGURATION_FILE);
}
if (url != null) {
LOG.debug("Configuring redis from jim.properties found in the classpath: " + url);

View File

@ -9,12 +9,12 @@ package org.jim.core.cluster;
* @author WChao
* @date 2020-04-29
*/
public abstract class ImCluster implements ICluster{
public abstract class ImCluster implements ICluster {
/**
* IM集群配置
*/
protected ImClusterConfig clusterConfig;
public ImCluster(ImClusterConfig clusterConfig){
this.clusterConfig = clusterConfig;
}

View File

@ -1,8 +1,9 @@
package org.jim.core.cluster;
import org.jim.core.ImPacket;
import java.util.UUID;
import org.jim.core.ImPacket;
/**
* 成员变量group, userId, ip谁有值就发给谁toAll为true则发给所有<br>
* packet是不允许为null的

View File

@ -12,6 +12,7 @@ import org.jim.core.listener.ImGroupListenerAdapter;
import org.jim.core.listener.ImListener;
import org.jim.core.listener.ImUserListener;
import org.tio.core.TioConfig;
import org.tio.core.ssl.SslConfig;
import org.tio.utils.Threads;
import org.tio.utils.prop.MapWithLockPropSupport;
import org.tio.utils.thread.pool.DefaultThreadFactory;
@ -28,11 +29,11 @@ public abstract class ImConfig extends MapWithLockPropSupport implements ImConst
/**
* IP地址
*/
protected String bindIp;
protected String bindIp = "0.0.0.0";
/**
* 监听端口
*/
protected Integer bindPort = 80;
protected Integer bindPort = 0;
/**
* 默认的接收数据的buffer size
@ -41,7 +42,7 @@ public abstract class ImConfig extends MapWithLockPropSupport implements ImConst
/**
* 配置名称
*/
protected String name = "j-im";
protected String name = "J-IM";
/**
* 集群配置
* 如果此值不为null就表示要集群
@ -51,12 +52,17 @@ public abstract class ImConfig extends MapWithLockPropSupport implements ImConst
* tio相关配置信息
*/
protected TioConfig tioConfig;
/**
* SSL配置
*/
protected SslConfig sslConfig;
/**
* 心跳包发送时长heartbeatTimeout/2
*/
protected long heartbeatTimeout = 0;
/**
* J-IM内部线程池
*/
protected SynThreadPoolExecutor jimExecutor;
/**
* 群组绑定监听器
@ -116,15 +122,15 @@ public abstract class ImConfig extends MapWithLockPropSupport implements ImConst
/**
* IP地址
*/
protected String bindIp;
protected String bindIp = "0.0.0.0";
/**
* 监听端口
*/
protected Integer bindPort = 80;
protected Integer bindPort = 0;
/**
* 配置名称
*/
protected String name = "j-im";
protected String name = "J-IM";
/**
* 默认的接收数据的buffer size
@ -141,10 +147,9 @@ public abstract class ImConfig extends MapWithLockPropSupport implements ImConst
*/
protected ImCluster cluster;
/**
* tio相关配置信息
* SSL配置
*/
protected TioConfig tioConfig;
protected SslConfig sslConfig;
/**
* 群组绑定监听器
*/
@ -191,8 +196,8 @@ public abstract class ImConfig extends MapWithLockPropSupport implements ImConst
this.cluster = cluster;
return theBuilder;
}
public B tioConfig(TioConfig tioConfig){
this.tioConfig = tioConfig;
public B sslConfig(SslConfig sslConfig){
this.sslConfig = sslConfig;
return theBuilder;
}
public B groupListener(ImGroupListener imGroupListener){
@ -291,6 +296,21 @@ public abstract class ImConfig extends MapWithLockPropSupport implements ImConst
this.cluster = cluster;
}
public SslConfig getSslConfig() {
return sslConfig;
}
public void setSslConfig(SslConfig sslConfig) {
this.sslConfig = sslConfig;
this.tioConfig.setSslConfig(sslConfig);
}
public String toBindAddressString(){
StringBuilder builder = new StringBuilder();
builder.append(bindIp).append(":").append(bindPort);
return builder.toString();
}
static {
JimBanner banner = new JimBanner();
banner.printBanner(System.out);

View File

@ -34,6 +34,17 @@ public class HttpConvertPacket implements IProtocolConverter {
return null;
}
@Override
public ImPacket RespPacket(ImPacket imPacket, Command command, ImChannelContext imChannelContext) {
ImSessionContext sessionContext = imChannelContext.getSessionContext();
if(sessionContext instanceof HttpSession){
HttpResponse response = (HttpResponse)imPacket;
response.setCommand(command);
return response;
}
return null;
}
@Override
public ImPacket ReqPacket(byte[] body, Command command, ImChannelContext channelContext) {

View File

@ -70,8 +70,8 @@ public class HttpResponseEncoder implements ImConst {
sb.append(Http.ResponseHeaderKey.Set_Cookie).append(": ");
sb.append(cookie.toString());
sb.append("\r\n");
if (log.isInfoEnabled()) {
log.info("{}, set-cookie:{}", channelContext, cookie.toString());
if (log.isDebugEnabled()) {
log.debug("{}, set-cookie:{}", channelContext, cookie.toString());
}
}
}

View File

@ -35,7 +35,7 @@ public class User extends Message implements Serializable{
/**
* 在线状态(onlineoffline)
*/
private String status = ImConst.OFFLINE;
private String status = UserStatusType.OFFLINE.getStatus();
/**
* 个性签名;
*/
@ -151,7 +151,7 @@ public class User extends Message implements Serializable{
/**
* 在线状态(onlineoffline)
*/
private String status = ImConst.OFFLINE;
private String status = UserStatusType.OFFLINE.getStatus();
/**
* 个性签名;
*/

View File

@ -8,7 +8,7 @@ public enum UserStatusType {
*
* <code>ONLINE = 0;</code>
*/
ONLINE(0),
ONLINE(0, "online", "在线"),
/**
* <pre>
*离线
@ -16,7 +16,7 @@ public enum UserStatusType {
*
* <code>OFFLINE = 1;</code>
*/
OFFLINE(1),
OFFLINE(1, "offline", "离线"),
/**
* <pre>
* ALL所有(在线+离线)
@ -24,7 +24,7 @@ public enum UserStatusType {
*
* <code>ALL = 2;</code>
*/
ALL(2);
ALL(2, "all", "所有");
public final int getNumber() {
@ -45,7 +45,22 @@ public enum UserStatusType {
}
private final int value;
UserStatusType(int value) {
private final String status;
private final String desc;
UserStatusType(int value, String status, String desc) {
this.value = value;
this.status = status;
this.desc = desc;
}
public String getStatus() {
return status;
}
public String getDesc() {
return desc;
}
}

View File

@ -29,4 +29,13 @@ public interface IProtocolConverter {
* @return
*/
ImPacket RespPacket(byte[] body,Command command, ImChannelContext imChannelContext);
/**
* 转化响应包
* @param imPacket
* @param command
* @param imChannelContext
* @return
*/
ImPacket RespPacket(ImPacket imPacket, Command command, ImChannelContext imChannelContext);
}

View File

@ -30,6 +30,13 @@ public class TcpConvertPacket implements IProtocolConverter {
}
return null;
}
@Override
public ImPacket RespPacket(ImPacket imPacket, Command command, ImChannelContext imChannelContext) {
return this.RespPacket(imPacket.getBody(), command, imChannelContext);
}
/**
* 转TCP协议请求包;
*/

View File

@ -63,7 +63,7 @@ public class TcpServerDecoder implements ImConst {
}catch(Exception e){
logger.error(e.toString());
}
logger.info("TCP解码成功...");
logger.debug("TCP解码成功...");
//byteBuffer的总长度是 = 1byte协议版本号+1byte消息标志位+4byte同步序列号(如果是同步发送则多4byte同步序列号,否则无4byte序列号)+1byte命令码+4byte消息的长度+消息体的长度
TcpPacket tcpPacket = new TcpPacket(Command.forNumber(cmdByte), body);
tcpPacket.setVersion(version);

View File

@ -3,17 +3,12 @@
*/
package org.jim.core.utils;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.jim.core.ImChannelContext;
import org.jim.core.ImSessionContext;
import org.jim.core.Jim;
import org.jim.core.packets.ImClientNode;
import org.jim.core.packets.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.Objects;
/**
* IM工具类;
@ -24,41 +19,6 @@ public class ImKit {
private static Logger logger = LoggerFactory.getLogger(ImKit.class);
/**
* 根据群组获取所有用户;
* @param groupId 群组ID
* @return 群组用户集合列表
*/
public static List<User> getUsersByGroup(String groupId){
List<ImChannelContext> channelContexts = Jim.getByGroup(groupId);
List<User> users = Lists.newArrayList();
if(CollectionUtils.isEmpty(channelContexts)){
return users;
}
Map<String,User> userMap = new HashMap<>();
channelContexts.forEach(imChannelContext -> {
User user = imChannelContext.getSessionContext().getImClientNode().getUser();
if(Objects.nonNull(user) && Objects.isNull(userMap.get(user.getUserId()))){
userMap.put(user.getUserId(), user);
users.add(user);
}
});
return users;
}
/**
* 根据用户ID获取用户信息(一个用户ID会有多端通道,默认取第一个)
* @param userId 用户ID
* @return user信息
*/
public static User getUser(String userId){
List<ImChannelContext> imChannelContexts = Jim.getByUserId(userId);
if(CollectionUtils.isEmpty(imChannelContexts)) {
return null;
}
return imChannelContexts.get(0).getSessionContext().getImClientNode().getUser();
}
/**
* 设置Client对象到ImSessionContext中
* @param channelContext 通道上下文

View File

@ -33,6 +33,12 @@ public class WsConvertPacket implements IProtocolConverter {
return null;
}
@Override
public ImPacket RespPacket(ImPacket imPacket, Command command, ImChannelContext imChannelContext) {
return this.RespPacket(imPacket.getBody(), command, imChannelContext);
}
@Override
public ImPacket ReqPacket(byte[] body, Command command, ImChannelContext channelContext) {

View File

@ -4,11 +4,9 @@
package org.jim.server.demo;
import org.apache.commons.lang3.StringUtils;
import org.jim.core.Jim;
import org.jim.core.config.ImConfig;
import org.jim.core.packets.Command;
import org.jim.core.utils.PropUtil;
import org.jim.server.ImServerStarter;
import org.jim.server.JimServer;
import org.jim.server.command.CommandManager;
import org.jim.server.command.handler.ChatReqHandler;
import org.jim.server.command.handler.HandshakeReqHandler;
@ -37,7 +35,7 @@ public class ImServerDemoStart {
imServerConfig.setImGroupListener(new ImDemoGroupListener());
//设置绑定用户监听器非必须根据需要自己选择性实现;
imServerConfig.setImUserListener(new ImDemoUserListener());
ImServerStarter imServerStarter = new ImServerStarter(imServerConfig);
JimServer jimServer = new JimServer(imServerConfig);
/*****************start 以下处理器根据业务需要自行添加与扩展每个Command都可以添加扩展,此处为demo中处理**********************************/
@ -51,7 +49,7 @@ public class ImServerDemoStart {
ChatReqHandler chatReqHandler = CommandManager.getCommand(Command.COMMAND_CHAT_REQ, ChatReqHandler.class);
chatReqHandler.setSingleProcessor(new DefaultAsyncChatMessageProcessor());
/*****************end *******************************************************************************************/
imServerStarter.start();
jimServer.start();
}
/**

View File

@ -5,13 +5,13 @@ package org.jim.server.demo.command;
import org.jim.core.ImChannelContext;
import org.jim.core.ImConst;
import org.jim.core.Jim;
import org.jim.core.ImPacket;
import org.jim.core.exception.ImException;
import org.jim.core.http.HttpRequest;
import org.jim.core.packets.Command;
import org.jim.core.packets.LoginReqBody;
import org.jim.core.utils.JsonKit;
import org.jim.server.JimServerAPI;
import org.jim.server.command.CommandManager;
import org.jim.server.command.handler.LoginReqHandler;
import org.jim.server.processor.handshake.WsHandshakeProcessor;
@ -38,7 +38,7 @@ public class DemoWsHandshakeProcessor extends WsHandshakeProcessor {
}
ImPacket loginRespPacket = loginHandler.handler(request, imChannelContext);
if(loginRespPacket != null){
Jim.send(imChannelContext, loginRespPacket);
JimServerAPI.send(imChannelContext, loginRespPacket);
}
}
}

View File

@ -2,21 +2,22 @@ package org.jim.server.demo.listener;
import org.jim.core.*;
import org.jim.core.exception.ImException;
import org.jim.core.listener.ImGroupListener;
import org.jim.core.packets.*;
import org.jim.core.utils.JsonKit;
import org.jim.server.JimServerAPI;
import org.jim.server.listener.AbstractImGroupListener;
import org.jim.server.protocol.ProtocolManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 群组绑定监听器
* @author WChao
* 2017年5月13日 下午10:38:36
*/
public class ImDemoGroupListener extends AbstractImGroupListener {
private static Logger logger = LoggerFactory.getLogger(ImGroupListener.class);
private static Logger logger = LoggerFactory.getLogger(ImDemoGroupListener.class);
@Override
public void doAfterBind(ImChannelContext imChannelContext, Group group) throws ImException {
@ -50,7 +51,7 @@ public class ImDemoGroupListener extends AbstractImGroupListener {
RespBody respBody = new RespBody(Command.COMMAND_EXIT_GROUP_NOTIFY_RESP,exitGroupNotifyRespBody);
ImPacket imPacket = new ImPacket(Command.COMMAND_EXIT_GROUP_NOTIFY_RESP, respBody.toByte());
Jim.sendToGroup(group.getGroupId(), ProtocolManager.Converter.respPacket(imPacket, imChannelContext));
JimServerAPI.sendToGroup(group.getGroupId(), imPacket);
}
/**
@ -61,12 +62,12 @@ public class ImDemoGroupListener extends AbstractImGroupListener {
public void joinGroupNotify(Group group, ImChannelContext imChannelContext)throws ImException{
ImSessionContext imSessionContext = imChannelContext.getSessionContext();
User clientUser = imSessionContext.getImClientNode().getUser();
User notifyUser = User.newBuilder().userId(clientUser.getUserId()).nick(clientUser.getNick()).build();
User notifyUser = User.newBuilder().userId(clientUser.getUserId()).nick(clientUser.getNick()).status(UserStatusType.ONLINE.getStatus()).build();
String groupId = group.getGroupId();
//发进房间通知 COMMAND_JOIN_GROUP_NOTIFY_RESP
JoinGroupNotifyRespBody joinGroupNotifyRespBody = JoinGroupNotifyRespBody.success();
joinGroupNotifyRespBody.setGroup(groupId).setUser(notifyUser);
Jim.sendToGroup(groupId, ProtocolManager.Converter.respPacket(joinGroupNotifyRespBody,imChannelContext));
JimServerAPI.sendToGroup(groupId, ProtocolManager.Converter.respPacket(joinGroupNotifyRespBody,imChannelContext));
}
}

View File

@ -5,10 +5,7 @@ package org.jim.server.demo.service;
import cn.hutool.core.util.RandomUtil;
import org.jim.core.*;
import org.jim.core.packets.Group;
import org.jim.core.packets.LoginReqBody;
import org.jim.core.packets.LoginRespBody;
import org.jim.core.packets.User;
import org.jim.core.packets.*;
import org.jim.core.session.id.impl.UUIDSessionIdGenerator;
import org.jim.core.utils.Md5;
import org.jim.server.processor.login.LoginCmdProcessor;
@ -70,7 +67,7 @@ public class LoginServiceProcessor extends AbstractProtocolCmdProcessor implemen
.addGroup(Group.newBuilder().groupId("100").name("J-IM朋友圈").build());
//模拟的用户好友,正式根据业务去查数据库或者缓存;
initFriends(builder);
builder.avatar(nextImg()).status(ONLINE);
builder.avatar(nextImg()).status(UserStatusType.ONLINE.getStatus());
user = builder.build();
if (tokenMap.size() > 10000) {
tokenMap.clear();

View File

@ -1,6 +0,0 @@
_____ _____ ____ ____
|_ _| |_ _||_ \ / _|
| | ______ | | | \/ |
_ | ||______|| | | |\ /| |
| |__' | _| |_ _| |_\/_| |_
`.____.' |_____||_____||_____|

View File

@ -6,9 +6,8 @@ package org.jim.server;
import com.google.common.base.Stopwatch;
import org.jim.core.ImConst;
import org.jim.core.cache.redis.RedissonTemplate;
import org.jim.core.cluster.redis.RedisCluster;
import org.jim.core.cluster.redis.RedisClusterConfig;
import org.jim.core.config.ImConfig;
import org.jim.server.cluster.redis.RedisCluster;
import org.jim.server.cluster.redis.RedisClusterConfig;
import org.jim.server.config.ImServerConfig;
import org.jim.server.protocol.ProtocolManager;
import org.jim.server.helper.redis.RedisMessageHelper;
@ -24,13 +23,13 @@ import java.util.concurrent.TimeUnit;
* @author WChao
*
*/
public class ImServerStarter {
public class JimServer {
private static Logger log = LoggerFactory.getLogger(ImServerStarter.class);
private static Logger log = LoggerFactory.getLogger(JimServer.class);
private TioServer tioServer = null;
private ImServerConfig imServerConfig;
public ImServerStarter(ImServerConfig imServerConfig){
public JimServer(ImServerConfig imServerConfig){
this.imServerConfig = imServerConfig;
}
@ -55,10 +54,10 @@ public class ImServerStarter {
public void start() throws IOException {
Stopwatch timeWatch = Stopwatch.createStarted();
log.warn("J-IM server start");
log.warn("J-IM Server start");
init(imServerConfig);
tioServer.start(this.imServerConfig.getBindIp(), this.imServerConfig.getBindPort());
log.warn("J-IM server started at address: {} time:{}ms", imServerConfig.getBindIp()+":"+imServerConfig.getBindPort(), timeWatch.elapsed(TimeUnit.MILLISECONDS));
log.warn("J-IM Server started at address: {} time:{}ms", imServerConfig.getBindIp()+":"+imServerConfig.getBindPort(), timeWatch.elapsed(TimeUnit.MILLISECONDS));
}
public void stop(){

View File

@ -1,36 +1,42 @@
package org.jim.core;
package org.jim.server;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jim.core.ImChannelContext;
import org.jim.core.ImConst;
import org.jim.core.ImPacket;
import org.jim.core.cluster.ImCluster;
import org.jim.core.config.ImConfig;
import org.jim.core.exception.ImException;
import org.jim.core.listener.ImGroupListener;
import org.jim.core.listener.ImUserListener;
import org.jim.core.packets.Group;
import org.jim.core.packets.User;
import org.jim.core.packets.UserStatusType;
import org.jim.server.protocol.ProtocolManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.Tio;
import org.tio.core.ChannelContext;
import org.tio.core.ChannelContextFilter;
import org.tio.core.Tio;
import org.tio.core.TioConfig;
import org.tio.utils.lock.SetWithLock;
import java.util.*;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
/**
* 版本: [1.0]
* 功能说明: JIM
* @author : WChao 创建时间: 2017年9月22日 上午9:07:18
*/
public class Jim implements ImConst{
public class JimServerAPI implements ImConst{
public static ImConfig imConfig = ImConfig.Global.get();
private static Logger log = LoggerFactory.getLogger(Jim.class);
private static Logger log = LoggerFactory.getLogger(JimServerAPI.class);
/**
* 根据群组ID获取该群组下所有Channel
@ -39,17 +45,42 @@ public class Jim implements ImConst{
*/
public static List<ImChannelContext> getByGroup(String groupId){
SetWithLock<ChannelContext> channelContextSetWithLock = Tio.getByGroup(imConfig.getTioConfig(), groupId);
return convertChannelToImChannel(channelContextSetWithLock);
List<ImChannelContext> imChannelContextList = convertChannelToImChannel(channelContextSetWithLock);
if (CollectionUtils.isEmpty(imChannelContextList)) {
//log.info("{}, there is no bind channel with groupId[{}]", imConfig.getName(), groupId);
return imChannelContextList;
}
return imChannelContextList;
}
/**
* 根据用户ID获取用户下所有Channel
* 根据用户ID获取用户下所有channel
* @param userId 用户ID
* @return 用户所有通道集合
* @return 用户绑定所有通道集合
*/
public static List<ImChannelContext> getByUserId(String userId){
SetWithLock<ChannelContext> channelContextSetWithLock = Tio.getByUserid(imConfig.getTioConfig(), userId);
return convertChannelToImChannel(channelContextSetWithLock);
List<ImChannelContext> imChannelContextList = convertChannelToImChannel(channelContextSetWithLock);
if (CollectionUtils.isEmpty(imChannelContextList)) {
//log.info("{}, there is no bind channel with userId[{}]", imConfig.getName(), userId);
return imChannelContextList;
}
return imChannelContextList;
}
/**
* 根据指定IP获取所有绑定的channel
* @param ip 指定IP地址
* @return 所有绑定ip的通道集合
*/
public static List<ImChannelContext> getByIp(String ip){
SetWithLock<ChannelContext> channelContextSetWithLock = imConfig.getTioConfig().ips.clients(imConfig.getTioConfig(), ip);
List<ImChannelContext> imChannelContextList = convertChannelToImChannel(channelContextSetWithLock);
if (CollectionUtils.isEmpty(imChannelContextList)) {
//log.info("{}, there is no bind channel with ip[{}]", imConfig.getName(), ip);
return imChannelContextList;
}
return imChannelContextList;
}
/**
@ -57,9 +88,20 @@ public class Jim implements ImConst{
* @param groupId 群组ID
* @param packet 消息包
*/
public static void sendToGroup(String groupId, ImPacket packet){
public static Boolean sendToGroup(String groupId, ImPacket packet){
List<ImChannelContext> imChannelContextList = getByGroup(groupId);
if(CollectionUtils.isEmpty(imChannelContextList)){
ImCluster cluster = imConfig.getCluster();
if (cluster != null && !packet.isFromCluster()) {
cluster.clusterToGroup(groupId, packet);
}
return true;
}
try {
Tio.sendToGroup(imConfig.getTioConfig(), groupId, packet);
imChannelContextList.forEach(imChannelContext -> {
send(imChannelContext,packet);
});
return true;
}finally {
ImCluster cluster = imConfig.getCluster();
if (Objects.nonNull(cluster) && !packet.isFromCluster()) {
@ -69,7 +111,7 @@ public class Jim implements ImConst{
}
/**
* 发送到指定通道;
* 发送到指定通道
* @param imChannelContext IM通道上下文
* @param imPacket 消息包
*/
@ -77,30 +119,52 @@ public class Jim implements ImConst{
if(imChannelContext == null){
return false;
}
return Tio.send(imChannelContext.getTioChannelContext(),imPacket);
ImPacket convertPacket = convertPacket(imChannelContext , imPacket);
if(convertPacket == null){
return false;
}
return Tio.send(imChannelContext.getTioChannelContext(), convertPacket);
}
/**
* 阻塞发送确认把packet发送到对端后再返回
* @param imChannelContext IM通道上下文
* @param packet 消息包
* @param imPacket 消息包
* @return
*/
public static boolean bSend(ImChannelContext imChannelContext , ImPacket packet){
public static boolean bSend(ImChannelContext imChannelContext , ImPacket imPacket){
if(imChannelContext == null){
return false;
}
return Tio.bSend(imChannelContext.getTioChannelContext(), packet);
ImPacket convertPacket = convertPacket(imChannelContext , imPacket);
if(convertPacket == null){
return false;
}
return Tio.bSend(imChannelContext.getTioChannelContext(), convertPacket);
}
/**
* 发送到指定用户;
* 发送到指定用户(所有不同协议端)
* @param userId 用户ID
* @param packet 消息包
*/
public static void sendToUser(String userId,ImPacket packet){
public static boolean sendToUser(String userId, ImPacket packet){
List<ImChannelContext> imChannelContexts = getByUserId(userId);
if(CollectionUtils.isEmpty(imChannelContexts)){
ImCluster cluster = imConfig.getCluster();
if (cluster != null && !packet.isFromCluster()) {
cluster.clusterToUser(userId, packet);
}
return true;
}
try {
Tio.sendToUser(imConfig.getTioConfig(), userId, packet);
imChannelContexts.forEach(imChannelContext -> {
send(imChannelContext, packet);
});
return true;
}catch (Exception e){
log.error("an exception occurred when sending to the specified user", e);
return false;
}finally {
ImCluster cluster = imConfig.getCluster();
if (Objects.nonNull(cluster) && !packet.isFromCluster()) {
@ -114,8 +178,8 @@ public class Jim implements ImConst{
* @param ip 客户端IP地址
* @param packet 消息包
*/
public static void sendToIp( String ip, ImPacket packet) {
sendToIp(ip, packet, null);
public static Boolean sendToIp( String ip, ImPacket packet) {
return sendToIp(ip, packet, null);
}
/**
@ -124,9 +188,23 @@ public class Jim implements ImConst{
* @param packet 消息包
* @param channelContextFilter 通道过滤器
*/
public static void sendToIp(String ip, ImPacket packet, ChannelContextFilter channelContextFilter) {
public static Boolean sendToIp(String ip, ImPacket packet, ChannelContextFilter channelContextFilter) {
List<ImChannelContext> imChannelContexts = getByIp(ip);
if (CollectionUtils.isEmpty(imChannelContexts)) {
ImCluster cluster = imConfig.getCluster();
if (cluster != null && !packet.isFromCluster()) {
cluster.clusterToIp(ip, packet);
}
return true;
}
try{
Tio.sendToIp(imConfig.getTioConfig(), ip, packet, channelContextFilter);
imChannelContexts.forEach(imChannelContext -> {
send(imChannelContext, packet);
});
return true;
}catch (Exception e){
log.error("an exception occurred when sending to the specified ip:[{}]", ip, e);
return false;
}finally {
ImCluster cluster = imConfig.getCluster();
if (Objects.nonNull(cluster) && !packet.isFromCluster()) {
@ -143,7 +221,7 @@ public class Jim implements ImConst{
public static boolean bindUser(ImChannelContext imChannelContext, String userId){
User user = imChannelContext.getSessionContext().getImClientNode().getUser();
if(Objects.isNull(user)){
user = User.newBuilder().userId(userId).status(ONLINE).build();
user = User.newBuilder().userId(userId).status(UserStatusType.ONLINE.getStatus()).build();
}
return bindUser(imChannelContext, user);
}
@ -154,11 +232,11 @@ public class Jim implements ImConst{
* @param user 绑定用户信息
*/
public static boolean bindUser(ImChannelContext imChannelContext, User user){
String userId = user.getUserId();
if(StringUtils.isEmpty(userId)){
log.error("userId is null");
if(Objects.isNull(user)|| StringUtils.isEmpty(user.getUserId())){
log.error("user or userId is null");
return false;
}
String userId = user.getUserId();
Tio.bindUser(imChannelContext.getTioChannelContext(), userId);
SetWithLock<ChannelContext> channelContextSetWithLock = Tio.getByUserid(imConfig.getTioConfig(), userId);
ReadLock lock = channelContextSetWithLock.getLock().readLock();
@ -196,11 +274,11 @@ public class Jim implements ImConst{
* @author: WChao
*/
public static boolean unbindUser(User user){
String userId = user.getUserId();
if(StringUtils.isEmpty(userId)){
log.error("userId is null");
if(Objects.isNull(user)|| StringUtils.isEmpty(user.getUserId())){
log.error("user or userId is null");
return false;
}
String userId = user.getUserId();
TioConfig tioConfig = imConfig.getTioConfig();
SetWithLock<ChannelContext> userChannels = Tio.getByUserid(tioConfig, userId);
Set<ChannelContext> channelContexts = userChannels.getObj();
@ -211,7 +289,7 @@ public class Jim implements ImConst{
try{
readLock.lock();
for (ChannelContext channelContext : channelContexts){
ImChannelContext imChannelContext = (ImChannelContext)channelContext.get(ImConst.Key.IM_CHANNEL_CONTEXT_KEY);
ImChannelContext imChannelContext = (ImChannelContext)channelContext.get(Key.IM_CHANNEL_CONTEXT_KEY);
ImUserListener imUserListener = imConfig.getImUserListener();
if(Objects.isNull(imUserListener))continue;
User existUser = imChannelContext.getSessionContext().getImClientNode().getUser();
@ -226,6 +304,7 @@ public class Jim implements ImConst{
}
return true;
}
/**
* 绑定群组(如果配置了群组监听器,执行回调)
* @param imChannelContext IM通道上下文
@ -241,37 +320,52 @@ public class Jim implements ImConst{
* @param group 绑定群组对象
*/
public static boolean bindGroup(ImChannelContext imChannelContext, Group group){
String groupId = group.getGroupId();
if(StringUtils.isEmpty(groupId)){
log.error("groupId is null");
try {
String groupId = group.getGroupId();
if(StringUtils.isEmpty(groupId)){
log.error("groupId is null");
return false;
}
Tio.bindGroup(imChannelContext.getTioChannelContext(), group.getGroupId());
}catch (Exception e){
log.error("an exception occurred in the binding group", e);
return false;
}
Tio.bindGroup(imChannelContext.getTioChannelContext(), group.getGroupId());
return true;
}
/**
* 与指定组解除绑定关系
* @param groupId
* @param imChannelContext
* @param groupId 解绑群组ID
* @param imChannelContext IM通道上下文
* @author WChao
*/
public static boolean unbindGroup(String groupId, ImChannelContext imChannelContext){
if(StringUtils.isEmpty(groupId)){
log.error("groupId is null");
try{
if(StringUtils.isEmpty(groupId)){
log.error("groupId is null");
return false;
}
Tio.unbindGroup(groupId, imChannelContext.getTioChannelContext());
}catch (Exception e){
log.error("an exception occurred in the unBinding group", e);
return false;
}
Tio.unbindGroup(groupId, imChannelContext.getTioChannelContext());
return true;
}
/**
* 与所有组解除解绑关系
* @param imChannelContext
* @param imChannelContext IM通道上下文
* @author WChao
*/
public static boolean unbindGroup(ImChannelContext imChannelContext){
Tio.unbindGroup(imChannelContext.getTioChannelContext());
try{
Tio.unbindGroup(imChannelContext.getTioChannelContext());
}catch (Exception e){
log.error("an exception occurred in the unBinding group", e);
return false;
}
return true;
}
@ -285,7 +379,12 @@ public class Jim implements ImConst{
log.error("groupId or userId is null");
return false;
}
Tio.unbindGroup(imConfig.getTioConfig(), userId, groupId);
try {
Tio.unbindGroup(imConfig.getTioConfig(), userId, groupId);
}catch (Exception e){
log.error("an exception occurred in the unBinding group", e);
return false;
}
return true;
}
@ -304,7 +403,7 @@ public class Jim implements ImConst{
try{
readLock.lock();
for(ChannelContext channelContext : channels){
ImChannelContext imChannelContext = (ImChannelContext)channelContext.get(ImConst.Key.IM_CHANNEL_CONTEXT_KEY);
ImChannelContext imChannelContext = (ImChannelContext)channelContext.get(Key.IM_CHANNEL_CONTEXT_KEY);
remove(imChannelContext, remark);
}
}finally{
@ -314,8 +413,8 @@ public class Jim implements ImConst{
/**
* 移除指定channel, 和close方法一样只不过不再进行重连等维护性的操作
* @param imChannelContext
* @param remark
* @param imChannelContext IM通道上下文
* @param remark 移除描述
*/
public static void remove(ImChannelContext imChannelContext, String remark){
Tio.remove(imChannelContext.getTioChannelContext(), remark);
@ -323,8 +422,8 @@ public class Jim implements ImConst{
/**
* 关闭连接
* @param imChannelContext
* @param remark
* @param imChannelContext IM通道上下文
* @param remark 移除描述
*/
public static void close(ImChannelContext imChannelContext, String remark){
Tio.close(imChannelContext.getTioChannelContext(), remark);
@ -360,4 +459,28 @@ public class Jim implements ImConst{
return imChannelContexts;
}
/**
* 转换协议包同时设置Packet包信息;
* @param imChannelContext IM通道上下文
* @param packet 消息包
* @return
*/
private static ImPacket convertPacket(ImChannelContext imChannelContext ,ImPacket packet){
if(Objects.isNull(imChannelContext) || Objects.isNull(packet)) {
return null;
}
try{
ImPacket respPacket = ProtocolManager.Converter.respPacket(packet, packet.getCommand(), imChannelContext);
if(respPacket == null){
log.error("convert protocol package is empty, please check the protocol");
return null;
}
respPacket.setSynSeq(packet.getSynSeq());
return respPacket;
}catch (ImException e){
log.error("convert protocol packet is abnormal, please check the protocol");
return null;
}
}
}

View File

@ -1,7 +1,7 @@
/**
*
*/
package org.jim.core.cluster.redis;
package org.jim.server.cluster.redis;
import org.jim.core.ImPacket;
import org.jim.core.cluster.ImCluster;

View File

@ -1,28 +1,24 @@
/**
*
*/
package org.jim.core.cluster.redis;
package org.jim.server.cluster.redis;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.jim.core.ImConst;
import org.jim.core.Jim;
import org.jim.core.ImPacket;
import org.jim.core.cache.redis.JedisTemplate;
import org.jim.core.cluster.ImClusterConfig;
import org.jim.core.cluster.ImClusterVO;
import org.jim.server.JimServerAPI;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.api.listener.MessageListener;
import org.redisson.api.listener.StatusListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.Tio;
import org.tio.utils.json.Json;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
/**
* @desc Redis集群配置
* @author WChao
@ -91,12 +87,12 @@ public class RedisClusterConfig extends ImClusterConfig implements ImConst {
//发送给指定组
String group = imClusterVo.getGroup();
if (StringUtils.isNotBlank(group)) {
Jim.sendToGroup(group, packet);
JimServerAPI.sendToGroup(group, packet);
}
//发送给指定用户
String userId = imClusterVo.getUserId();
if (StringUtils.isNotBlank(userId)) {
Jim.sendToUser(userId, packet);
JimServerAPI.sendToUser(userId, packet);
}
//发送给指定token
String token = imClusterVo.getToken();
@ -106,7 +102,7 @@ public class RedisClusterConfig extends ImClusterConfig implements ImConst {
//发送给指定ip
String ip = imClusterVo.getIp();
if (StringUtils.isNotBlank(ip)) {
//Jim.sendToIp(me.groupContext, ip, packet);
JimServerAPI.sendToIp(ip, packet);
}
});
return me;

View File

@ -2,7 +2,6 @@ package org.jim.server.command.handler;
import org.jim.core.ImChannelContext;
import org.jim.core.ImPacket;
import org.jim.core.Jim;
import org.jim.core.config.ImConfig;
import org.jim.core.exception.ImException;
import org.jim.core.packets.ChatBody;
@ -10,6 +9,7 @@ import org.jim.core.packets.ChatType;
import org.jim.core.packets.Command;
import org.jim.core.packets.RespBody;
import org.jim.server.ImServerChannelContext;
import org.jim.server.JimServerAPI;
import org.jim.server.command.AbstractCmdHandler;
import org.jim.server.config.ImServerConfig;
import org.jim.server.protocol.ProtocolManager;
@ -53,7 +53,7 @@ public class ChatReqHandler extends AbstractCmdHandler {
if(ChatType.CHAT_TYPE_PRIVATE.getNumber() == chatBody.getChatType()){
String toId = chatBody.getTo();
if(ChatKit.isOnline(toId, isStore)){
Jim.sendToUser(toId, ProtocolManager.Converter.respPacket(chatPacket, imServerChannelContext));
JimServerAPI.sendToUser(toId, chatPacket);
//发送成功响应包
return ProtocolManager.Packet.success(channelContext);
}else{
@ -63,7 +63,7 @@ public class ChatReqHandler extends AbstractCmdHandler {
//群聊
}else if(ChatType.CHAT_TYPE_PUBLIC.getNumber() == chatBody.getChatType()){
String groupId = chatBody.getGroupId();
Jim.sendToGroup(groupId, ProtocolManager.Converter.respPacket(chatPacket, imServerChannelContext));
JimServerAPI.sendToGroup(groupId, chatPacket);
//发送成功响应包
return ProtocolManager.Packet.success(channelContext);
}

View File

@ -1,7 +1,6 @@
package org.jim.server.command.handler;
import org.jim.core.ImChannelContext;
import org.jim.core.Jim;
import org.jim.core.ImPacket;
import org.jim.core.ImStatus;
import org.jim.core.exception.ImException;
@ -9,6 +8,7 @@ import org.jim.core.packets.CloseReqBody;
import org.jim.core.packets.Command;
import org.jim.core.packets.RespBody;
import org.jim.core.utils.JsonKit;
import org.jim.server.JimServerAPI;
import org.jim.server.command.AbstractCmdHandler;
import org.jim.server.protocol.ProtocolManager;
@ -29,12 +29,12 @@ public class CloseReqHandler extends AbstractCmdHandler
//关闭请求消息格式不正确
return ProtocolManager.Converter.respPacket(new RespBody(Command.COMMAND_CLOSE_REQ, ImStatus.C10020), imChannelContext);
}
Jim.bSend(imChannelContext, ProtocolManager.Converter.respPacket(new RespBody(Command.COMMAND_CLOSE_REQ, ImStatus.C10021), imChannelContext));
JimServerAPI.bSend(imChannelContext, ProtocolManager.Converter.respPacket(new RespBody(Command.COMMAND_CLOSE_REQ, ImStatus.C10021), imChannelContext));
if(closeReqBody == null || closeReqBody.getUserId() == null){
Jim.remove(imChannelContext, "收到关闭请求");
JimServerAPI.remove(imChannelContext, "收到关闭请求");
}else{
String userId = closeReqBody.getUserId();
Jim.remove(userId, "收到关闭请求!");
JimServerAPI.remove(userId, "收到关闭请求!");
}
return null;
}

View File

@ -1,12 +1,12 @@
package org.jim.server.command.handler;
import org.jim.core.ImChannelContext;
import org.jim.core.Jim;
import org.jim.core.ImPacket;
import org.jim.core.exception.ImException;
import org.jim.core.http.HttpRequest;
import org.jim.core.packets.Command;
import org.jim.core.ws.WsSessionContext;
import org.jim.server.JimServerAPI;
import org.jim.server.command.AbstractCmdHandler;
import org.jim.server.processor.handshake.HandshakeCmdProcessor;
@ -24,15 +24,15 @@ public class HandshakeReqHandler extends AbstractCmdHandler {
HandshakeCmdProcessor handshakeProcessor = this.getMultiProcessor(channelContext,HandshakeCmdProcessor.class);
if(Objects.isNull(handshakeProcessor)){
Jim.remove(channelContext, "没有对应的握手协议处理器HandshakeCmdProcessor...");
JimServerAPI.remove(channelContext, "没有对应的握手协议处理器HandshakeCmdProcessor...");
return null;
}
ImPacket handShakePacket = handshakeProcessor.handshake(packet, channelContext);
if (handShakePacket == null) {
Jim.remove(channelContext, "业务层不同意握手");
JimServerAPI.remove(channelContext, "业务层不同意握手");
return null;
}
Jim.send(channelContext, handShakePacket);
JimServerAPI.send(channelContext, handShakePacket);
WsSessionContext wsSessionContext = (WsSessionContext) channelContext.getSessionContext();
HttpRequest request = wsSessionContext.getHandshakeRequestPacket();
handshakeProcessor.onAfterHandshake(request, channelContext);

View File

@ -3,6 +3,7 @@ package org.jim.server.command.handler;
import org.apache.commons.lang3.StringUtils;
import org.jim.core.*;
import org.jim.core.exception.ImException;
import org.jim.server.JimServerAPI;
import org.jim.server.processor.group.GroupCmdProcessor;
import org.jim.server.protocol.ProtocolManager;
import org.slf4j.Logger;
@ -32,7 +33,7 @@ public class JoinGroupReqHandler extends AbstractCmdHandler {
String groupId = joinGroup.getGroupId();
if (StringUtils.isBlank(groupId)) {
log.error("group is null,{}", imChannelContext);
Jim.close(imChannelContext, "group is null when join group");
JimServerAPI.close(imChannelContext, "group is null when join group");
return null;
}
//实际绑定之前执行处理器动作
@ -48,7 +49,7 @@ public class JoinGroupReqHandler extends AbstractCmdHandler {
}
}
//处理完处理器内容后
Jim.bindGroup(imChannelContext, joinGroup);
JimServerAPI.bindGroup(imChannelContext, joinGroup);
return null;
}
@Override

View File

@ -5,14 +5,11 @@ import org.jim.core.*;
import org.jim.core.config.ImConfig;
import org.jim.core.exception.ImException;
import org.jim.core.message.MessageHelper;
import org.jim.core.packets.Command;
import org.jim.core.packets.Group;
import org.jim.core.packets.LoginReqBody;
import org.jim.core.packets.LoginRespBody;
import org.jim.core.packets.User;
import org.jim.core.packets.*;
import org.jim.core.protocol.IProtocol;
import org.jim.core.utils.JsonKit;
import org.jim.server.ImServerChannelContext;
import org.jim.server.JimServerAPI;
import org.jim.server.command.AbstractCmdHandler;
import org.jim.server.command.CommandManager;
import org.jim.server.processor.login.LoginCmdProcessor;
@ -41,7 +38,7 @@ public class LoginReqHandler extends AbstractCmdHandler {
User user = getUserByProcessor(imChannelContext, loginProcessor, loginReqBody, loginRespBody);
IProtocol protocol = imServerChannelContext.getProtocolHandler().getProtocol();
user.setTerminal(Objects.isNull(protocol) ? Protocol.UNKNOWN : protocol.name());
Jim.bindUser(imServerChannelContext, user);
JimServerAPI.bindUser(imServerChannelContext, user);
//初始化绑定或者解绑群组;
initGroup(imChannelContext, user);
loginProcessor.onSuccess(user, imChannelContext);
@ -59,15 +56,15 @@ public class LoginReqHandler extends AbstractCmdHandler {
*/
private User getUserByProcessor(ImChannelContext imChannelContext, LoginCmdProcessor loginProcessor, LoginReqBody loginReqBody, LoginRespBody loginRespBody)throws ImException{
if(Objects.isNull(loginProcessor)){
User user = User.newBuilder().userId(loginReqBody.getUserId()).status(ONLINE).build();
User user = User.newBuilder().userId(loginReqBody.getUserId()).status(UserStatusType.ONLINE.getStatus()).build();
return user;
}
loginRespBody = loginProcessor.doLogin(loginReqBody, imChannelContext);
if (Objects.isNull(loginRespBody) || loginRespBody.getCode() != ImStatus.C10007.getCode()) {
log.error("login failed, userId:{}, password:{}", loginReqBody.getUserId(), loginReqBody.getPassword());
loginProcessor.onFailed(imChannelContext);
Jim.bSend(imChannelContext, ProtocolManager.Converter.respPacket(loginRespBody, imChannelContext));
Jim.remove(imChannelContext, "userId or token is incorrect");
JimServerAPI.bSend(imChannelContext, ProtocolManager.Converter.respPacket(loginRespBody, imChannelContext));
JimServerAPI.remove(imChannelContext, "userId or token is incorrect");
return null;
}
return loginProcessor.getUser(loginReqBody, imChannelContext);

View File

@ -6,7 +6,7 @@ import org.jim.core.packets.Group;
import org.jim.core.packets.User;
import org.jim.core.packets.UserReqBody;
import org.jim.core.packets.UserStatusType;
import org.jim.core.utils.ImKit;
import org.jim.server.util.ImServerKit;
import java.util.ArrayList;
import java.util.List;
@ -71,13 +71,13 @@ public class NonPersistentUserInfo implements IUserInfo {
if(FRIEND_GROUP_FLAG == flag){
users = group.getUsers();
}else if(GROUP_FLAG == flag){
users = ImKit.getUsersByGroup(group.getGroupId());
users = ImServerKit.getUsersByGroup(group.getGroupId());
}
resultGroups.add(cloneGroup);
if(CollectionUtils.isEmpty(users))return;
List<User> cloneUsers = new ArrayList<User>();
users.forEach(user -> {
User onlineUser = ImKit.getUser(user.getUserId());
User onlineUser = ImServerKit.getUser(user.getUserId());
//在线
if(onlineUser != null && UserStatusType.ONLINE.getNumber() == type){
cloneUsers.add(onlineUser.clone());

View File

@ -42,10 +42,6 @@ public class ImServerConfig extends ImConfig {
* 用户消息持久化助手;
*/
private MessageHelper messageHelper;
/**
* SSL配置
*/
private SslConfig sslConfig;
/**
* http相关配置;
*/
@ -108,8 +104,6 @@ public class ImServerConfig extends ImConfig {
private String isSSL = OFF;
private SslConfig sslConfig;
private HttpConfig httpConfig;
private WsConfig wsConfig;
@ -144,11 +138,6 @@ public class ImServerConfig extends ImConfig {
return getThis();
}
public Builder sslConfig(SslConfig sslConfig){
this.sslConfig = sslConfig;
return getThis();
}
public Builder httConfig(HttpConfig httpConfig){
this.httpConfig = httpConfig;
return getThis();

View File

@ -12,6 +12,7 @@ import org.jim.core.listener.AbstractImStoreBindListener;
import org.jim.core.message.MessageHelper;
import org.jim.core.packets.Group;
import org.jim.core.packets.User;
import org.jim.core.packets.UserStatusType;
import org.jim.server.config.ImServerConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -63,7 +64,7 @@ public class RedisImStoreBindListener extends AbstractImStoreBindListener {
if(!isStore() || Objects.isNull(user)) {
return;
}
user.setStatus(ONLINE);
user.setStatus(UserStatusType.ONLINE.getStatus());
this.messageHelper.updateUserTerminal(user);
initUserInfo(user);
}
@ -73,7 +74,7 @@ public class RedisImStoreBindListener extends AbstractImStoreBindListener {
if(!isStore() || Objects.isNull(user)) {
return;
}
user.setStatus(OFFLINE);
user.setStatus(UserStatusType.OFFLINE.getStatus());
this.messageHelper.updateUserTerminal(user);
}

View File

@ -54,7 +54,7 @@ public class RedisMessageHelper extends AbstractMessageHelper{
String terminalKey = terminalKeyIterator.next();
terminalKey = terminalKey.substring(terminalKey.indexOf(userId));
String isOnline = RedisCacheManager.getCache(USER).get(terminalKey, String.class);
if(ONLINE.equals(isOnline)){
if(UserStatusType.ONLINE.getStatus().equals(isOnline)){
return true;
}
}
@ -275,9 +275,9 @@ public class RedisMessageHelper extends AbstractMessageHelper{
*/
private void validateStatusByType(Integer type, List<User> users, User user) {
String status = user.getStatus();
if(UserStatusType.ONLINE.getNumber() == type && ONLINE.equals(status)){
if(UserStatusType.ONLINE.getNumber() == type && UserStatusType.ONLINE.getStatus().equals(status)){
users.add(user);
}else if(UserStatusType.OFFLINE.getNumber() == type && OFFLINE.equals(status)){
}else if(UserStatusType.OFFLINE.getNumber() == type && UserStatusType.OFFLINE.getStatus().equals(status)){
users.add(user);
}else if(UserStatusType.ALL.getNumber() == type){
users.add(user);
@ -291,7 +291,7 @@ public class RedisMessageHelper extends AbstractMessageHelper{
return null;
}
boolean isOnline = this.isOnline(userId);
String status = isOnline ? ONLINE : OFFLINE;
String status = isOnline ? UserStatusType.ONLINE.getStatus() : UserStatusType.OFFLINE.getStatus();
if( UserStatusType.ONLINE.getNumber() == type && isOnline){
user.setStatus(status);
return user;
@ -344,9 +344,9 @@ public class RedisMessageHelper extends AbstractMessageHelper{
String userId = user.getUserId();
boolean isOnline = this.isOnline(userId);
if(isOnline){
user.setStatus(ONLINE);
user.setStatus(UserStatusType.ONLINE.getStatus());
}else{
user.setStatus(OFFLINE);
user.setStatus(UserStatusType.OFFLINE.getStatus());
}
return true;
}

View File

@ -110,48 +110,74 @@ public class ProtocolManager implements ImConst{
/**
* 功能描述[转换不同协议响应包]
* @authorWChao 创建时间: 2017年9月21日 下午3:21:54
* @param respBody
* @param channelContext
* @param respBody 响应消息体
* @param imChannelContext IM通道上下文
* @return
*
*/
public static ImPacket respPacket(RespBody respBody, ImChannelContext channelContext)throws ImException {
public static ImPacket respPacket(RespBody respBody, ImChannelContext imChannelContext)throws ImException {
if(Objects.isNull(respBody)) {
throw new ImException("响应包体不能为空!");
}
return respPacket(respBody.toByte(), respBody.getCommand(), channelContext);
return respPacket(respBody.toByte(), respBody.getCommand(), imChannelContext);
}
/**
* 功能描述[转换不同协议响应包]
* @param body 消息体字节
* @param command 命令码
* @param imChannelContext IM通道上下文
* @return
* @throws ImException
*/
public static ImPacket respPacket(byte[] body, Command command, ImChannelContext imChannelContext)throws ImException{
return getProtocolConverter(imChannelContext).RespPacket(body, command, imChannelContext);
}
/**
* 功能描述[转换不同协议响应包]
* @param imPacket 消息包
* @param imChannelContext IM通道上下文
* @return
* @throws ImException
*/
public static ImPacket respPacket(ImPacket imPacket, ImChannelContext imChannelContext)throws ImException{
return respPacket(imPacket, imPacket.getCommand(), imChannelContext);
}
/**
* 功能描述[转换不同协议响应包]
* @authorWChao 创建时间: 2017年9月21日 下午3:21:54
* @param body
* @param channelContext
* @param imPacket 消息包
* @param command 命令码
* @param imChannelContext IM通道上下文
* @return
*
*/
public static ImPacket respPacket(byte[] body, Command command, ImChannelContext channelContext)throws ImException{
ImServerChannelContext serverChannelContext = (ImServerChannelContext)channelContext;
public static ImPacket respPacket(ImPacket imPacket,Command command, ImChannelContext imChannelContext)throws ImException{
return getProtocolConverter(imChannelContext).RespPacket(imPacket, command, imChannelContext);
}
/**
* 通过通道获取当前通道协议
* @param imChannelContext IM通道上下文
* @return
* @throws ImException
*/
private static IProtocolConverter getProtocolConverter(ImChannelContext imChannelContext) throws ImException{
ImServerChannelContext serverChannelContext = (ImServerChannelContext)imChannelContext;
AbstractProtocolHandler protocolHandler = serverChannelContext.getProtocolHandler();
if(Objects.isNull(protocolHandler)){
throw new ImException("协议[ProtocolHandler]未初始化,协议包转化失败");
}
IProtocolConverter converter = protocolHandler.getProtocol().getConverter();
if(converter != null){
return converter.RespPacket(body, command, channelContext);
return converter;
}else {
throw new ImException("未获取到协议转化器[ProtocolConverter]");
}
}
public static ImPacket respPacket(ImPacket imPacket, ImChannelContext channelContext)throws ImException{
return respPacket(imPacket, imPacket.getCommand(), channelContext);
}
public static ImPacket respPacket(ImPacket imPacket,Command command, ImChannelContext channelContext)throws ImException{
return respPacket(imPacket.getBody(), command, channelContext);
}
}
public static class Packet{

View File

@ -326,7 +326,9 @@ public class DefaultHttpRequestHandler implements IHttpRequestHandler,ImConst.Ht
cookie = new Cookie(domain, name, sessionId, maxAge);
httpResponse.addCookie(cookie);
httpConfig.getSessionStore().put(sessionId, httpSession);
log.info("{} 创建会话Cookie, {}", request.getImChannelContext(), cookie);
if(log.isDebugEnabled()){
log.info("{} 创建会话Cookie, {}", request.getImChannelContext(), cookie);
}
} else {
sessionId = cookie.getValue();
HttpSession httpSession1 = (HttpSession) httpConfig.getSessionStore().get(sessionId);

View File

@ -6,7 +6,6 @@ package org.jim.server.protocol.http;
import org.jim.core.ImChannelContext;
import org.jim.core.ImConst;
import org.jim.core.ImPacket;
import org.jim.core.Jim;
import org.jim.core.config.ImConfig;
import org.jim.core.exception.ImDecodeException;
import org.jim.core.exception.ImException;
@ -14,7 +13,8 @@ import org.jim.core.http.*;
import org.jim.core.http.handler.IHttpRequestHandler;
import org.jim.core.protocol.AbstractProtocol;
import org.jim.core.session.id.impl.UUIDSessionIdGenerator;
import org.jim.server.ImServerStarter;
import org.jim.server.JimServer;
import org.jim.server.JimServerAPI;
import org.jim.server.config.ImServerConfig;
import org.jim.server.protocol.AbstractProtocolHandler;
import org.jim.server.protocol.http.mvc.Routes;
@ -59,11 +59,11 @@ public class HttpProtocolHandler extends AbstractProtocolHandler {
}
if(Objects.isNull(httpConfig.getScanPackages())){
//J-IM MVC需要扫描的根目录包
String[] scanPackages = new String[] { ImServerStarter.class.getPackage().getName() };
String[] scanPackages = new String[] { JimServer.class.getPackage().getName() };
httpConfig.setScanPackages(scanPackages);
}else{
String[] scanPackages = new String[httpConfig.getScanPackages().length+1];
scanPackages[0] = ImServerStarter.class.getPackage().getName();
scanPackages[0] = JimServer.class.getPackage().getName();
System.arraycopy(httpConfig.getScanPackages(), 0, scanPackages, 1, httpConfig.getScanPackages().length);
httpConfig.setScanPackages(scanPackages);
}
@ -84,7 +84,7 @@ public class HttpProtocolHandler extends AbstractProtocolHandler {
public void handler(ImPacket imPacket, ImChannelContext imChannelContext)throws ImException {
HttpRequest httpRequestPacket = (HttpRequest) imPacket;
HttpResponse httpResponsePacket = httpRequestHandler.handler(httpRequestPacket, httpRequestPacket.getRequestLine());
Jim.send(imChannelContext, httpResponsePacket);
JimServerAPI.send(imChannelContext, httpResponsePacket);
}
@Override

View File

@ -11,6 +11,7 @@ import org.jim.core.packets.Command;
import org.jim.core.packets.RespBody;
import org.jim.core.protocol.AbstractProtocol;
import org.jim.core.tcp.*;
import org.jim.server.JimServerAPI;
import org.jim.server.command.AbstractCmdHandler;
import org.jim.server.command.CommandManager;
import org.jim.server.config.ImServerConfig;
@ -54,12 +55,12 @@ public class TcpProtocolHandler extends AbstractProtocolHandler {
AbstractCmdHandler cmdHandler = CommandManager.getCommand(tcpPacket.getCommand());
if(cmdHandler == null){
ImPacket imPacket = new ImPacket(Command.COMMAND_UNKNOW, new RespBody(Command.COMMAND_UNKNOW,ImStatus.C10017).toByte());
Jim.send(imChannelContext, imPacket);
JimServerAPI.send(imChannelContext, imPacket);
return;
}
ImPacket response = cmdHandler.handler(tcpPacket, imChannelContext);
if(Objects.nonNull(response) && tcpPacket.getSynSeq() < 1){
Jim.send(imChannelContext, response);
JimServerAPI.send(imChannelContext, response);
}
}

View File

@ -2,15 +2,14 @@ package org.jim.server.protocol.ws;
import org.jim.core.ImChannelContext;
import org.jim.core.ImConst;
import org.jim.core.Jim;
import org.jim.core.ImPacket;
import org.jim.core.config.ImConfig;
import org.jim.core.packets.ChatBody;
import org.jim.core.ws.IWsMsgHandler;
import org.jim.core.ws.Opcode;
import org.jim.core.ws.WsRequestPacket;
import org.jim.core.ws.WsResponsePacket;
import org.jim.core.ws.WsConfig;
import org.jim.server.JimServerAPI;
import org.jim.server.config.ImServerConfig;
import org.jim.server.protocol.ProtocolManager;
import org.jim.server.util.ChatKit;
@ -42,7 +41,7 @@ public class WsMsgHandler implements IWsMsgHandler{
boolean isStore = ImServerConfig.ON.equals(imServerConfig.getIsStore());
String toId = chatBody.getTo();
if(ChatKit.isOnline(toId,isStore)){
Jim.sendToUser(toId, wsRequestPacket);
JimServerAPI.sendToUser(toId, wsRequestPacket);
ImPacket sendSuccessPacket = ProtocolManager.Packet.success(imChannelContext);
text = new String(sendSuccessPacket.getBody(), ImConst.Http.CHARSET_NAME);
}else{
@ -85,7 +84,7 @@ public class WsMsgHandler implements IWsMsgHandler{
WsResponsePacket wsResponse = null;
if (opcode == Opcode.TEXT) {
if (bytes == null || bytes.length == 0) {
Jim.remove(imChannelContext, "错误的webSocket包body为空");
JimServerAPI.remove(imChannelContext, "错误的webSocket包body为空");
return null;
}
String text = new String(bytes, wsServerConfig.getCharset());
@ -95,7 +94,7 @@ public class WsMsgHandler implements IWsMsgHandler{
return wsResponse;
} else if (opcode == Opcode.BINARY) {
if (bytes == null || bytes.length == 0) {
Jim.remove(imChannelContext, "错误的webSocket包body为空");
JimServerAPI.remove(imChannelContext, "错误的webSocket包body为空");
return null;
}
Object retObj = this.onBytes(wsRequest, bytes, imChannelContext);
@ -111,7 +110,7 @@ public class WsMsgHandler implements IWsMsgHandler{
wsResponse = processRetObj(retObj, methodName, imChannelContext);
return wsResponse;
} else {
Jim.remove(imChannelContext, "错误的webSocket包错误的Opcode");
JimServerAPI.remove(imChannelContext, "错误的webSocket包错误的Opcode");
return null;
}
}
@ -149,7 +148,7 @@ public class WsMsgHandler implements IWsMsgHandler{
}
@Override
public Object onClose(WsRequestPacket webSocketPacket, byte[] bytes, ImChannelContext imChannelContext) throws Exception {
Jim.remove(imChannelContext, "receive close flag");
JimServerAPI.remove(imChannelContext, "receive close flag");
return null;
}

View File

@ -17,6 +17,7 @@ import org.jim.core.packets.RespBody;
import org.jim.core.protocol.AbstractProtocol;
import org.jim.core.utils.JsonKit;
import org.jim.core.ws.*;
import org.jim.server.JimServerAPI;
import org.jim.server.command.AbstractCmdHandler;
import org.jim.server.command.CommandManager;
import org.jim.server.config.ImServerConfig;
@ -87,12 +88,12 @@ public class WsProtocolHandler extends AbstractProtocolHandler {
return;
}
ImPacket wsPacket = new ImPacket(Command.COMMAND_UNKNOW, new RespBody(Command.COMMAND_UNKNOW,ImStatus.C10017).toByte());
Jim.send(imChannelContext, wsPacket);
JimServerAPI.send(imChannelContext, wsPacket);
return;
}
ImPacket response = cmdHandler.handler(wsRequestPacket, imChannelContext);
if(Objects.nonNull(response)){
Jim.send(imChannelContext, response);
JimServerAPI.send(imChannelContext, response);
}
}

View File

@ -9,12 +9,12 @@ import org.apache.log4j.Logger;
import org.jim.core.ImChannelContext;
import org.jim.core.ImConst;
import org.jim.core.ImSessionContext;
import org.jim.core.Jim;
import org.jim.core.config.ImConfig;
import org.jim.core.packets.ChatBody;
import org.jim.core.packets.User;
import org.jim.core.session.id.impl.UUIDSessionIdGenerator;
import org.jim.core.utils.JsonKit;
import org.jim.server.JimServerAPI;
import org.jim.server.config.ImServerConfig;
import java.util.List;
@ -106,7 +106,7 @@ public class ChatKit {
ImServerConfig imServerConfig = ImConfig.Global.get();
return imServerConfig.getMessageHelper().isOnline(userId);
}
List<ImChannelContext> imChannelContexts = Jim.getByUserId(userId);
List<ImChannelContext> imChannelContexts = JimServerAPI.getByUserId(userId);
if(CollectionUtils.isNotEmpty(imChannelContexts)){
return true;
}

View File

@ -0,0 +1,63 @@
/**
*
*/
package org.jim.server.util;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.jim.core.ImChannelContext;
import org.jim.core.packets.User;
import org.jim.server.JimServerAPI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* IM工具类;
* @author WChao
*
*/
public class ImServerKit {
private static Logger logger = LoggerFactory.getLogger(ImServerKit.class);
/**
* 根据群组获取所有用户;
* @param groupId 群组ID
* @return 群组用户集合列表
*/
public static List<User> getUsersByGroup(String groupId){
List<ImChannelContext> channelContexts = JimServerAPI.getByGroup(groupId);
List<User> users = Lists.newArrayList();
if(CollectionUtils.isEmpty(channelContexts)){
return users;
}
Map<String,User> userMap = new HashMap<>();
channelContexts.forEach(imChannelContext -> {
User user = imChannelContext.getSessionContext().getImClientNode().getUser();
if(Objects.nonNull(user) && Objects.isNull(userMap.get(user.getUserId()))){
userMap.put(user.getUserId(), user);
users.add(user);
}
});
return users;
}
/**
* 根据用户ID获取用户信息(一个用户ID会有多端通道,默认取第一个)
* @param userId 用户ID
* @return user信息
*/
public static User getUser(String userId){
List<ImChannelContext> imChannelContexts = JimServerAPI.getByUserId(userId);
if(CollectionUtils.isEmpty(imChannelContexts)) {
return null;
}
return imChannelContexts.get(0).getSessionContext().getImClientNode().getUser();
}
}

View File

@ -30,8 +30,9 @@
<modules>
<module>jim-core</module>
<module>jim-server</module>
<module>jim-client</module>
<module>jim-server-demo</module>
<!--<module>jim-client</module>-->
<module>jim-client-demo</module>
</modules>
<properties>
<jim.version>3.0.0.v20200101-RELEASE</jim.version>
@ -93,7 +94,11 @@
<artifactId>jim-server</artifactId>
<version>${jim.version}</version>
</dependency>
<dependency>
<groupId>org.j-im</groupId>
<artifactId>jim-client</artifactId>
<version>${jim.version}</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>