99
1010import com .github .getcurrentthread .soopapi .config .SOOPChatConfig ;
1111import com .github .getcurrentthread .soopapi .connection .ConnectionManager ;
12+ import com .github .getcurrentthread .soopapi .connection .SOOPConnection ;
13+ import com .github .getcurrentthread .soopapi .exception .ConnectionException ;
1214import com .github .getcurrentthread .soopapi .model .Message ;
1315import com .github .getcurrentthread .soopapi .util .SOOPChatUtils ;
1416
@@ -19,6 +21,7 @@ public class SOOPChatClient implements AutoCloseable {
1921 private final ConnectionManager connectionManager ;
2022 private final List <IChatMessageObserver > observers ;
2123 private volatile boolean isConnected ;
24+ private volatile SOOPConnection connection ;
2225
2326 public SOOPChatClient (SOOPChatConfig config ) {
2427 this .config = validateConfig (config );
@@ -56,6 +59,11 @@ private void notifyObservers(Message message) {
5659 }
5760 }
5861
62+ /**
63+ * 채팅에 비동기적으로 연결합니다.
64+ *
65+ * @return 연결 작업을 나타내는 CompletableFuture
66+ */
5967 public CompletableFuture <Void > connectToChat () {
6068 if (isConnected ) {
6169 return CompletableFuture .completedFuture (null );
@@ -64,30 +72,89 @@ public CompletableFuture<Void> connectToChat() {
6472 return CompletableFuture .runAsync (
6573 () -> {
6674 try {
67- connectionManager
68- .connect (config , this ::notifyObservers )
69- .thenRun (() -> isConnected = true )
70- .exceptionally (
71- throwable -> {
72- LOGGER .log (
73- Level .SEVERE , "Failed to connect" , throwable );
74- return null ;
75- })
76- .join ();
75+ connection =
76+ connectionManager .connect (config , this ::notifyObservers ).join ();
77+ isConnected = true ;
7778 } catch (Exception e ) {
78- throw new CompletionException ("Failed to connect to chat" , e );
79+ LOGGER .log (Level .SEVERE , "채팅 연결 실패" , e );
80+ if (e .getCause () instanceof ConnectionException ) {
81+ throw new CompletionException (e .getCause ());
82+ } else {
83+ throw new CompletionException ("채팅 연결 실패" , e );
84+ }
7985 }
8086 });
8187 }
8288
89+ /**
90+ * 채팅 연결을 시도하고 완료될 때까지 현재 스레드를 차단합니다.
91+ *
92+ * @throws ConnectionException 연결 오류가 발생한 경우
93+ */
94+ public void connectToChattingBlocking () throws ConnectionException {
95+ try {
96+ connectToChat ().join ();
97+ } catch (CompletionException e ) {
98+ if (e .getCause () instanceof ConnectionException ) {
99+ throw (ConnectionException ) e .getCause ();
100+ } else {
101+ throw new ConnectionException ("채팅 연결 실패" , e );
102+ }
103+ }
104+ }
105+
106+ /**
107+ * 연결이 끊어진 경우 재연결을 시도합니다.
108+ *
109+ * @return 재연결 작업을 나타내는 CompletableFuture
110+ */
111+ public CompletableFuture <Void > reconnect () {
112+ if (connection == null ) {
113+ return CompletableFuture .failedFuture (
114+ new IllegalStateException ("연결이 초기화되지 않았습니다. 먼저 connectToChat을 호출하세요." ));
115+ }
116+
117+ return connection
118+ .reconnect ()
119+ .thenRun (() -> isConnected = true )
120+ .exceptionally (
121+ e -> {
122+ isConnected = false ;
123+ LOGGER .log (Level .SEVERE , "재연결 실패" , e );
124+ throw new CompletionException (e );
125+ });
126+ }
127+
128+ /**
129+ * 현재 연결 상태를 확인합니다.
130+ *
131+ * @return 연결 상태 정보를 포함하는 CompletableFuture
132+ */
133+ public CompletableFuture <ConnectionStatus > getConnectionStatus () {
134+ if (connection == null ) {
135+ return CompletableFuture .completedFuture (new ConnectionStatus (false , false , 0 ));
136+ }
137+
138+ return connection
139+ .getStatus ()
140+ .thenApply (
141+ status ->
142+ new ConnectionStatus (
143+ status .isConnected (),
144+ status .isReconnecting (),
145+ status .getRetryCount ()));
146+ }
147+
148+ /** 현재 연결을 해제합니다. */
83149 public void disconnect () {
84150 if (!isConnected ) {
85151 return ;
86152 }
87153
88154 try {
89- connectionManager .disconnect (config .getBid ());
155+ connectionManager .disconnect (config .getBid ()). join () ;
90156 isConnected = false ;
157+ connection = null ;
91158 } catch (Exception e ) {
92159 LOGGER .log (Level .WARNING , "Error during disconnect" , e );
93160 }
@@ -98,11 +165,46 @@ public void close() {
98165 disconnect ();
99166 }
100167
168+ /**
169+ * 현재 연결 상태를 반환합니다.
170+ *
171+ * @return 연결 상태
172+ */
101173 public boolean isConnected () {
102- return isConnected ;
174+ return isConnected && ( connection != null && connection . isConnected ()) ;
103175 }
104176
177+ /**
178+ * 방송인 ID를 반환합니다.
179+ *
180+ * @return 방송인 ID
181+ */
105182 public String getBid () {
106183 return config .getBid ();
107184 }
185+
186+ /** 연결 상태 정보를 제공하는 클래스 */
187+ public static class ConnectionStatus {
188+ private final boolean connected ;
189+ private final boolean reconnecting ;
190+ private final int retryCount ;
191+
192+ public ConnectionStatus (boolean connected , boolean reconnecting , int retryCount ) {
193+ this .connected = connected ;
194+ this .reconnecting = reconnecting ;
195+ this .retryCount = retryCount ;
196+ }
197+
198+ public boolean isConnected () {
199+ return connected ;
200+ }
201+
202+ public boolean isReconnecting () {
203+ return reconnecting ;
204+ }
205+
206+ public int getRetryCount () {
207+ return retryCount ;
208+ }
209+ }
108210}
0 commit comments