Skip to content

Commit

Permalink
Merge pull request #148 from crossoverJie/feature/client-sdk
Browse files Browse the repository at this point in the history
Client SDK
  • Loading branch information
crossoverJie authored Sep 16, 2024
2 parents 8703c9c + 4c149f8 commit 3c4f917
Show file tree
Hide file tree
Showing 91 changed files with 1,931 additions and 1,657 deletions.
51 changes: 51 additions & 0 deletions cim-client-sdk/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.crossoverjie.netty</groupId>
<artifactId>cim</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>

<artifactId>cim-client-sdk</artifactId>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>


<dependencies>
<dependency>
<groupId>com.crossoverjie.netty</groupId>
<artifactId>cim-common</artifactId>
</dependency>

<dependency>
<groupId>com.crossoverjie.netty</groupId>
<artifactId>cim-rout-api</artifactId>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.crossoverjie.netty</groupId>
<artifactId>cim-integration-test</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.crossoverjie.cim.client.sdk;

import com.crossoverjie.cim.client.sdk.impl.ClientBuilderImpl;
import com.crossoverjie.cim.client.sdk.impl.ClientConfigurationData;
import com.crossoverjie.cim.common.pojo.CIMUserInfo;
import com.crossoverjie.cim.route.api.vo.req.P2PReqVO;
import com.crossoverjie.cim.route.api.vo.res.CIMServerResVO;
import java.io.Closeable;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;

