Setup channel model and join a channel
This commit is contained in:
parent
a6ec8e5676
commit
06bc4d5451
13 changed files with 364 additions and 71 deletions
|
@ -1,11 +1,13 @@
|
|||
package fr.univ.lyon1.client;
|
||||
|
||||
import fr.univ.lyon1.common.Message;
|
||||
import fr.univ.lyon1.common.User;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.List;
|
||||
|
||||
public class Client {
|
||||
private final int port;
|
||||
|
@ -15,12 +17,16 @@ public class Client {
|
|||
private ObjectInputStream in;
|
||||
protected boolean started = false;
|
||||
|
||||
public Client(String address, int port, String uuid, String password) throws IOException, InterruptedException, Exception {
|
||||
public Client(String address, int port, String uuid, String password) throws Exception {
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
socket = new Socket(address, port);
|
||||
out = new ObjectOutputStream(socket.getOutputStream());
|
||||
while (!this.auth(uuid, password));
|
||||
out.writeObject("listUsers");
|
||||
out.flush();
|
||||
out.writeObject("join general");
|
||||
out.flush();
|
||||
}
|
||||
|
||||
public boolean auth(String uuid, String password) throws IOException {
|
||||
|
@ -52,10 +58,9 @@ public class Client {
|
|||
}
|
||||
|
||||
public String sendMessage(String content) {
|
||||
Message msg = new Message(null, content);
|
||||
|
||||
try {
|
||||
out.writeObject(msg);
|
||||
out.writeObject(new Message(content));
|
||||
out.flush();
|
||||
} catch (IOException e) {
|
||||
System.err.println("Fail to send message !");
|
||||
|
@ -71,9 +76,24 @@ public class Client {
|
|||
return msg;
|
||||
}
|
||||
|
||||
public void action(Object data) {
|
||||
if (data instanceof Message)
|
||||
messageReceived((Message) data);
|
||||
else if (data instanceof List) {
|
||||
List<Object> tmpList = (List<Object>) data;
|
||||
if (tmpList.get(0) instanceof User) {
|
||||
List<User> users = (List<User>) data;
|
||||
for (User u : users) {
|
||||
System.out.println(u);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void run() throws InterruptedException, IOException {
|
||||
if (started)
|
||||
return;
|
||||
|
||||
Thread clientSendThread = new Thread(new ClientSend(this, out, socket));
|
||||
clientSendThread.start();
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package fr.univ.lyon1.client;
|
||||
|
||||
import fr.univ.lyon1.common.Message;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.net.Socket;
|
||||
|
@ -27,15 +25,15 @@ public class ClientReceive implements Runnable {
|
|||
}
|
||||
|
||||
while(true) {
|
||||
Message msg;
|
||||
Object data;
|
||||
try {
|
||||
msg = (Message) in.readObject();
|
||||
data = in.readObject();
|
||||
} catch (ClassNotFoundException|IOException e) {
|
||||
if (e instanceof SocketException) {
|
||||
System.err.println("Connexion closed");
|
||||
break;
|
||||
}
|
||||
System.err.println("Fail to read message object !");
|
||||
System.err.println("Fail to read object !");
|
||||
e.printStackTrace();
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
|
@ -45,10 +43,10 @@ public class ClientReceive implements Runnable {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (msg == null)
|
||||
if (data == null)
|
||||
break;
|
||||
|
||||
this.client.messageReceived(msg);
|
||||
this.client.action(data);
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
32
src/fr/univ/lyon1/common/Channel.java
Normal file
32
src/fr/univ/lyon1/common/Channel.java
Normal file
|
@ -0,0 +1,32 @@
|
|||
package fr.univ.lyon1.common;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.UUID;
|
||||
|
||||
public class Channel implements Serializable {
|
||||
private final UUID uuid;
|
||||
private String name;
|
||||
|
||||
public Channel(UUID uuid, String name) {
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Channel(String name) {
|
||||
this.uuid = UUID.randomUUID();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -4,27 +4,43 @@ import java.io.Serializable;
|
|||
import java.util.UUID;
|
||||
|
||||
public class Message implements Serializable {
|
||||
private Channel channel;
|
||||
private User sender;
|
||||
private final String content;
|
||||
private final UUID uuid;
|
||||
|
||||
|
||||
public Message(User sender, String content) {
|
||||
public Message(Channel channel, User sender, String content) {
|
||||
this.uuid = UUID.randomUUID();
|
||||
this.channel = channel;
|
||||
this.sender = sender;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public Message(UUID uuid, User sender, String content) {
|
||||
public Message(UUID uuid, Channel channel, User sender, String content) {
|
||||
this.uuid = uuid;
|
||||
this.channel = channel;
|
||||
this.sender = sender;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public Message(String content) {
|
||||
this.uuid = UUID.randomUUID();
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public Message repley(User user, String content) {
|
||||
return new Message(this.channel, user, content);
|
||||
}
|
||||
|
||||
public void setSender(User sender) {
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
public Channel getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public User getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
@ -35,6 +51,9 @@ public class Message implements Serializable {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return sender + ": " + content;
|
||||
if (channel != null)
|
||||
return "#"+channel+" "+sender+": "+content;
|
||||
else
|
||||
return sender + ": " + content;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,7 @@
|
|||
package fr.univ.lyon1.common;
|
||||
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import java.io.Serializable;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.KeySpec;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
public class User implements Serializable {
|
||||
private final UUID uuid;
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
package fr.univ.lyon1.common.channel;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class Channel {
|
||||
private final UUID uuid;
|
||||
private String name;
|
||||
|
||||
public Channel(UUID uuid, String name) {
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package fr.univ.lyon1.common.channel;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class PrivateChannel extends Channel {
|
||||
|
||||
public PrivateChannel(UUID uuid, String name) {
|
||||
super(uuid, name);
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package fr.univ.lyon1.common.channel;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class PublicChannel extends Channel {
|
||||
public PublicChannel(UUID uuid, String name) {
|
||||
super(uuid, name);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
package fr.univ.lyon1.server;
|
||||
|
||||
import fr.univ.lyon1.common.Channel;
|
||||
import fr.univ.lyon1.common.Message;
|
||||
import fr.univ.lyon1.common.User;
|
||||
import fr.univ.lyon1.server.models.ChannelModel;
|
||||
import fr.univ.lyon1.server.models.UserModel;
|
||||
|
||||
import java.io.EOFException;
|
||||
|
@ -9,6 +11,7 @@ import java.io.IOException;
|
|||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.List;
|
||||
|
||||
public class ConnectedClient implements Runnable {
|
||||
private static int idCounter = 0;
|
||||
|
@ -17,7 +20,7 @@ public class ConnectedClient implements Runnable {
|
|||
private final Socket socket;
|
||||
private final ObjectOutputStream out;
|
||||
private ObjectInputStream in;
|
||||
private User user;
|
||||
private UserModel user;
|
||||
|
||||
ConnectedClient(Server server, Socket socket) throws IOException {
|
||||
this.server = server;
|
||||
|
@ -66,16 +69,66 @@ public class ConnectedClient implements Runnable {
|
|||
return message;
|
||||
}
|
||||
|
||||
private void actionMessage(Object data) throws IOException, ClassNotFoundException {
|
||||
Message msg = (Message) data;
|
||||
|
||||
if (msg.getContent().startsWith("/"))
|
||||
command(msg);
|
||||
else {
|
||||
msg.setSender(this.user);
|
||||
server.broadcastMessage(msg, id);
|
||||
}
|
||||
}
|
||||
|
||||
private void command(Message msg) throws IOException {
|
||||
String content = msg.getContent();
|
||||
if (content.equals("/list")) {
|
||||
List<User> users = server.getUsers();
|
||||
sendMessage(msg.repley(Server.getServerUser(), "Total: "+users.toArray().length + "\n" + String.join(", ", users.stream().map(User::getUsername).toList())));
|
||||
} else if (content.startsWith("/join"))
|
||||
actionJoinChannel(content);
|
||||
}
|
||||
|
||||
private void actionListUsers() throws IOException {
|
||||
out.writeObject(server.getUsers());
|
||||
out.flush();
|
||||
}
|
||||
|
||||
private void actionJoinChannel(String msg) throws IOException {
|
||||
String name = msg.replaceAll("^(/?join) ", "");
|
||||
System.out.println(name);
|
||||
ChannelModel chan = ChannelModel.get(name);
|
||||
|
||||
if (chan == null) {
|
||||
chan = new ChannelModel(name);
|
||||
chan.addUser(user);
|
||||
} else
|
||||
if (!chan.have(user))
|
||||
chan.addUser(user);
|
||||
|
||||
out.writeObject(chan);
|
||||
out.flush();
|
||||
|
||||
server.broadcastMessage(new Message(chan, Server.getServerUser(), user.getUsername()+" joined the channel !"), -1);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
Message msg = (Message) in.readObject();
|
||||
|
||||
if (msg == null)
|
||||
Object data = in.readObject();
|
||||
if (data == null)
|
||||
break;
|
||||
|
||||
msg.setSender(this.user);
|
||||
server.broadcastMessage(msg, id);
|
||||
if (data instanceof Message)
|
||||
actionMessage(data);
|
||||
else if (data instanceof String) {
|
||||
String msg = (String) data;
|
||||
if (data.equals("listUsers"))
|
||||
actionListUsers();
|
||||
else if (((String) data).startsWith("join"))
|
||||
actionJoinChannel(msg);
|
||||
}
|
||||
}
|
||||
} catch (IOException | ClassNotFoundException e) {
|
||||
if (!(e instanceof EOFException)) {
|
||||
|
@ -97,4 +150,8 @@ public class ConnectedClient implements Runnable {
|
|||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package fr.univ.lyon1.server;
|
|||
|
||||
import fr.univ.lyon1.common.Message;
|
||||
import fr.univ.lyon1.common.User;
|
||||
import fr.univ.lyon1.server.models.UserChannelModel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
@ -21,19 +22,16 @@ public class Server {
|
|||
}
|
||||
|
||||
public ConnectedClient addClient(ConnectedClient newClient) {
|
||||
Message msg = new Message( serverUser, newClient.getId() + " is connected !");
|
||||
|
||||
clients.add(newClient);
|
||||
|
||||
broadcastMessage(msg, -1);
|
||||
|
||||
System.out.println("Client "+newClient.getId()+" is connected !");
|
||||
System.out.println("Client "+newClient.getUser().getUsername()+" is connected !");
|
||||
|
||||
return newClient;
|
||||
}
|
||||
|
||||
public int broadcastMessage(Message message, int id) {
|
||||
for (ConnectedClient client : clients) {
|
||||
List<UUID> users = UserChannelModel.getUsers(message.getChannel()).stream().map(User::getUUID).toList();
|
||||
for (ConnectedClient client : clients.stream().filter(connectedClient -> users.contains(connectedClient.getUser().getUUID())).toList()) {
|
||||
if (id == -1 || client.getId() != id)
|
||||
try {
|
||||
client.sendMessage(message);
|
||||
|
@ -55,10 +53,6 @@ public class Server {
|
|||
|
||||
clients.remove(client);
|
||||
|
||||
Message msg = new Message(serverUser, "Client "+client.getId()+" is disconnected");
|
||||
|
||||
broadcastMessage(msg, -1);
|
||||
|
||||
System.out.println("Client "+client.getId()+" disconnected");
|
||||
return client;
|
||||
}
|
||||
|
@ -66,4 +60,12 @@ public class Server {
|
|||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public static User getServerUser() {
|
||||
return serverUser;
|
||||
}
|
||||
|
||||
public List<User> getUsers() {
|
||||
return clients.stream().map(ConnectedClient::getUser).toList();
|
||||
}
|
||||
}
|
||||
|
|
112
src/fr/univ/lyon1/server/models/ChannelModel.java
Normal file
112
src/fr/univ/lyon1/server/models/ChannelModel.java
Normal file
|
@ -0,0 +1,112 @@
|
|||
package fr.univ.lyon1.server.models;
|
||||
|
||||
import fr.univ.lyon1.common.Channel;
|
||||
import fr.univ.lyon1.common.User;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ChannelModel extends Channel implements Model {
|
||||
private static final String TABLE_NAME = "Channel";
|
||||
|
||||
public ChannelModel(String name) {
|
||||
super(name);
|
||||
create();
|
||||
}
|
||||
|
||||
private ChannelModel(UUID uuid, String name) {
|
||||
super(uuid, name);
|
||||
}
|
||||
|
||||
public void addUser(User user) {
|
||||
new UserChannelModel(user, this);
|
||||
}
|
||||
|
||||
public boolean have(User user) {
|
||||
return UserChannelModel.exist(user, this);
|
||||
}
|
||||
|
||||
public static ChannelModel get(String name) {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("SELECT UUID FROM "+TABLE_NAME+" WHERE name = ?");
|
||||
ps.setString(1, name);
|
||||
if (ps.execute()) {
|
||||
ResultSet rs = ps.getResultSet();
|
||||
if (rs.next())
|
||||
return get(UUID.fromString(rs.getString("UUID")));
|
||||
}
|
||||
} catch (SQLException err) {
|
||||
err.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ChannelModel get(UUID uuid) {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("SELECT * FROM "+TABLE_NAME+" WHERE UUID = ?");
|
||||
ps.setString(1, uuid.toString());
|
||||
if (ps.execute()) {
|
||||
ResultSet rs = ps.getResultSet();
|
||||
if (rs.next())
|
||||
return new ChannelModel(
|
||||
UUID.fromString(rs.getString("UUID")),
|
||||
rs.getString("NAME")
|
||||
);
|
||||
}
|
||||
} catch (SQLException err) {
|
||||
err.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean exist() {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("SELECT UUID FROM "+TABLE_NAME+" WHERE UUID = ?");
|
||||
ps.setString(1, super.getUUID().toString());
|
||||
ps.execute();
|
||||
return ps.getResultSet().next();
|
||||
} catch (SQLException err) {
|
||||
err.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean create() {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("INSERT INTO "+TABLE_NAME+" (UUID, name) VALUES (?, ?)");
|
||||
ps.setString(1, super.getUUID().toString());
|
||||
ps.setString(2, super.getName());
|
||||
return ps.executeUpdate() > 0;
|
||||
} catch (SQLException err) {
|
||||
err.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean save() {
|
||||
if (!exist())
|
||||
return create();
|
||||
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("UPDATE "+TABLE_NAME+" SET name = ? WHERE UUID = ?");
|
||||
ps.setString(1, super.getName());
|
||||
return ps.executeUpdate() > 0;
|
||||
} catch (SQLException err) {
|
||||
err.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void generateTable() {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("CREATE TABLE "+TABLE_NAME+" ( UUID varchar(40) primary key, name varchar(16) unique )");
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,4 @@ import fr.univ.lyon1.server.Database;
|
|||
|
||||
public interface Model {
|
||||
Database database = Database.getDatabase();
|
||||
|
||||
|
||||
}
|
||||
|
|
96
src/fr/univ/lyon1/server/models/UserChannelModel.java
Normal file
96
src/fr/univ/lyon1/server/models/UserChannelModel.java
Normal file
|
@ -0,0 +1,96 @@
|
|||
package fr.univ.lyon1.server.models;
|
||||
|
||||
import fr.univ.lyon1.common.Channel;
|
||||
import fr.univ.lyon1.common.User;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class UserChannelModel implements Model {
|
||||
private User user;
|
||||
private Channel channel;
|
||||
|
||||
private static final String TABLE_NAME = "UserChannel";
|
||||
|
||||
public UserChannelModel(User user, Channel channel) {
|
||||
this.user = user;
|
||||
this.channel = channel;
|
||||
|
||||
if (!exist(user, channel))
|
||||
create();
|
||||
}
|
||||
|
||||
public static List<User> getUsers(Channel channel) {
|
||||
List<User> users = new ArrayList<>();
|
||||
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("SELECT userUUID FROM "+TABLE_NAME+" WHERE channelUUID = ?");
|
||||
ps.setString(1, channel.getUUID().toString());
|
||||
if (ps.execute()) {
|
||||
ResultSet rs = ps.getResultSet();
|
||||
while (rs.next())
|
||||
users.add(UserModel.get(UUID.fromString(rs.getString("userUUID"))));
|
||||
}
|
||||
} catch (SQLException err) {
|
||||
err.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
public static List<Channel> getChannels(User user) {
|
||||
List<Channel> channels = new ArrayList<>();
|
||||
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("SELECT channelUUID FROM "+TABLE_NAME+" WHERE userUUID = ?");
|
||||
ps.setString(1, user.getUUID().toString());
|
||||
if (ps.execute()) {
|
||||
ResultSet rs = ps.getResultSet();
|
||||
while (rs.next())
|
||||
channels.add(ChannelModel.get(UUID.fromString(rs.getString("channelUUID"))));
|
||||
}
|
||||
} catch (SQLException err) {
|
||||
err.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return channels;
|
||||
}
|
||||
|
||||
public static boolean exist(User user, Channel channel) {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("SELECT 1 FROM "+TABLE_NAME+" WHERE userUUID = ? AND channelUUID = ?");
|
||||
ps.setString(1, user.getUUID().toString());
|
||||
ps.setString(2, channel.getUUID().toString());
|
||||
ps.execute();
|
||||
return ps.getResultSet().next();
|
||||
} catch (SQLException err) {
|
||||
err.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean create() {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("INSERT INTO "+TABLE_NAME+" (userUUID, channelUUID) VALUES (?, ?)");
|
||||
ps.setString(1, user.getUUID().toString());
|
||||
ps.setString(2, channel.getUUID().toString());
|
||||
return ps.executeUpdate() > 0;
|
||||
} catch (SQLException err) {
|
||||
err.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void generateTable() {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("CREATE TABLE "+TABLE_NAME+" (userUUID varchar(40) not null references User(UUID), channelUUID varchar(40) not null references Channel(UUID), PRIMARY KEY (userUUID, channelUUID))");
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
Reference in a new issue