Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions OWNER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Агафонов Александр
2 changes: 1 addition & 1 deletion client.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<root>

<bean id="protocol" class="arhangel.dim.core.net.StringProtocol"/>
<bean id="protocol" class="arhangel.dim.core.net.BinaryProtocol"/>

<bean id="client" class="arhangel.dim.client.Client">
<property name="port" val="19000"/>
Expand Down
35 changes: 35 additions & 0 deletions server.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<root>

<bean id="protocol" class="arhangel.dim.core.net.BinaryProtocol"/>

<bean id="poolingDataSource" class="org.postgresql.ds.PGPoolingDataSource">
<property name="dataSourceName" val="Data source"/>
<property name="serverName" val="178.62.140.149"/>
<property name="databaseName" val="gafusss"/>
<property name="user" val="trackuser"/>
<property name="password" val="trackuser"/>
<property name="maxConnections" val="10"/>
</bean>

<bean id="daoFactory" class="arhangel.dim.core.store.PostgresqlDaoFactory">
<property name="dataSource" ref="poolingDataSource"/>
</bean>

<bean id="userDao" class="arhangel.dim.core.store.PostgresqlUserDao">
<property name="parentFactory" ref="daoFactory"/>
</bean>

<bean id="messageDao" class="arhangel.dim.core.store.PostgresqlMessageDao">
<property name="parentFactory" ref="daoFactory"/>
</bean>

<bean id="server" class="arhangel.dim.server.Server">
<property name="port" val="19000"/>
<property name="bufferSize" val="8192"/>
<property name="protocol" ref="protocol"/>
<property name="maxConnection" val="16"/>
<property name="userStore" ref="userDao"/>
<property name="messageStore" ref="messageDao"/>
</bean>

</root>
210 changes: 159 additions & 51 deletions src/main/java/arhangel/dim/client/Client.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
package arhangel.dim.client;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Arrays;
import java.util.Scanner;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import arhangel.dim.container.Container;
import arhangel.dim.container.Context;
import arhangel.dim.container.InvalidConfigurationException;
import arhangel.dim.core.messages.ChatCreateMessage;
import arhangel.dim.core.messages.ChatHistoryMessage;
import arhangel.dim.core.messages.ChatHistoryResultMessage;
import arhangel.dim.core.messages.ChatListMessage;
import arhangel.dim.core.messages.ChatListResultMessage;
import arhangel.dim.core.messages.InfoMessage;
import arhangel.dim.core.messages.InfoResultMessage;
import arhangel.dim.core.messages.LoginMessage;
import arhangel.dim.core.messages.Message;
import arhangel.dim.core.messages.RegisterMessage;
import arhangel.dim.core.messages.StatusMessage;
import arhangel.dim.core.messages.TextMessage;
import arhangel.dim.core.messages.Type;
import arhangel.dim.core.net.ConnectionHandler;
import arhangel.dim.core.net.Protocol;
import arhangel.dim.core.net.ProtocolException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

import static java.lang.Long.parseLong;

/**
* Клиент для тестирования серверного приложения
Expand All @@ -26,13 +39,12 @@ public class Client implements ConnectionHandler {

/**
* Механизм логирования позволяет более гибко управлять записью данных в лог (консоль, файл и тд)
* */
static Logger log = LoggerFactory.getLogger(Client.class);
*/
private static Logger log = LoggerFactory.getLogger(Client.class);