public interface Client extends Closeable {

static ClientBuilder builder() {
return new ClientBuilderImpl();
}

default void sendP2P(P2PReqVO p2PReqVO) throws Exception{
sendP2PAsync(p2PReqVO).get();
};

CompletableFuture<Void> sendP2PAsync(P2PReqVO p2PReqVO);

default void sendGroup(String msg) throws Exception{
sendGroupAsync(msg).get();
};

CompletableFuture<Void> sendGroupAsync(String msg);

ClientState.State getState();

ClientConfigurationData.Auth getAuth();

Set<CIMUserInfo> getOnlineUser() throws Exception;

Optional<CIMServerResVO> getServerInfo();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.crossoverjie.cim.client.sdk;

import com.crossoverjie.cim.client.sdk.impl.ClientConfigurationData;
import com.crossoverjie.cim.client.sdk.io.MessageListener;
import com.crossoverjie.cim.client.sdk.io.ReconnectCheck;
import java.util.concurrent.ThreadPoolExecutor;
import okhttp3.OkHttpClient;

/**
* @author crossoverJie
*/
public interface ClientBuilder {

Client build();
ClientBuilder auth(ClientConfigurationData.Auth auth);
ClientBuilder routeUrl(String routeUrl);
ClientBuilder loginRetryCount(int loginRetryCount);
ClientBuilder event(Event event);
ClientBuilder reconnectCheck(ReconnectCheck reconnectCheck);
ClientBuilder okHttpClient(OkHttpClient okHttpClient);
ClientBuilder messageListener(MessageListener messageListener);
ClientBuilder callbackThreadPool(ThreadPoolExecutor callbackThreadPool);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.crossoverjie.cim.client.sdk;

import java.util.concurrent.atomic.AtomicReference;

public abstract class ClientState {

private static final AtomicReference<State> STATE = new AtomicReference<>(State.Initialized);

public enum State {
/**
* Client state
*/
Initialized, Reconnecting, Ready, Closed, Failed
}

public void setState(State s) {
STATE.set(s);
}

public State getState() {
return STATE.get();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.crossoverjie.cim.client.sdk;

public interface Event {
void debug(String msg, Object... replace);
void info(String msg, Object... replace);
void warn(String msg, Object... replace);
void error(String msg, Object... replace);
void fatal(Client client);

class DefaultEvent implements Event {
@Override
public void debug(String msg, Object... replace) {
System.out.println(msg);
}

@Override
public void info(String msg, Object... replace) {
System.out.println(msg);
}

@Override
public void warn(String msg, Object... replace) {
System.out.println(msg);
}

@Override
public void error(String msg, Object... replace) {
System.err.println(msg);
}

@Override
public void fatal(Client client) {

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.crossoverjie.cim.client.sdk;

import com.crossoverjie.cim.client.sdk.impl.ClientImpl;
import com.crossoverjie.cim.common.kit.HeartBeatHandler;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.netty.channel.ChannelHandlerContext;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

public final class ReConnectManager {

private ScheduledExecutorService scheduledExecutorService;

/**
* Trigger reconnect job
*
* @param ctx
*/
public void reConnect(ChannelHandlerContext ctx) {
buildExecutor();
scheduledExecutorService.scheduleAtFixedRate(() -> {
try {
ClientImpl.getClient().getHeartBeatHandler().process(ctx);
} catch (Exception e) {
ClientImpl.getClient().getConf().getEvent().error("ReConnectManager reConnect error", e);
}
},
0, 10, TimeUnit.SECONDS);
}

/**
* Close reconnect job if reconnect success.
*/
public void reConnectSuccess() {
scheduledExecutorService.shutdown();
}


/***
* build a thread executor
*/
private void buildExecutor() {
if (scheduledExecutorService == null || scheduledExecutorService.isShutdown()) {
ThreadFactory factory = new ThreadFactoryBuilder()
.setNameFormat("reConnect-job-%d")
.setDaemon(true)
.build();
scheduledExecutorService = new ScheduledThreadPoolExecutor(1, factory);
}
}

private static class ClientHeartBeatHandle implements HeartBeatHandler {

@Override
public void process(ChannelHandlerContext ctx) throws Exception {
ClientImpl.getClient().reconnect();
}
}

public static ReConnectManager createReConnectManager() {
return new ReConnectManager();
}

public static HeartBeatHandler createHeartBeatHandler() {
return new ClientHeartBeatHandle();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.crossoverjie.cim.client.sdk;

import com.crossoverjie.cim.client.sdk.impl.ClientImpl;
import com.crossoverjie.cim.common.core.proxy.RpcProxyManager;
import com.crossoverjie.cim.common.enums.StatusEnum;
import com.crossoverjie.cim.common.exception.CIMException;
import com.crossoverjie.cim.common.pojo.CIMUserInfo;
import com.crossoverjie.cim.common.res.BaseResponse;
import com.crossoverjie.cim.common.res.NULLBody;
import com.crossoverjie.cim.route.api.RouteApi;
import com.crossoverjie.cim.route.api.vo.req.ChatReqVO;
import com.crossoverjie.cim.route.api.vo.req.LoginReqVO;
import com.crossoverjie.cim.route.api.vo.req.P2PReqVO;
import com.crossoverjie.cim.route.api.vo.res.CIMServerResVO;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import okhttp3.OkHttpClient;

public class RouteManager {


private final RouteApi routeApi;
private final Event event;

public RouteManager(String routeUrl, OkHttpClient okHttpClient, Event event) {
routeApi = RpcProxyManager.create(RouteApi.class, routeUrl, okHttpClient);
this.event = event;
}

public CIMServerResVO getServer(LoginReqVO loginReqVO) throws Exception {
BaseResponse<CIMServerResVO> cimServerResVO = routeApi.login(loginReqVO);

// repeat fail
if (!cimServerResVO.getCode().equals(StatusEnum.SUCCESS.getCode())) {
event.info(cimServerResVO.getMessage());

// when client in Reconnecting state, could exit.
if (ClientImpl.getClient().getState() == ClientState.State.Reconnecting) {
event.warn("###{}###", StatusEnum.RECONNECT_FAIL.getMessage());
throw new CIMException(StatusEnum.RECONNECT_FAIL);
}
}
return cimServerResVO.getDataBody();
}

public CompletableFuture<Void> sendP2P(CompletableFuture<Void> future, P2PReqVO p2PReqVO) {
return CompletableFuture.runAsync(() -> {
try {
BaseResponse<NULLBody> response = routeApi.p2pRoute(p2PReqVO);
if (response.getCode().equals(StatusEnum.OFF_LINE.getCode())) {
future.completeExceptionally(new CIMException(StatusEnum.OFF_LINE));
}
future.complete(null);
} catch (Exception e) {
future.completeExceptionally(e);
event.error("send p2p msg error", e);
}
});
}

public CompletableFuture<Void> sendGroupMsg(ChatReqVO chatReqVO) {
return CompletableFuture.runAsync(() -> {
try {
routeApi.groupRoute(chatReqVO);
} catch (Exception e) {
event.error("send group msg error", e);
}
});
}

public void offLine(Long userId) {
ChatReqVO vo = new ChatReqVO(userId, "offLine");
routeApi.offLine(vo);
}

public Set<CIMUserInfo> onlineUser() throws Exception {
BaseResponse<Set<CIMUserInfo>> onlineUsersResVO = routeApi.onlineUser();
return onlineUsersResVO.getDataBody();
}
}
Loading

0 comments on commit 3c4f917

Please sign in to comment.