Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Alexandre Ducarne
ai12-othello
Commits
bd9f9bbe
Commit
bd9f9bbe
authored
Nov 20, 2019
by
alexandre.ducarne
Browse files
WebSocket implementation over Stomp
Add WebSocket support for both Client & Server. Add utils dependencies, reformat code
parent
b32d96b5
Changes
72
Hide whitespace changes
Inline
Side-by-side
README.md
View file @
bd9f9bbe
<p
align=
"center"
>
<h3
align=
"center"
>
AI20
Projet othello
</h3>
</p>
# AI12
Projet othello
## Getting Started
...
...
pom.xml
View file @
bd9f9bbe
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>
4.0.0
</modelVersion>
<parent>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-parent
</artifactId>
<version>
2.2.0.RELEASE
</version>
<relativePath
/>
</parent>
<groupId>
com.ai12
</groupId>
<artifactId>
othello
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
<name>
othello
</name>
<description>
ai12 othello game
</description>
<packaging>
jar
</packaging>
<project
xmlns
:xsi
=
"http://
www.w3.org/2001/XMLSchema-instance
"
xmlns=
"http://
maven.apache.org/POM/4.0.0
"
xsi:schemaLocation=
"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>
4.0.0
</modelVersion>
<parent>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-parent
</artifactId>
<version>
2.2.0.RELEASE
</version>
<relativePath/>
</parent>
<groupId>
com.ai12
</groupId>
<artifactId>
othello
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
<name>
othello
</name>
<description>
ai12 othello game
</description>
<packaging>
jar
</packaging>
<properties>
<java.version>
1.8
</java.version>
</properties>
<properties>
<java.version>
1.8
</java.version>
</properties>
<dependencies>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-websocket
</artifactId>
</dependency>
<dependencies>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-websocket
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-test
</artifactId>
<scope>
compile
</scope>
<exclusions>
<exclusion>
<groupId>
org.junit.vintage
</groupId>
<artifactId>
junit-vintage-engine
</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-test
</artifactId>
<scope>
compile
</scope>
<exclusions>
<exclusion>
<groupId>
org.junit.vintage
</groupId>
<artifactId>
junit-vintage-engine
</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>
io.vavr
</groupId>
<artifactId>
vavr
</artifactId>
<version>
0.10.2
</version>
</dependency>
</dependencies>
</dependencies>
<build>
<sourceDirectory>
src
</sourceDirectory>
<testSourceDirectory>
src/test
</testSourceDirectory>
<plugins>
<plugin>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
<configuration>
<mainClass>
baleine.client.ClientApp
</mainClass>
</configuration>
</plugin>
</plugins>
</build>
<build>
<sourceDirectory>
src
</sourceDirectory>
<testSourceDirectory>
src/test
</testSourceDirectory>
<plugins>
<plugin>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
<configuration>
<mainClass>
baleine.client.ClientApp
</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
src/baleine/client/ClientApp.java
View file @
bd9f9bbe
...
...
@@ -2,38 +2,43 @@ package baleine.client;
import
baleine.client.game.controller.TransApplicationController
;
import
baleine.client.game.view.TransApplicationView
;
import
baleine.client.network.WebSocketStompSessionInitializer
;
import
javafx.application.Application
;
import
javafx.scene.Scene
;
import
javafx.stage.Stage
;
/**
* Main application
*
*
* @author thomas
*/
public
class
ClientApp
extends
Application
{
private
TransApplicationView
view
;
private
TransApplicationController
controller
;
@Override
public
void
start
(
Stage
stage
)
throws
Exception
{
private
TransApplicationView
view
;
private
TransApplicationController
controller
;
view
=
new
TransApplicationView
();
controller
=
new
TransApplicationController
(
view
);
/**
* Run the client app (JavaFX)
*
* @param args additional args
* @throws Exception TODO: remove once the Websocket is initialized in the proper class
*/
public
static
void
main
(
String
[]
args
)
throws
Exception
{
// TODO: call this method right when the user connects to a server instead of here
WebSocketStompSessionInitializer
.
init
(
"localhost"
,
8080
);
launch
(
args
);
}
// Application content display
stage
.
setTitle
(
"Translator"
);
stage
.
setScene
(
new
Scene
(
view
,
600
,
400
));
stage
.
setResizable
(
false
);
stage
.
show
();
}
@Override
public
void
start
(
Stage
stage
)
{
/**
* Run
*
* @param args
*/
public
static
void
main
(
String
[]
args
)
{
launch
(
args
);
}
view
=
new
TransApplicationView
();
controller
=
new
TransApplicationController
(
view
);
// Application content display
stage
.
setTitle
(
"Translator"
);
stage
.
setScene
(
new
Scene
(
view
,
600
,
400
));
stage
.
setResizable
(
false
);
stage
.
show
();
}
}
src/baleine/client/main/.gitkeep
deleted
100644 → 0
View file @
b32d96b5
src/baleine/client/network/.gitkeep
deleted
100644 → 0
View file @
b32d96b5
src/baleine/client/network/MessageController.java
0 → 100644
View file @
bd9f9bbe
package
baleine.client.network
;
import
baleine.client.network.messages.clientToServer.LoginUserToServerMessage
;
import
java.util.Optional
;
/**
* Class handling message sending & receiving
*/
public
class
MessageController
{
private
static
MessageController
instance
=
new
MessageController
();
private
WebSocketStompSessionHandler
wsHandler
;
private
MessageController
()
{
}
/**
* Get the unique instance of this class
*
* @return the instance of MessageController
*/
public
static
MessageController
getInstance
()
{
return
Optional
.
of
(
instance
).
orElseGet
(
MessageController:
:
new
);
}
/**
* Set the new WebSocket handler to use for messaging
*
* @param wsHandler the WebSocket handler to use
*/
void
setWsHandler
(
WebSocketStompSessionHandler
wsHandler
)
{
this
.
wsHandler
=
wsHandler
;
}
/**
* Send a LoginUserToServerMessage to the server
*
* @param message the message to send
*/
public
void
sendLoginUserToServer
(
LoginUserToServerMessage
message
)
{
wsHandler
.
sendMessage
(
message
);
}
}
src/baleine/client/network/WebSocketStompSessionHandler.java
0 → 100644
View file @
bd9f9bbe
package
baleine.client.network
;
import
baleine.common.message.MessageMeta
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.Logger
;
import
org.springframework.messaging.simp.stomp.StompCommand
;
import
org.springframework.messaging.simp.stomp.StompHeaders
;
import
org.springframework.messaging.simp.stomp.StompSession
;
import
org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter
;
import
java.lang.reflect.Type
;
/**
* WebSocket message handler
*/
public
class
WebSocketStompSessionHandler
extends
StompSessionHandlerAdapter
{
public
static
final
String
RECEIVE_MESSAGE_TOPIC
=
"/topic/clientMessages"
;
public
static
final
String
SEND_MESSAGE_TOPIC
=
"/topic/serverMessages"
;
private
static
final
Logger
LOGGER
=
LogManager
.
getLogger
(
WebSocketStompSessionHandler
.
class
);
private
StompSession
stompSession
;
/**
* Send a message to the server
*
* @param message the message to be sent
*/
public
void
sendMessage
(
MessageMeta
message
)
{
stompSession
.
send
(
SEND_MESSAGE_TOPIC
,
message
);
LOGGER
.
info
(
"Message sent to server : "
+
message
.
toString
());
}
/**
* Set the session and subscribe to a topic after handshake
*
* @param session the session opened between server and client
* @param connectedHeaders the StomHeaders of the session
*/
@Override
public
void
afterConnected
(
StompSession
session
,
StompHeaders
connectedHeaders
)
{
stompSession
=
session
;
MessageController
.
getInstance
().
setWsHandler
(
this
);
LOGGER
.
info
(
"New session established : "
+
session
.
getSessionId
());
stompSession
.
subscribe
(
RECEIVE_MESSAGE_TOPIC
,
this
);
LOGGER
.
info
(
"Subscribed to "
+
RECEIVE_MESSAGE_TOPIC
);
}
/**
* Log an error when an exception is raised
*
* @param session the current session
* @param command the current StompCommand
* @param headers the current StompHeaders
* @param payload the current payload
* @param exception the raised exception
*/
@Override
public
void
handleException
(
StompSession
session
,
StompCommand
command
,
StompHeaders
headers
,
byte
[]
payload
,
Throwable
exception
)
{
LOGGER
.
error
(
"Got an exception"
,
exception
);
}
/**
* Get the payload for serialization/deserialization with Jackson API
*
* @param headers the StompHeaders
* @return the type of the class to serialize/deserialize
*/
@Override
public
Type
getPayloadType
(
StompHeaders
headers
)
{
return
MessageMeta
.
class
;
}
/**
* Process the message received from the server
* Triggered when the client app receives a message from the server
*
* @param headers the current StompHeaders
* @param payload the message sent by the server
*/
@Override
public
void
handleFrame
(
StompHeaders
headers
,
Object
payload
)
{
MessageMeta
message
=
(
MessageMeta
)
payload
;
LOGGER
.
info
(
"New server message received : "
+
message
.
toString
());
message
.
processData
();
}
}
src/baleine/client/network/WebSocketStompSessionInitializer.java
0 → 100644
View file @
bd9f9bbe
package
baleine.client.network
;
import
org.springframework.messaging.converter.MappingJackson2MessageConverter
;
import
org.springframework.messaging.simp.stomp.StompSession
;
import
org.springframework.util.concurrent.ListenableFuture
;
import
org.springframework.web.socket.WebSocketHttpHeaders
;
import
org.springframework.web.socket.client.standard.StandardWebSocketClient
;
import
org.springframework.web.socket.messaging.WebSocketStompClient
;
import
org.springframework.web.socket.sockjs.client.SockJsClient
;
import
org.springframework.web.socket.sockjs.client.Transport
;
import
org.springframework.web.socket.sockjs.client.WebSocketTransport
;
import
org.springframework.web.socket.sockjs.frame.Jackson2SockJsMessageCodec
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.concurrent.ExecutionException
;
/**
* Class responsible for the first connection and handshake between the server and client
*/
public
class
WebSocketStompSessionInitializer
{
private
final
static
WebSocketHttpHeaders
headers
=
new
WebSocketHttpHeaders
();
/**
* Open a new WebSocket connection over Stomp
*
* @param hostname the hostname of the server
* @param port the port of the server
* @throws ExecutionException Exception thrown when attempting to retrieve the result of a task that aborted by throwing an exception
* @throws InterruptedException Exception thrown when the server closes the connection
*/
public
static
void
init
(
String
hostname
,
Integer
port
)
throws
ExecutionException
,
InterruptedException
{
WebSocketStompSessionInitializer
webSocketStompSessionInitializer
=
new
WebSocketStompSessionInitializer
();
ListenableFuture
<
StompSession
>
session
=
webSocketStompSessionInitializer
.
connect
(
hostname
,
port
);
session
.
get
();
}
/**
* Set the configuration to be used for messaging
*
* @param hostname the hostname of the server
* @param port the port of the server
* @return a listenable future list containing the newly opened stomp session
*/
private
ListenableFuture
<
StompSession
>
connect
(
String
hostname
,
Integer
port
)
{
List
<
Transport
>
transports
=
new
ArrayList
<>(
1
);
transports
.
add
(
new
WebSocketTransport
(
new
StandardWebSocketClient
()));
SockJsClient
sockJsClient
=
new
SockJsClient
(
transports
);
sockJsClient
.
setMessageCodec
(
new
Jackson2SockJsMessageCodec
());
WebSocketStompClient
stompClient
=
new
WebSocketStompClient
(
sockJsClient
);
stompClient
.
setMessageConverter
(
new
MappingJackson2MessageConverter
());
String
url
=
"ws://{host}:{port}/messages"
;
return
stompClient
.
connect
(
url
,
headers
,
new
WebSocketStompSessionHandler
(),
hostname
,
port
);
}
}
src/baleine/client/network/messages/clientToServer/ChatMessageToServerMessage.java
View file @
bd9f9bbe
package
baleine.client.network.messages.clientToServer
;
import
baleine.common.message.IMessageMeta
;
import
baleine.common.message.MessageMeta
;
import
java.sql.Timestamp
;
import
java.util.UUID
;
/**
* Message containing a chat message to send to the server.
*/
public
class
ChatMessageToServerMessage
implements
IMessageMeta
{
public
class
ChatMessageToServerMessage
extends
MessageMeta
{
private
String
message
;
private
Timestamp
time
;
private
UUID
playerID
;
private
UUID
gameID
;
public
ChatMessageToServerMessage
(
String
message
,
Timestamp
time
,
UUID
playerID
,
UUID
gameID
)
{
this
.
message
=
message
;
this
.
time
=
time
;
this
.
playerID
=
playerID
;
this
.
gameID
=
gameID
;
}
public
ChatMessageToServerMessage
()
{
}
@Override
public
void
processData
()
{
// TODO: Implement
}
public
String
getMessage
()
{
return
message
;
}
public
void
setMessage
(
String
message
)
{
this
.
message
=
message
;
}
public
Timestamp
getTime
()
{
return
time
;
}
public
void
setTime
(
Timestamp
time
)
{
this
.
time
=
time
;
}
public
UUID
getPlayerID
()
{
return
playerID
;
}
@Override
public
void
processData
()
{
public
void
setPlayerID
(
UUID
playerID
)
{
this
.
playerID
=
playerID
;
}
}
public
UUID
getGameID
()
{
return
gameID
;
}
public
void
setGameID
(
UUID
gameID
)
{
this
.
gameID
=
gameID
;
}
}
src/baleine/client/network/messages/clientToServer/DisconnectionRequestMessage.java
View file @
bd9f9bbe
package
baleine.client.network.messages.clientToServer
;
import
baleine.common.message.IMessageMeta
;
import
baleine.common.message.MessageMeta
;
import
java.util.UUID
;
/**
* Message containing a disconnection request to send to the server.
*/
public
class
DisconnectionRequestMessage
implements
IMessageMeta
{
public
class
DisconnectionRequestMessage
extends
MessageMeta
{
private
UUID
playerAskingID
;
public
DisconnectionRequestMessage
(
UUID
playerAskingID
)
{
this
.
playerAskingID
=
playerAskingID
;
}
public
DisconnectionRequestMessage
()
{
}
@Override
public
void
processData
()
{
@Override
public
void
processData
()
{
}
}
public
UUID
getPlayerAskingID
()
{
return
playerAskingID
;
}
public
void
setPlayerAskingID
(
UUID
playerAskingID
)
{
this
.
playerAskingID
=
playerAskingID
;
}
}
src/baleine/client/network/messages/clientToServer/GameSaveRequestMessage.java
View file @
bd9f9bbe
package
baleine.client.network.messages.clientToServer
;
import
baleine.common.message.IMessageMeta
;
import
baleine.common.message.MessageMeta
;
import
java.util.UUID
;
/**
* Message containing a game save request.
*/
public
class
GameSaveRequestMessage
implements
IMessageMeta
{
public
class
GameSaveRequestMessage
extends
MessageMeta
{
private
UUID
gameID
;
private
UUID
playerID
;
public
GameSaveRequestMessage
(
UUID
gameID
,
UUID
playerID
)
{
this
.
gameID
=
gameID
;
this
.
playerID
=
playerID
;
}
public
GameSaveRequestMessage
()
{
}
@Override
public
void
processData
()
{
// TODO: Implement
}
public
UUID
getGameID
()
{
return
gameID
;
}
@Override
public
void
processData
()
{
public
void
setGameID
(
UUID
gameID
)
{
this
.
gameID
=
gameID
;
}
}
public
UUID
getPlayerID
()
{
return
playerID
;
}
public
void
setPlayerID
(
UUID
playerID
)
{
this
.
playerID
=
playerID
;
}
}
src/baleine/client/network/messages/clientToServer/JoinRequestAnswerMessage.java
View file @
bd9f9bbe
package
baleine.client.network.messages.clientToServer
;
import
baleine.common.message.IMessageMeta
;
import
baleine.common.message.MessageMeta
;
import
java.util.UUID
;
/**
* Message containing the answer to a join request.
*/
public
class
JoinRequestAnswerMessage
implements
IMessageMeta
{
public
class
JoinRequestAnswerMessage
extends
MessageMeta
{
private
UUID
playerAskingID
;
private
UUID
gameAskedID
;
private
Boolean
answer
;
public
JoinRequestAnswerMessage
(
UUID
playerAskingID
,
UUID
gameAskedID
,
Boolean
answer
)
{
this
.
playerAskingID
=
playerAskingID
;
this
.
gameAskedID
=
gameAskedID
;
this
.
answer
=
answer
;
}
public
JoinRequestAnswerMessage
()
{
}
@Override
public
void
processData
()
{
// TODO: Implement
}
public
UUID
getPlayerAskingID
()
{
return
playerAskingID
;
}
public
void
setPlayerAskingID
(
UUID
playerAskingID
)
{
this
.
playerAskingID
=
playerAskingID
;
}
public
UUID
getGameAskedID
()
{
return
gameAskedID
;
}
public
void
setGameAskedID
(
UUID
gameAskedID
)
{
this
.
gameAskedID
=
gameAskedID
;
}
@Override
public
void
processData
()
{
public
Boolean
getAnswer
()
{
return
answer
;
}
}
public
void
setAnswer
(
Boolean
answer
)
{
this
.
answer
=
answer
;