/**
* Протокол, хост и порт инициализируются из конфига
*
* */
*/
private Protocol protocol;
private int port;
private String host;
Expand All @@ -45,35 +57,12 @@ public class Client implements ConnectionHandler {
/**
* С каждым сокетом связано 2 канала in/out
*/
private Socket socket;
private InputStream in;
private OutputStream out;

public Protocol getProtocol() {
return protocol;
}

public void setProtocol(Protocol protocol) {
this.protocol = protocol;
}

public int getPort() {
return port;
}

public void setPort(int port) {
this.port = port;
}

public String getHost() {
return host;
}

public void setHost(String host) {
this.host = host;
}

public void initSocket() throws IOException {
Socket socket = new Socket(host, port);
socket = new Socket(host, port);
in = socket.getInputStream();
out = socket.getOutputStream();

Expand All @@ -87,6 +76,9 @@ public void initSocket() throws IOException {
try {
// Здесь поток блокируется на ожидании данных
int read = in.read(buf);
if (Thread.currentThread().isInterrupted() || read == -1) {
return;
}
if (read > 0) {

// По сети передается поток байт, его нужно раскодировать с помощью протокола
Expand All @@ -110,6 +102,34 @@ public void initSocket() throws IOException {
@Override
public void onMessage(Message msg) {
log.info("Message received: {}", msg);
switch (msg.getType()) {
case MSG_STATUS:
StatusMessage statusMessage = (StatusMessage) msg;
System.out.println(statusMessage.getText());
break;
case MSG_INFO_RESULT:
InfoResultMessage infoResultMessage = (InfoResultMessage) msg;
System.out.println("User: " + infoResultMessage.getLogin());
System.out.println("ID: " + infoResultMessage.getUserId());
System.out.println("Chats: " + infoResultMessage.getChatIds());
break;
case MSG_CHAT_LIST_RESULT:
ChatListResultMessage chatListResultMessage = (ChatListResultMessage) msg;
System.out.println("Chats: " + chatListResultMessage.getChatIds());
break;
case MSG_CHAT_HIST_RESULT:
ChatHistoryResultMessage chatHistoryResultMessage = (ChatHistoryResultMessage) msg;
for (TextMessage message : chatHistoryResultMessage.getMessages()) {
System.out.println("[" + message.getSenderLogin() + "]: " + message.getText());
}
break;
case MSG_TEXT:
TextMessage textMessage = (TextMessage) msg;
System.out.println("[" + textMessage.getSenderLogin() + "]: " + textMessage.getText());
break;
default:
log.info("Received unsupported message type {}", msg.getType());
}
}

/**
Expand All @@ -120,22 +140,102 @@ public void processInput(String line) throws IOException, ProtocolException {
String[] tokens = line.split(" ");
log.info("Tokens: {}", Arrays.toString(tokens));
String cmdType = tokens[0];

switch (cmdType) {
case "/register":
if (tokens.length != 3) {
System.out.println("Expected 2 parameters");
return;
}
RegisterMessage registerMessage = new RegisterMessage();
registerMessage.setType(Type.MSG_REGISTER);
registerMessage.setLogin(tokens[1]);
registerMessage.setSecret(tokens[2]);
send(registerMessage);
break;
case "/login":
// TODO: реализация
if (tokens.length != 3) {
System.out.println("Expected 2 parameters");
return;
}
LoginMessage loginMessage = new LoginMessage();
loginMessage.setType(Type.MSG_LOGIN);
loginMessage.setLogin(tokens[1]);
loginMessage.setSecret(tokens[2]);
send(loginMessage);
break;
case "/help":
// TODO: реализация
case "/info":
if (tokens.length > 2) {
System.out.println("Expected less than 2 parameters");
return;
} else {
InfoMessage infoMessage = new InfoMessage();
infoMessage.setType(Type.MSG_INFO);
if (tokens.length == 2) {
infoMessage.setLogin(tokens[1]);
} else {
infoMessage.setLogin(null);
}
send(infoMessage);
}
break;
case "/chat_create":
if (tokens.length < 2) {
System.out.println("Expected at least 1 parameter");
return;
}
ChatCreateMessage chatCreateMessage = new ChatCreateMessage();
chatCreateMessage.setType(Type.MSG_CHAT_CREATE);
Set<String> users = new HashSet<>();
users.addAll(Arrays.asList(tokens).subList(1, tokens.length));
chatCreateMessage.setUsers(users);
send(chatCreateMessage);
break;
case "/chat_list":
ChatListMessage chatListMessage = new ChatListMessage();
chatListMessage.setType(Type.MSG_CHAT_LIST);
send(chatListMessage);
break;
case "/chat_history":
if (tokens.length != 2) {
System.out.println("Expected 1 argument");
}
ChatHistoryMessage chatHistoryMessage = new ChatHistoryMessage();
chatHistoryMessage.setType(Type.MSG_CHAT_HIST);
try {
chatHistoryMessage.setChatId(parseLong(tokens[1]));
} catch (Exception e) {
System.out.println("Expected number");
return;
}
send(chatHistoryMessage);
break;
case "/text":
// FIXME: пример реализации для простого текстового сообщения
tokens = line.split(" ", 3);
if (tokens.length != 3) {
System.out.println("Expected 2 parameters");
return;
}
TextMessage sendMessage = new TextMessage();
sendMessage.setType(Type.MSG_TEXT);
sendMessage.setText(tokens[1]);
try {
sendMessage.setChatId(parseLong(tokens[1]));
} catch (Exception e) {
System.out.println("Expected number as first parameter");
return;
}
sendMessage.setText(tokens[2]);
send(sendMessage);
break;
// TODO: implement another types from wiki

case "/help":
System.out.println("/register <login> <password> - register and log in");
System.out.println("/login <login> <password> - log in");
System.out.println("/info [login] - get information about user, self if not specified");
System.out.println("/chat_list - get your chat ids");
System.out.println("/chat_create <login1> [login2 login3 ...] - create chat with specified users");
System.out.println("/chat_history <chat id> - get history of chat messages");
System.out.println("/text <chat id> <message> - send message to chat");
break;
default:
log.error("Invalid input: " + line);
}
Expand All @@ -153,16 +253,24 @@ public void send(Message msg) throws IOException, ProtocolException {

@Override
public void close() {
// TODO: написать реализацию. Закройте ресурсы и остановите поток-слушатель
socketThread.interrupt();
try {
socket.shutdownOutput();
socket.shutdownInput();
socketThread.join();
socket.close();
} catch (Exception e) {
//Silently close
}
}

public static void main(String[] args) throws Exception {

Client client = null;
Client client;
// Пользуемся механизмом контейнера
try {
Container context = new Container("client.xml");
client = (Client) context.getByName("client");
Context context = new Context("client.xml");
client = (Client) context.getBeanByName("client");
} catch (InvalidConfigurationException e) {
log.error("Failed to create client", e);
return;
Expand Down
20 changes: 18 additions & 2 deletions src/main/java/arhangel/dim/container/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
Expand Down Expand Up @@ -89,7 +90,15 @@ public void instantiateBeans() throws Exception {
for (String name : bean.getProperties().keySet()) {
// ищем поле с таким именен внутри класса
// учитывая приватные
Field field = clazz.getDeclaredField(name);
Field field = null;
Class superclazz = clazz;
while ((field == null) && (superclazz != null)) {
try {
field = superclazz.getDeclaredField(name);
} catch (NoSuchFieldException e) {
superclazz = superclazz.getSuperclass();
}
}
if (field == null) {
throw new InvalidConfigurationException("Failed to set field [" + name + "] for class " + clazz.getName());
}
Expand All @@ -102,7 +111,14 @@ public void instantiateBeans() throws Exception {

switch (prop.getType()) {
case VAL:
field.set(ob, convert(type.getTypeName(), prop.getValue()));
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, convert(type.getTypeName(), prop.getValue()));
} else {
field.set(ob, convert(type.getTypeName(), prop.getValue()));
}
break;
case REF:
String refName = prop.getValue();
Expand Down
Loading