├── scripts ├── client.bat ├── server.bat ├── make-jar.bat ├── client.sh ├── server.sh └── make-jar.sh ├── images ├── avatar.png ├── msgbox-info.png ├── login-banner.psd ├── msgbox-error.psd └── connection-banner.psd ├── src └── main │ ├── resources │ ├── app.properties │ ├── images │ │ ├── avatar.png │ │ ├── banner.png │ │ ├── offline.png │ │ ├── online.png │ │ ├── profile.png │ │ ├── button-send.png │ │ ├── login-banner.png │ │ ├── msgbox-error.png │ │ ├── msgbox-info.png │ │ ├── personal-logo.png │ │ └── connection-banner.png │ ├── layouts │ │ ├── dynamic-text-area.fxml │ │ ├── chat-message-mine.fxml │ │ ├── chat-message-friend.fxml │ │ ├── password-dialog.fxml │ │ ├── friend-list-view-cell.fxml │ │ ├── chat-window.fxml │ │ ├── register-window.fxml │ │ ├── login-window.fxml │ │ ├── connection-window.fxml │ │ ├── profile-window.fxml │ │ ├── friend-list-window.fxml │ │ └── main-window.fxml │ ├── css │ │ ├── chatmsg.css │ │ └── chatbox.css │ └── log4j.properties │ └── java │ └── com │ └── blogspot │ └── sontx │ └── chatsocket │ ├── lib │ ├── Function.java │ ├── Callback.java │ ├── bean │ │ ├── ResponseCode.java │ │ ├── RequestCode.java │ │ ├── Response.java │ │ ├── UpdatePassword.java │ │ ├── Request.java │ │ ├── ChatMessage.java │ │ ├── LoginInfo.java │ │ ├── RegisterInfo.java │ │ └── Profile.java │ ├── view │ │ ├── ViewFactory.java │ │ ├── BaseView.java │ │ ├── WindowUtils.java │ │ ├── TwoLineJLabel.java │ │ ├── AbstractViewFactory.java │ │ ├── FitImageJLabel.java │ │ ├── ClickableJLabel.java │ │ ├── AbstractSwingWindow.java │ │ ├── AbstractJavaFxWindow.java │ │ └── javafx │ │ │ └── custom │ │ │ └── DynamicTextArea.java │ ├── service │ │ ├── message │ │ │ ├── MessageType.java │ │ │ ├── MessageBox.java │ │ │ ├── SwingMessageBox.java │ │ │ └── JavaFxMessageBox.java │ │ ├── Service.java │ │ ├── event │ │ │ ├── StopServiceEvent.java │ │ │ └── ShowMessageBoxEvent.java │ │ ├── BackgroundService.java │ │ ├── MessageBoxService.java │ │ └── AbstractService.java │ ├── event │ │ └── AppShutdownEvent.java │ ├── settings │ │ ├── SettingsLoader.java │ │ └── Setting.java │ ├── Presenter.java │ ├── bo │ │ ├── ObjectAdapter.java │ │ ├── ObjectTransmission.java │ │ ├── AbstractResource.java │ │ ├── ByteTransmission.java │ │ ├── LayoutsResource.java │ │ ├── SocketByteTransmission.java │ │ ├── DefaultObjectTransmission.java │ │ ├── SerializableObjectAdapter.java │ │ └── ImagesResource.java │ ├── NotRegisteredException.java │ ├── thread │ │ ├── ThreadInvoker.java │ │ ├── JavaFxThreadInvoker.java │ │ ├── AbstractThreadInvoker.java │ │ └── SwingThreadInvoker.java │ ├── utils │ │ ├── StreamUtils.java │ │ ├── NetworkUtils.java │ │ └── Security.java │ ├── platform │ │ ├── Platform.java │ │ ├── SwingPlatform.java │ │ ├── JavaFxPlatform.java │ │ └── AbstractPlatform.java │ ├── AbstractPresenter.java │ └── Component.java │ ├── client │ ├── event │ │ ├── OpenRegisterEvent.java │ │ ├── ConnectedToServerEvent.java │ │ ├── UpdateFriendListEvent.java │ │ ├── UpdatePasswordEvent.java │ │ ├── ClientShutdownEvent.java │ │ ├── LoginEvent.java │ │ ├── ConnectToServerEvent.java │ │ ├── OpenChatEvent.java │ │ ├── LoggedEvent.java │ │ ├── RegisterEvent.java │ │ ├── UpdateProfileEvent.java │ │ ├── OpenMyProfileEvent.java │ │ ├── RegisteredEvent.java │ │ ├── ChatWindowClosedEvent.java │ │ ├── FriendInfoChangedEvent.java │ │ ├── SendChatMessageEvent.java │ │ ├── MyAccountInfoReceivedEvent.java │ │ ├── ChatMessageReceivedEvent.java │ │ └── FriendListReceivedEvent.java │ ├── AppClient.java │ ├── model │ │ ├── Client.java │ │ ├── handler │ │ │ ├── ResponseHandler.java │ │ │ ├── DoNothingResponseHandler.java │ │ │ ├── AbstractResponseHandler.java │ │ │ ├── PasswordResponseHandler.java │ │ │ ├── FriendInfoResponseHandler.java │ │ │ ├── LoginResponseHandler.java │ │ │ ├── RegisterResponseHandler.java │ │ │ ├── AccountInfoResponseHandler.java │ │ │ ├── UpdateProfileResponseHandler.java │ │ │ ├── FriendListResponseHandler.java │ │ │ ├── ChatMessageResponseHandler.java │ │ │ └── ResponseHandlerFactory.java │ │ ├── ChattingManager.java │ │ └── UserProfile.java │ ├── ClientSettings.java │ ├── view │ │ ├── RegisterView.java │ │ ├── javafx │ │ │ ├── custom │ │ │ │ ├── ChatMessageInternal.java │ │ │ │ ├── ChatMessageCell.java │ │ │ │ └── FriendListViewCell.java │ │ │ ├── JavaFxViewFactory.java │ │ │ ├── LoginWindow.java │ │ │ ├── RegisterWindow.java │ │ │ ├── ConnectionWindow.java │ │ │ ├── ChatWindow.java │ │ │ ├── ProfileWindow.java │ │ │ ├── PasswordDialog.java │ │ │ └── FriendListWindow.java │ │ ├── ConnectionView.java │ │ ├── LoginView.java │ │ ├── ChatView.java │ │ ├── ProfileView.java │ │ ├── swing │ │ │ ├── SwingViewFactory.java │ │ │ ├── custom │ │ │ │ ├── FriendEntry.java │ │ │ │ └── FriendCellRenderer.java │ │ │ ├── PasswordDialog.java │ │ │ ├── ConnectionWindow.java │ │ │ └── LoginWindow.java │ │ └── FriendListView.java │ ├── platform │ │ ├── ClientSwingPlatform.java │ │ └── ClientJavaFxPlatform.java │ └── presenter │ │ ├── RegisterPresenter.java │ │ ├── LoginPresenter.java │ │ ├── ConnectionPresenter.java │ │ ├── ProfilePresenter.java │ │ └── ChatPresenter.java │ ├── server │ ├── view │ │ ├── LogView.java │ │ ├── MainView.java │ │ ├── swing │ │ │ ├── SwingViewFactory.java │ │ │ └── AddressDialog.java │ │ └── javafx │ │ │ ├── JavaFxViewFactory.java │ │ │ ├── AddressDialog.java │ │ │ └── MainWindow.java │ ├── event │ │ ├── StopServerEvent.java │ │ ├── StartServerEvent.java │ │ ├── ServerStatusChangedEvent.java │ │ ├── LookupWorkerEvent.java │ │ ├── AccountInfoChangedEvent.java │ │ ├── ShutdownWorkerEvent.java │ │ ├── ShutdownServerEvent.java │ │ ├── RequestReceivedEvent.java │ │ └── ForwardChatMessageEvent.java │ ├── AppServer.java │ ├── model │ │ ├── handler │ │ │ ├── RequestHandler.java │ │ │ ├── DoNothingRequestHandler.java │ │ │ ├── AccountInfoRequestHandler.java │ │ │ ├── FriendListRequestHandler.java │ │ │ ├── ChatMessageRequestHandler.java │ │ │ ├── RequestHandlerFactory.java │ │ │ ├── UpdateProfileRequestHandler.java │ │ │ ├── PasswordRequestHandler.java │ │ │ ├── AbstractRequestHandler.java │ │ │ ├── LoginRequestHandler.java │ │ │ └── RegisterRequestHandler.java │ │ ├── Server.java │ │ ├── account │ │ │ ├── Account.java │ │ │ ├── AccountStorage.java │ │ │ ├── AccountManager.java │ │ │ └── AccountManagerImpl.java │ │ ├── Worker.java │ │ ├── RequestRouter.java │ │ └── SocketServer.java │ ├── ServerSettings.java │ ├── platform │ │ ├── ServerSwingPlatform.java │ │ └── ServerJavaFxPlatform.java │ └── presenter │ │ ├── Log4jUIAppender.java │ │ └── MainPresenter.java │ ├── Program.java │ ├── AppConfig.java │ └── Bootstrap.java ├── .travis.yml ├── LICENSE ├── .gitignore └── pom.xml /scripts/client.bat: -------------------------------------------------------------------------------- 1 | start homechat.jar --mode=client -------------------------------------------------------------------------------- /scripts/server.bat: -------------------------------------------------------------------------------- 1 | start homechat.jar --mode=server -------------------------------------------------------------------------------- /scripts/make-jar.bat: -------------------------------------------------------------------------------- 1 | cd ../ 2 | mvn clean compile assembly:single -------------------------------------------------------------------------------- /scripts/client.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | java -jar homechat.jar --mode=client -------------------------------------------------------------------------------- /scripts/server.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | java -jar homechat.jar --mode=server -------------------------------------------------------------------------------- /images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sontx/chat-socket/HEAD/images/avatar.png -------------------------------------------------------------------------------- /scripts/make-jar.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd ../ 3 | mvn clean compile assembly:single -------------------------------------------------------------------------------- /images/msgbox-info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sontx/chat-socket/HEAD/images/msgbox-info.png -------------------------------------------------------------------------------- /images/login-banner.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sontx/chat-socket/HEAD/images/login-banner.psd -------------------------------------------------------------------------------- /images/msgbox-error.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sontx/chat-socket/HEAD/images/msgbox-error.psd -------------------------------------------------------------------------------- /images/connection-banner.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sontx/chat-socket/HEAD/images/connection-banner.psd -------------------------------------------------------------------------------- /src/main/resources/app.properties: -------------------------------------------------------------------------------- 1 | AppName=Homechat 2 | AppVersion=v2.1.0 3 | HomePageUrl=http://www.code4bugs.com/ -------------------------------------------------------------------------------- /src/main/resources/images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sontx/chat-socket/HEAD/src/main/resources/images/avatar.png -------------------------------------------------------------------------------- /src/main/resources/images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sontx/chat-socket/HEAD/src/main/resources/images/banner.png -------------------------------------------------------------------------------- /src/main/resources/images/offline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sontx/chat-socket/HEAD/src/main/resources/images/offline.png -------------------------------------------------------------------------------- /src/main/resources/images/online.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sontx/chat-socket/HEAD/src/main/resources/images/online.png -------------------------------------------------------------------------------- /src/main/resources/images/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sontx/chat-socket/HEAD/src/main/resources/images/profile.png -------------------------------------------------------------------------------- /src/main/resources/images/button-send.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sontx/chat-socket/HEAD/src/main/resources/images/button-send.png -------------------------------------------------------------------------------- /src/main/resources/images/login-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sontx/chat-socket/HEAD/src/main/resources/images/login-banner.png -------------------------------------------------------------------------------- /src/main/resources/images/msgbox-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sontx/chat-socket/HEAD/src/main/resources/images/msgbox-error.png -------------------------------------------------------------------------------- /src/main/resources/images/msgbox-info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sontx/chat-socket/HEAD/src/main/resources/images/msgbox-info.png -------------------------------------------------------------------------------- /src/main/resources/images/personal-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sontx/chat-socket/HEAD/src/main/resources/images/personal-logo.png -------------------------------------------------------------------------------- /src/main/resources/images/connection-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sontx/chat-socket/HEAD/src/main/resources/images/connection-banner.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: openjdk8 3 | before_install: 4 | - sudo apt update 5 | - sudo apt install openjfx 6 | script: mvn clean verify -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/Function.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib; 2 | 3 | public interface Function { 4 | T call(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/OpenRegisterEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | public class OpenRegisterEvent { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/Callback.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib; 2 | 3 | public interface Callback { 4 | void call(T obj); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/bean/ResponseCode.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.bean; 2 | 3 | public enum ResponseCode { 4 | OK, 5 | Fail 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/view/ViewFactory.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.view; 2 | 3 | public interface ViewFactory { 4 | T create(Class viewType); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/service/message/MessageType.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.service.message; 2 | 3 | public enum MessageType { 4 | Info, 5 | Error 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/event/AppShutdownEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.event; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class AppShutdownEvent { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/ConnectedToServerEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ConnectedToServerEvent { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/UpdateFriendListEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class UpdateFriendListEvent { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/view/LogView.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.view; 2 | 3 | public interface LogView { 4 | void appendLog(String message); 5 | 6 | void clearLog(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/settings/SettingsLoader.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.settings; 2 | 3 | public interface SettingsLoader { 4 | T get(Class settingType); 5 | 6 | void save(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/service/message/MessageBox.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.service.message; 2 | 3 | public interface MessageBox { 4 | void show(String caption, String text, MessageType type); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/event/StopServerEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.event; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Stops server command. 7 | */ 8 | @Data 9 | public class StopServerEvent { 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/Program.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket; 2 | 3 | /** 4 | * The main entry of application. 5 | */ 6 | public class Program { 7 | public static void main(String[] args) { 8 | new Bootstrap().boot(args); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/Presenter.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.platform.Platform; 4 | 5 | public interface Presenter { 6 | void show(); 7 | 8 | void setPlatform(Platform platform); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/resources/layouts/dynamic-text-area.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/AppClient.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.platform.Platform; 4 | 5 | /** 6 | * Client entry point. 7 | */ 8 | public interface AppClient { 9 | void start(Platform platform); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/AppServer.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.platform.Platform; 4 | 5 | /** 6 | * Server entry point. 7 | */ 8 | public interface AppServer { 9 | void start(Platform platform); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/bo/ObjectAdapter.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.bo; 2 | 3 | /** 4 | * Convert bytes to object and vice versa. 5 | */ 6 | public interface ObjectAdapter { 7 | byte[] objectToBytes(Object obj); 8 | 9 | Object bytesToObject(byte[] bytes); 10 | } -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/bean/RequestCode.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.bean; 2 | 3 | public enum RequestCode { 4 | AccountInfo, 5 | FriendList, 6 | ChatMessage, 7 | Login, 8 | Register, 9 | UpdatePassword, 10 | UpdateProfile, 11 | FriendInfoUpdated 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/model/handler/RequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.model.handler; 2 | 3 | import com.blogspot.sontx.chatsocket.server.event.RequestReceivedEvent; 4 | 5 | public interface RequestHandler { 6 | void handle(RequestReceivedEvent event) throws Exception; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/UpdatePasswordEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.UpdatePassword; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class UpdatePasswordEvent { 8 | private UpdatePassword updatePassword; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/NotRegisteredException.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib; 2 | 3 | public class NotRegisteredException extends RuntimeException { 4 | public NotRegisteredException() { 5 | } 6 | 7 | public NotRegisteredException(String msg) { 8 | super(msg); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/ServerSettings.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.settings.Setting; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Setting(key = "server") 8 | public class ServerSettings { 9 | private String ip; 10 | private int port; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/model/Client.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.model; 2 | 3 | import java.io.Closeable; 4 | 5 | /** 6 | * Lowest layer that communicates with {@link com.blogspot.sontx.chatsocket.server.model.Server}. 7 | */ 8 | public interface Client extends Closeable { 9 | void start(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/service/Service.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.service; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.platform.Platform; 4 | 5 | public interface Service { 6 | int getId(); 7 | 8 | void setPlatform(Platform platform); 9 | 10 | void start(); 11 | 12 | void stop(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/service/event/StopServiceEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.service.event; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class StopServiceEvent { 11 | private int id; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/ClientShutdownEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class ClientShutdownEvent { 11 | private Exception reason; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/model/Server.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.model; 2 | 3 | import java.io.Closeable; 4 | 5 | /** 6 | * Communication server that will wait for connections from clients and routes these connections 7 | * to {@link Worker}. 8 | */ 9 | public interface Server extends Closeable { 10 | void start(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/ClientSettings.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.settings.Setting; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Setting(key = "client") 8 | public class ClientSettings { 9 | private String serverIp; 10 | private int serverPort; 11 | private String loggedUserName; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/LoginEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class LoginEvent { 11 | private String username; 12 | private String password; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/view/BaseView.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.view; 2 | 3 | public interface BaseView { 4 | void showWindow(); 5 | 6 | void closeWindow(); 7 | 8 | void setTitle(String title); 9 | 10 | void setOnClosingListener(Runnable listener); 11 | 12 | void setMainWindow(); 13 | 14 | boolean isMainWindow(); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/model/account/Account.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.model.account; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | import lombok.Data; 5 | 6 | @Data 7 | class Account { 8 | private String id; 9 | private String username; 10 | private String passwordHash; 11 | private Profile profile; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/model/handler/ResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.model.handler; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Response; 4 | import com.blogspot.sontx.chatsocket.lib.bo.ObjectTransmission; 5 | 6 | public interface ResponseHandler { 7 | void handle(ObjectTransmission transmission, Response response) throws Exception; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/ConnectToServerEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class ConnectToServerEvent { 11 | private String serverIp; 12 | private int serverPort; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/model/handler/DoNothingRequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.model.handler; 2 | 3 | import com.blogspot.sontx.chatsocket.server.event.RequestReceivedEvent; 4 | 5 | public class DoNothingRequestHandler extends AbstractRequestHandler { 6 | @Override 7 | public void handle(RequestReceivedEvent event) throws Exception { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/OpenChatEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | public class OpenChatEvent { 12 | private Profile chatWith; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/view/RegisterView.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.view; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.view.BaseView; 4 | 5 | public interface RegisterView extends BaseView { 6 | void setRegisterButtonClickListener(Runnable listener); 7 | 8 | String getUsername(); 9 | 10 | String getPassword(); 11 | 12 | String getDisplayName(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/LoggedEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | public class LoggedEvent { 12 | private Profile loggedAccount; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/resources/css/chatmsg.css: -------------------------------------------------------------------------------- 1 | .chatmsg-content { 2 | -fx-background-radius: 5; 3 | -fx-padding: 8px; 4 | -fx-min-width: 80px; 5 | -fx-wrap-text: true; 6 | -fx-alignment: center; 7 | } 8 | 9 | .chatmsg-content--friend { 10 | -fx-background-color: #f1f0f0; 11 | -fx-text-fill: #4b4f56; 12 | } 13 | 14 | .chatmsg-content--mine { 15 | -fx-background-color: #4080ff; 16 | -fx-text-fill: #fff; 17 | } -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/RegisterEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class RegisterEvent { 11 | private String username; 12 | private String password; 13 | private String displayName; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/UpdateProfileEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | public class UpdateProfileEvent { 12 | private Profile profile; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/view/javafx/custom/ChatMessageInternal.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.view.javafx.custom; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class ChatMessageInternal { 11 | private String content; 12 | private boolean isMe; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/bo/ObjectTransmission.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.bo; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | 6 | /** 7 | * Communicating based on object stream. 8 | */ 9 | public interface ObjectTransmission extends Closeable { 10 | void sendObject(Object obj) throws IOException; 11 | 12 | Object receiveObject() throws IOException; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/thread/ThreadInvoker.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.thread; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.Function; 4 | 5 | public interface ThreadInvoker { 6 | void invokeLater(Runnable runnable); 7 | 8 | void invokeAndWait(Runnable runnable); 9 | 10 | T invokeWithResult(Function func); 11 | 12 | void executeAsync(Runnable callback); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/OpenMyProfileEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | public class OpenMyProfileEvent { 12 | private Profile myProfile; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/RegisteredEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | public class RegisteredEvent { 12 | private Profile registeredAccount; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/settings/Setting.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.settings; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.TYPE) 10 | public @interface Setting { 11 | String key(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/event/StartServerEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.event; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * Starts server command. 9 | */ 10 | @Data 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | public class StartServerEvent { 14 | private String ip; 15 | private int port; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/ChatWindowClosedEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | public class ChatWindowClosedEvent { 12 | private Profile chatWith; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/bean/Response.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.bean; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | @Data 8 | public class Response implements Serializable { 9 | private static final long serialVersionUID = -8799356878002273414L; 10 | 11 | private ResponseCode code; 12 | private RequestCode requestCode; 13 | private Object extra; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/bo/AbstractResource.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.bo; 2 | 3 | import java.io.InputStream; 4 | import java.net.URL; 5 | 6 | abstract class AbstractResource { 7 | InputStream loadResource(String path) { 8 | return getClass().getResourceAsStream(path); 9 | } 10 | 11 | URL toUrl(String path) { 12 | return getClass().getResource(path); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/FriendInfoChangedEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | public class FriendInfoChangedEvent { 12 | private Profile newFriendInfo; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/SendChatMessageEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.ChatMessage; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | public class SendChatMessageEvent { 12 | private ChatMessage chatMessage; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/event/ServerStatusChangedEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.event; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * Occurs when server's status changed. 9 | */ 10 | @Data 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | public class ServerStatusChangedEvent { 14 | private boolean running; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/MyAccountInfoReceivedEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | public class MyAccountInfoReceivedEvent { 12 | private Profile myProfile; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/bo/ByteTransmission.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.bo; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | 6 | /** 7 | * Communicating based on bytes stream. 8 | */ 9 | public interface ByteTransmission extends Closeable { 10 | void sendBytes(byte[] in) throws IOException; 11 | 12 | int receiveBytes(byte[] out, int offset, int length) throws IOException; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/ChatMessageReceivedEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.ChatMessage; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | public class ChatMessageReceivedEvent { 12 | private ChatMessage chatMessage; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/model/Worker.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.model; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * Communicates with an individual client. 9 | */ 10 | public interface Worker { 11 | void start(); 12 | 13 | void response(Object obj) throws IOException; 14 | 15 | void setAccount(Profile account); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/bean/UpdatePassword.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.bean; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | public class UpdatePassword implements Serializable { 13 | private String oldPassword; 14 | private String newPassword; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/event/FriendListReceivedEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.event; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.List; 9 | 10 | @Data 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | public class FriendListReceivedEvent { 14 | private List friendList; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/view/MainView.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.view; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.view.BaseView; 4 | 5 | public interface MainView extends BaseView { 6 | String getIp(); 7 | 8 | String getPort(); 9 | 10 | void setIp(String ip); 11 | 12 | void setPort(int port); 13 | 14 | void setStartButtonText(String text); 15 | 16 | void setStartButtonListener(Runnable listener); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/view/ConnectionView.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.view; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.view.BaseView; 4 | 5 | public interface ConnectionView extends BaseView { 6 | void setConnectButtonClickListener(Runnable listener); 7 | 8 | String getServerIp(); 9 | 10 | String getServerPort(); 11 | 12 | void setServerIp(String serverIp); 13 | 14 | void setServerPort(int serverPort); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/event/LookupWorkerEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.event; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | import com.blogspot.sontx.chatsocket.server.model.Worker; 5 | import lombok.Data; 6 | 7 | /** 8 | * Finds a running {@link Worker} by an {@link Profile}. 9 | */ 10 | @Data 11 | public class LookupWorkerEvent { 12 | private Profile matchedAccount; 13 | private Worker matchedWorker; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/resources/layouts/chat-message-mine.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/model/handler/DoNothingResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.model.handler; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Response; 4 | import com.blogspot.sontx.chatsocket.lib.bo.ObjectTransmission; 5 | 6 | public class DoNothingResponseHandler extends AbstractResponseHandler { 7 | @Override 8 | public void handle(ObjectTransmission transmission, Response response) throws Exception { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/resources/layouts/chat-message-friend.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/view/LoginView.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.view; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.view.BaseView; 4 | 5 | public interface LoginView extends BaseView { 6 | String getUsername(); 7 | 8 | String getPassword(); 9 | 10 | void setLoginButtonClickListener(Runnable listener); 11 | 12 | void setRegisterButtonClickListener(Runnable listener); 13 | 14 | void setUserName(String loggedUserName); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/view/ChatView.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.view; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.Callback; 4 | import com.blogspot.sontx.chatsocket.lib.view.BaseView; 5 | 6 | public interface ChatView extends BaseView { 7 | void appendFriendMessage(String message); 8 | 9 | void appendMeMyMessage(String message); 10 | 11 | void setSendButtonClickListener(Callback listener); 12 | 13 | void clearInput(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/event/AccountInfoChangedEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.event; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * Occurs when an account info changed. 10 | */ 11 | @Data 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class AccountInfoChangedEvent { 15 | private Profile profile; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/utils/StreamUtils.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.utils; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | 6 | public final class StreamUtils { 7 | StreamUtils() { 8 | } 9 | 10 | public static void tryCloseStream(Closeable obj) { 11 | if (obj != null) { 12 | try { 13 | obj.close(); 14 | } catch (IOException ignored) { 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/bean/Request.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.bean; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | public class Request implements Serializable { 13 | private static final long serialVersionUID = 982079171646222501L; 14 | 15 | private RequestCode code; 16 | private Object extra; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/bean/ChatMessage.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.bean; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | public class ChatMessage implements Serializable { 13 | private static final long serialVersionUID = 5140826547264710686L; 14 | 15 | private String whoId; 16 | private String content; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/bean/LoginInfo.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.bean; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | public class LoginInfo implements Serializable { 13 | private static final long serialVersionUID = 757708636228559068L; 14 | 15 | private String username; 16 | private String password; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/service/event/ShowMessageBoxEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.service.event; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.service.message.MessageType; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | public class ShowMessageBoxEvent { 12 | private String message; 13 | private String caption; 14 | private MessageType type; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/model/ChattingManager.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.model; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.ChatMessage; 4 | import com.blogspot.sontx.chatsocket.lib.bean.Response; 5 | 6 | /** 7 | * Manages received chat messages. 8 | */ 9 | public interface ChattingManager { 10 | void start(); 11 | 12 | void stop(); 13 | 14 | void processReceivedChatMessage(ChatMessage chatMessage); 15 | 16 | void processSentChatMessage(Response response); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/model/handler/AbstractResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.model.handler; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.service.AbstractService; 4 | import com.blogspot.sontx.chatsocket.lib.service.message.MessageType; 5 | 6 | abstract class AbstractResponseHandler extends AbstractService implements ResponseHandler { 7 | void showErrorMessage(String message, Object reason) { 8 | postMessageBox(message + ": " + reason, MessageType.Error); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/bean/RegisterInfo.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.bean; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | @Data 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class RegisterInfo implements Serializable { 13 | private static final long serialVersionUID = -3586442439987010899L; 14 | 15 | private String username; 16 | private String password; 17 | private String displayName; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/platform/ClientSwingPlatform.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.platform; 2 | 3 | import com.blogspot.sontx.chatsocket.client.view.swing.SwingViewFactory; 4 | import com.blogspot.sontx.chatsocket.lib.platform.SwingPlatform; 5 | import com.blogspot.sontx.chatsocket.lib.view.ViewFactory; 6 | import lombok.Getter; 7 | 8 | public class ClientSwingPlatform extends SwingPlatform { 9 | @Getter 10 | private final ViewFactory viewFactory; 11 | 12 | public ClientSwingPlatform() { 13 | viewFactory = new SwingViewFactory(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/event/ShutdownWorkerEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.event; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * Shutdown {@link com.blogspot.sontx.chatsocket.server.model.Worker} command. 9 | * All {@link com.blogspot.sontx.chatsocket.server.model.Worker} that have the corresponding serverSession 10 | * should shutdown. 11 | */ 12 | @Data 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public class ShutdownWorkerEvent { 16 | private int serverSession; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/platform/ServerSwingPlatform.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.platform; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.platform.SwingPlatform; 4 | import com.blogspot.sontx.chatsocket.lib.view.ViewFactory; 5 | import com.blogspot.sontx.chatsocket.server.view.swing.SwingViewFactory; 6 | import lombok.Getter; 7 | 8 | public class ServerSwingPlatform extends SwingPlatform { 9 | @Getter 10 | private final ViewFactory viewFactory; 11 | 12 | public ServerSwingPlatform() { 13 | viewFactory = new SwingViewFactory(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/view/WindowUtils.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.view; 2 | 3 | import javax.swing.*; 4 | 5 | public final class WindowUtils { 6 | 7 | private WindowUtils() { 8 | } 9 | 10 | public static void setSystemLookAndFeel() { 11 | try { 12 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 13 | } catch (ClassNotFoundException 14 | | InstantiationException 15 | | IllegalAccessException 16 | | UnsupportedLookAndFeelException ignored) { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/model/handler/AccountInfoRequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.model.handler; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Response; 4 | import com.blogspot.sontx.chatsocket.server.event.RequestReceivedEvent; 5 | 6 | /** 7 | * Handles account info request from the client. 8 | */ 9 | class AccountInfoRequestHandler extends AbstractRequestHandler { 10 | @Override 11 | Response handleWithAuthenticated(RequestReceivedEvent event) throws Exception { 12 | return okResponse(event.getProfile(), event.getRequest().getCode()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/event/ShutdownServerEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.event; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * Shutdown {@link com.blogspot.sontx.chatsocket.server.model.Server} command. 9 | * All {@link com.blogspot.sontx.chatsocket.server.model.Server} that have corresponding sessionId 10 | * should shutdown. 11 | */ 12 | @Data 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public class ShutdownServerEvent { 16 | public static final int ALL = -1; 17 | 18 | private int sessionId; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/platform/Platform.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.platform; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.settings.SettingsLoader; 4 | import com.blogspot.sontx.chatsocket.lib.thread.ThreadInvoker; 5 | import com.blogspot.sontx.chatsocket.lib.view.ViewFactory; 6 | 7 | public interface Platform { 8 | ViewFactory getViewFactory(); 9 | 10 | ThreadInvoker getThreadInvoker(); 11 | 12 | SettingsLoader getSettings(); 13 | 14 | void attach(Object service); 15 | 16 | void detach(Object service); 17 | 18 | void postEvent(Object event); 19 | 20 | void exit(); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/view/TwoLineJLabel.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.view; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.apache.commons.text.StringEscapeUtils; 5 | 6 | import javax.swing.*; 7 | 8 | public class TwoLineJLabel extends JLabel { 9 | protected void setText(String line1, String line2) { 10 | String escapedLine1 = StringUtils.isEmpty(line1) ? "" : StringEscapeUtils.escapeHtml3(line1); 11 | String escapedLine2 = StringUtils.isEmpty(line2) ? "" : StringEscapeUtils.escapeHtml3(line2); 12 | setText(String.format("%s
%s", escapedLine1, escapedLine2)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/event/RequestReceivedEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.event; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | import com.blogspot.sontx.chatsocket.lib.bean.Request; 5 | import com.blogspot.sontx.chatsocket.server.model.Worker; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | /** 11 | * Occurs when received a request from a client. 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class RequestReceivedEvent { 17 | private Request request; 18 | private Profile profile; 19 | private Worker sender; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/model/handler/PasswordResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.model.handler; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Response; 4 | import com.blogspot.sontx.chatsocket.lib.bean.ResponseCode; 5 | import com.blogspot.sontx.chatsocket.lib.bo.ObjectTransmission; 6 | 7 | public class PasswordResponseHandler extends AbstractResponseHandler { 8 | @Override 9 | public void handle(ObjectTransmission transmission, Response response) throws Exception { 10 | if (response.getCode() == ResponseCode.Fail) { 11 | showErrorMessage("Can not change password", response.getExtra()); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/service/BackgroundService.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.service; 2 | 3 | public abstract class BackgroundService extends AbstractService implements Runnable { 4 | private Thread thread; 5 | 6 | @Override 7 | public synchronized void start() { 8 | if (isStarted()) return; 9 | super.start(); 10 | thread = new Thread(this); 11 | thread.setDaemon(true); 12 | thread.start(); 13 | } 14 | 15 | @Override 16 | public synchronized void stop() { 17 | if (!isStarted()) return; 18 | super.stop(); 19 | thread.interrupt(); 20 | } 21 | 22 | public abstract void run(); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/model/account/AccountStorage.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.model.account; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | 5 | import java.util.List; 6 | import java.util.Optional; 7 | 8 | /** 9 | * Reads/writes accounts data from/to storage. 10 | */ 11 | public interface AccountStorage { 12 | Optional findById(String id); 13 | 14 | Optional findByUserName(String username); 15 | 16 | Account add(Account account); 17 | 18 | List findAll(); 19 | 20 | void updateDetail(String accountId, Profile profile); 21 | 22 | void updatePasswordHash(String accountId, String passwordHash); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/view/ProfileView.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.view; 2 | 3 | import com.blogspot.sontx.chatsocket.client.model.UserProfile; 4 | import com.blogspot.sontx.chatsocket.lib.Callback; 5 | import com.blogspot.sontx.chatsocket.lib.bean.UpdatePassword; 6 | import com.blogspot.sontx.chatsocket.lib.view.BaseView; 7 | 8 | public interface ProfileView extends BaseView { 9 | void setChangeDisplayNameButtonClickListener(Callback listener); 10 | 11 | void setChangeStatusButtonClickListener(Callback listener); 12 | 13 | void setChangePasswordButtonClickListener(Callback listener); 14 | 15 | void setProfile(UserProfile userProfile); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/bo/LayoutsResource.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.bo; 2 | 3 | import java.io.InputStream; 4 | import java.net.URL; 5 | 6 | public class LayoutsResource extends AbstractResource { 7 | private static LayoutsResource instance; 8 | 9 | public synchronized static LayoutsResource getInstance() { 10 | if (instance == null) 11 | instance = new LayoutsResource(); 12 | return instance; 13 | } 14 | 15 | public InputStream load(String layoutName) { 16 | return loadResource("/layouts/" + layoutName); 17 | } 18 | 19 | public URL getResource(String layoutName) { 20 | return toUrl("/layouts/" + layoutName); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/view/swing/SwingViewFactory.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.view.swing; 2 | 3 | import com.blogspot.sontx.chatsocket.client.view.*; 4 | import com.blogspot.sontx.chatsocket.lib.view.AbstractViewFactory; 5 | 6 | public class SwingViewFactory extends AbstractViewFactory { 7 | public SwingViewFactory() { 8 | register(ChatView.class, ChatWindow::new); 9 | register(ConnectionView.class, ConnectionWindow::new); 10 | register(FriendListView.class, FriendListWindow::new); 11 | register(LoginView.class, LoginWindow::new); 12 | register(ProfileView.class, ProfileWindow::new); 13 | register(RegisterView.class, RegisterWindow::new); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/view/FriendListView.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.view; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.Callback; 4 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 5 | import com.blogspot.sontx.chatsocket.lib.view.BaseView; 6 | 7 | import java.util.List; 8 | 9 | public interface FriendListView extends BaseView { 10 | void setMyInfoButtonClickListener(Runnable listener); 11 | 12 | void setFriendButtonClickListener(Callback listener); 13 | 14 | void setFriendList(List friendProfileList); 15 | 16 | void setMyAccountInfo(Profile profile); 17 | 18 | void updateFriend(Profile friendInfo); 19 | 20 | void addNewFriend(Profile newFriendInfo); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/thread/JavaFxThreadInvoker.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.thread; 2 | 3 | import com.sun.javafx.application.PlatformImpl; 4 | import javafx.application.Platform; 5 | import lombok.extern.log4j.Log4j; 6 | 7 | @Log4j 8 | public class JavaFxThreadInvoker extends AbstractThreadInvoker { 9 | @Override 10 | public void invokeLater(Runnable runnable) { 11 | if (PlatformImpl.isFxApplicationThread()) { 12 | executeAsync(() -> invokeLater(runnable)); 13 | } else { 14 | Platform.runLater(runnable); 15 | } 16 | } 17 | 18 | @Override 19 | public void invokeAndWait(Runnable runnable) { 20 | PlatformImpl.runAndWait(runnable); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/view/javafx/JavaFxViewFactory.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.view.javafx; 2 | 3 | import com.blogspot.sontx.chatsocket.client.view.*; 4 | import com.blogspot.sontx.chatsocket.lib.view.AbstractViewFactory; 5 | 6 | public class JavaFxViewFactory extends AbstractViewFactory { 7 | 8 | public JavaFxViewFactory() { 9 | register(ChatView.class, ChatWindow::new); 10 | register(ConnectionView.class, ConnectionWindow::new); 11 | register(FriendListView.class, FriendListWindow::new); 12 | register(LoginView.class, LoginWindow::new); 13 | register(ProfileView.class, ProfileWindow::new); 14 | register(RegisterView.class, RegisterWindow::new); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/service/MessageBoxService.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.service; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.service.event.ShowMessageBoxEvent; 4 | import com.blogspot.sontx.chatsocket.lib.service.message.MessageBox; 5 | import com.google.common.eventbus.Subscribe; 6 | 7 | public class MessageBoxService extends AbstractService { 8 | private final MessageBox messageBox; 9 | 10 | public MessageBoxService(MessageBox messageBox) { 11 | this.messageBox = messageBox; 12 | } 13 | 14 | @Subscribe 15 | public void onShowMessageBox(ShowMessageBoxEvent event) { 16 | runOnUiThread(() -> messageBox.show(event.getCaption(), event.getMessage(), event.getType())); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Root logger option 2 | log4j.rootLogger=DEBUG, stdout, file 3 | 4 | # Redirect log messages to console 5 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 6 | log4j.appender.stdout.Target=System.out 7 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 8 | log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n 9 | 10 | # Redirect log messages to a log file, support file rolling. 11 | log4j.appender.file=org.apache.log4j.RollingFileAppender 12 | log4j.appender.file.File=logs\\app.log 13 | log4j.appender.file.MaxFileSize=5MB 14 | log4j.appender.file.MaxBackupIndex=10 15 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 16 | log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/platform/SwingPlatform.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.platform; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.service.MessageBoxService; 4 | import com.blogspot.sontx.chatsocket.lib.service.message.SwingMessageBox; 5 | import com.blogspot.sontx.chatsocket.lib.thread.SwingThreadInvoker; 6 | import com.blogspot.sontx.chatsocket.lib.view.WindowUtils; 7 | 8 | public abstract class SwingPlatform extends AbstractPlatform { 9 | protected SwingPlatform() { 10 | super(new SwingThreadInvoker()); 11 | WindowUtils.setSystemLookAndFeel(); 12 | MessageBoxService messageBoxService = new MessageBoxService(new SwingMessageBox()); 13 | messageBoxService.setPlatform(this); 14 | messageBoxService.start(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/model/account/AccountManager.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.model.account; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | import com.blogspot.sontx.chatsocket.lib.bean.LoginInfo; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Manages accounts. 10 | */ 11 | public interface AccountManager { 12 | 13 | List getAllAccounts(); 14 | 15 | Profile findAccountByLoginInfo(LoginInfo loginInfo); 16 | 17 | boolean setPasswordHash(String accountId, String currentPasswordHash, String newPasswordHash); 18 | 19 | Profile findAccountByUserName(String username); 20 | 21 | Profile addAccount(String username, String passwordHash, String displayName); 22 | 23 | void updateDetail(Profile profile); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/bean/Profile.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.bean; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | 7 | import java.io.Serializable; 8 | 9 | @Data 10 | @EqualsAndHashCode(of = "id") 11 | public class Profile implements Serializable { 12 | public static final int STATE_ONLINE = 0; 13 | public static final int STATE_OFFLINE = 1; 14 | private static final long serialVersionUID = -4193811689167914857L; 15 | @JsonIgnore 16 | private String id; 17 | 18 | private String displayName; 19 | private String status; 20 | 21 | @JsonIgnore 22 | private int state; 23 | 24 | @JsonIgnore 25 | public boolean isOnline() { 26 | return state == STATE_ONLINE; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/view/swing/SwingViewFactory.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.view.swing; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.view.AbstractViewFactory; 4 | import com.blogspot.sontx.chatsocket.server.view.LogView; 5 | import com.blogspot.sontx.chatsocket.server.view.MainView; 6 | 7 | public class SwingViewFactory extends AbstractViewFactory { 8 | private MainWindow mainWindow; 9 | 10 | public SwingViewFactory() { 11 | register(MainView.class, this::createMainWindowIfNecessary); 12 | register(LogView.class, this::createMainWindowIfNecessary); 13 | } 14 | 15 | private MainWindow createMainWindowIfNecessary() { 16 | if (this.mainWindow == null) 17 | mainWindow = new MainWindow(); 18 | return mainWindow; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/model/handler/FriendInfoResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.model.handler; 2 | 3 | import com.blogspot.sontx.chatsocket.client.event.FriendInfoChangedEvent; 4 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 5 | import com.blogspot.sontx.chatsocket.lib.bean.Response; 6 | import com.blogspot.sontx.chatsocket.lib.bean.ResponseCode; 7 | import com.blogspot.sontx.chatsocket.lib.bo.ObjectTransmission; 8 | 9 | public class FriendInfoResponseHandler extends AbstractResponseHandler { 10 | @Override 11 | public void handle(ObjectTransmission transmission, Response response) throws Exception { 12 | if (response.getCode() == ResponseCode.OK) { 13 | post(new FriendInfoChangedEvent((Profile) response.getExtra())); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/platform/JavaFxPlatform.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.platform; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.service.MessageBoxService; 4 | import com.blogspot.sontx.chatsocket.lib.service.message.JavaFxMessageBox; 5 | import com.blogspot.sontx.chatsocket.lib.thread.JavaFxThreadInvoker; 6 | 7 | public abstract class JavaFxPlatform extends AbstractPlatform { 8 | protected JavaFxPlatform() { 9 | super(new JavaFxThreadInvoker()); 10 | MessageBoxService messageBoxService = new MessageBoxService(new JavaFxMessageBox()); 11 | messageBoxService.setPlatform(this); 12 | messageBoxService.start(); 13 | } 14 | 15 | @Override 16 | public void exit() { 17 | javafx.application.Platform.exit(); 18 | super.exit(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/resources/layouts/password-dialog.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/thread/AbstractThreadInvoker.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.thread; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.Function; 4 | 5 | import java.util.concurrent.ExecutorService; 6 | import java.util.concurrent.Executors; 7 | 8 | public abstract class AbstractThreadInvoker implements ThreadInvoker { 9 | private final ExecutorService executorService = Executors.newCachedThreadPool(); 10 | 11 | @Override 12 | public void executeAsync(Runnable callback) { 13 | executorService.execute(callback); 14 | } 15 | 16 | @Override 17 | @SuppressWarnings("unchecked") 18 | public T invokeWithResult(Function func) { 19 | Object[] result = new Object[1]; 20 | invokeAndWait(() -> result[0] = func.call()); 21 | return (T) result[0]; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/AbstractPresenter.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.event.AppShutdownEvent; 4 | import com.blogspot.sontx.chatsocket.lib.service.AbstractService; 5 | import com.blogspot.sontx.chatsocket.lib.view.BaseView; 6 | 7 | public abstract class AbstractPresenter extends AbstractService implements Presenter { 8 | protected final TView view; 9 | 10 | protected AbstractPresenter(TView view) { 11 | this.view = view; 12 | wireUpViewEvents(); 13 | } 14 | 15 | protected void wireUpViewEvents() { 16 | view.setOnClosingListener(this::onUserClosesView); 17 | } 18 | 19 | protected void onUserClosesView() { 20 | if (view.isMainWindow()) 21 | post(new AppShutdownEvent()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/view/AbstractViewFactory.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.view; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.Function; 4 | import com.blogspot.sontx.chatsocket.lib.NotRegisteredException; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public abstract class AbstractViewFactory implements ViewFactory { 10 | private final Map> views = new HashMap<>(); 11 | 12 | protected void register(Class viewType, Function factory) { 13 | views.put(viewType, factory); 14 | } 15 | 16 | @Override 17 | @SuppressWarnings("unchecked") 18 | public T create(Class viewType) { 19 | if (views.containsKey(viewType)) 20 | return (T) views.get(viewType).call(); 21 | throw new NotRegisteredException(""); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/event/ForwardChatMessageEvent.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.event; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.ChatMessage; 4 | import com.blogspot.sontx.chatsocket.lib.bean.Response; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * Chat message that already received from a client will 11 | * be forwarded to a {@link com.blogspot.sontx.chatsocket.server.model.Worker} to send to the receiver. 12 | * The {@link com.blogspot.sontx.chatsocket.server.model.Worker} should set the sent result to responseResult. 13 | */ 14 | @Data 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | public class ForwardChatMessageEvent { 18 | private ChatMessage forwardMessage; 19 | private String receiverId; 20 | private Response responseResult; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/model/UserProfile.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.model; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Data 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @EqualsAndHashCode(callSuper = true) 13 | public class UserProfile extends Profile { 14 | private String username; 15 | 16 | public void update(Profile profile) { 17 | if (profile != null) { 18 | super.setId(profile.getId()); 19 | super.setDisplayName(profile.getDisplayName()); 20 | super.setStatus(profile.getStatus()); 21 | super.setState(profile.getState()); 22 | } else { 23 | super.setId(""); 24 | } 25 | } 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/model/handler/LoginResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.model.handler; 2 | 3 | import com.blogspot.sontx.chatsocket.client.event.LoggedEvent; 4 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 5 | import com.blogspot.sontx.chatsocket.lib.bean.Response; 6 | import com.blogspot.sontx.chatsocket.lib.bean.ResponseCode; 7 | import com.blogspot.sontx.chatsocket.lib.bo.ObjectTransmission; 8 | 9 | public class LoginResponseHandler extends AbstractResponseHandler { 10 | @Override 11 | public void handle(ObjectTransmission transmission, Response response) throws Exception { 12 | if (response.getCode() == ResponseCode.OK) { 13 | post(new LoggedEvent((Profile) response.getExtra())); 14 | } else { 15 | showErrorMessage("Can not login", response.getExtra()); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/model/handler/RegisterResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.model.handler; 2 | 3 | import com.blogspot.sontx.chatsocket.client.event.RegisteredEvent; 4 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 5 | import com.blogspot.sontx.chatsocket.lib.bean.Response; 6 | import com.blogspot.sontx.chatsocket.lib.bean.ResponseCode; 7 | import com.blogspot.sontx.chatsocket.lib.bo.ObjectTransmission; 8 | 9 | public class RegisterResponseHandler extends AbstractResponseHandler { 10 | @Override 11 | public void handle(ObjectTransmission transmission, Response response) throws Exception { 12 | if (response.getCode() == ResponseCode.OK) { 13 | post(new RegisteredEvent((Profile) response.getExtra())); 14 | } else { 15 | showErrorMessage("Can not register new account", response.getExtra()); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/view/javafx/JavaFxViewFactory.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.view.javafx; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.view.AbstractViewFactory; 4 | import com.blogspot.sontx.chatsocket.server.view.LogView; 5 | import com.blogspot.sontx.chatsocket.server.view.MainView; 6 | import com.sun.javafx.application.PlatformImpl; 7 | 8 | public class JavaFxViewFactory extends AbstractViewFactory { 9 | private MainWindow mainWindow; 10 | 11 | public JavaFxViewFactory() { 12 | register(MainView.class, this::createMainWindowIfNecessary); 13 | register(LogView.class, this::createMainWindowIfNecessary); 14 | } 15 | 16 | private MainWindow createMainWindowIfNecessary() { 17 | if (mainWindow == null) { 18 | PlatformImpl.runAndWait(() -> mainWindow = new MainWindow()); 19 | } 20 | return mainWindow; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/model/handler/AccountInfoResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.model.handler; 2 | 3 | import com.blogspot.sontx.chatsocket.client.event.MyAccountInfoReceivedEvent; 4 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 5 | import com.blogspot.sontx.chatsocket.lib.bean.Response; 6 | import com.blogspot.sontx.chatsocket.lib.bean.ResponseCode; 7 | import com.blogspot.sontx.chatsocket.lib.bo.ObjectTransmission; 8 | 9 | public class AccountInfoResponseHandler extends AbstractResponseHandler { 10 | @Override 11 | public void handle(ObjectTransmission transmission, Response response) throws Exception { 12 | if (response.getCode() == ResponseCode.OK) { 13 | post(new MyAccountInfoReceivedEvent((Profile) response.getExtra())); 14 | } else { 15 | showErrorMessage("Can not fetch user info", response.getExtra()); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/model/handler/UpdateProfileResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.model.handler; 2 | 3 | import com.blogspot.sontx.chatsocket.client.event.MyAccountInfoReceivedEvent; 4 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 5 | import com.blogspot.sontx.chatsocket.lib.bean.Response; 6 | import com.blogspot.sontx.chatsocket.lib.bean.ResponseCode; 7 | import com.blogspot.sontx.chatsocket.lib.bo.ObjectTransmission; 8 | 9 | public class UpdateProfileResponseHandler extends AbstractResponseHandler { 10 | @Override 11 | public void handle(ObjectTransmission transmission, Response response) throws Exception { 12 | if (response.getCode() == ResponseCode.Fail) { 13 | showErrorMessage("Can not update user profile", response.getExtra()); 14 | } else { 15 | post(new MyAccountInfoReceivedEvent((Profile) response.getExtra())); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/view/javafx/custom/ChatMessageCell.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.view.javafx.custom; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bo.LayoutsResource; 4 | import javafx.fxml.FXMLLoader; 5 | import javafx.geometry.HPos; 6 | import javafx.scene.control.Label; 7 | import javafx.scene.layout.GridPane; 8 | 9 | import java.io.IOException; 10 | 11 | public class ChatMessageCell extends Label { 12 | public ChatMessageCell(ChatMessageInternal chatMessage) throws IOException { 13 | String layoutName = chatMessage.isMe() ? "chat-message-mine.fxml" : "chat-message-friend.fxml"; 14 | FXMLLoader loader = new FXMLLoader(LayoutsResource.getInstance().getResource(layoutName)); 15 | loader.setRoot(this); 16 | loader.setController(this); 17 | loader.load(); 18 | setText(chatMessage.getContent()); 19 | GridPane.setHalignment(this, chatMessage.isMe() ? HPos.RIGHT : HPos.LEFT); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/model/handler/FriendListResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.model.handler; 2 | 3 | import com.blogspot.sontx.chatsocket.client.event.FriendListReceivedEvent; 4 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 5 | import com.blogspot.sontx.chatsocket.lib.bean.Response; 6 | import com.blogspot.sontx.chatsocket.lib.bean.ResponseCode; 7 | import com.blogspot.sontx.chatsocket.lib.bo.ObjectTransmission; 8 | 9 | import java.util.List; 10 | 11 | public class FriendListResponseHandler extends AbstractResponseHandler { 12 | @Override 13 | @SuppressWarnings("unchecked") 14 | public void handle(ObjectTransmission transmission, Response response) throws Exception { 15 | if (response.getCode() == ResponseCode.OK) { 16 | post(new FriendListReceivedEvent((List) response.getExtra())); 17 | } else { 18 | showErrorMessage("Can not fetch friend list", response.getExtra()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/service/message/SwingMessageBox.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.service.message; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bo.ImagesResource; 4 | 5 | import javax.swing.*; 6 | 7 | public class SwingMessageBox implements MessageBox { 8 | @Override 9 | public void show(String caption, String text, MessageType type) { 10 | JLabel labelMessage = new JLabel(text, getImageIconByType(type), SwingConstants.CENTER); 11 | final JComponent[] components = new JComponent[]{labelMessage}; 12 | JOptionPane.showMessageDialog(null, components, caption, JOptionPane.PLAIN_MESSAGE); 13 | } 14 | 15 | private ImageIcon getImageIconByType(MessageType type) { 16 | switch (type) { 17 | case Error: 18 | return new ImageIcon(ImagesResource.getInstance().getImageByName("msgbox-error.png")); 19 | case Info: 20 | return new ImageIcon(ImagesResource.getInstance().getImageByName("msgbox-info.png")); 21 | } 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/thread/SwingThreadInvoker.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.thread; 2 | 3 | import lombok.extern.log4j.Log4j; 4 | 5 | import javax.swing.*; 6 | import java.awt.*; 7 | import java.lang.reflect.InvocationTargetException; 8 | 9 | @Log4j 10 | public class SwingThreadInvoker extends AbstractThreadInvoker { 11 | @Override 12 | public void invokeLater(Runnable runnable) { 13 | if (EventQueue.isDispatchThread()) { 14 | executeAsync(() -> invokeLater(runnable)); 15 | } else { 16 | SwingUtilities.invokeLater(runnable); 17 | } 18 | } 19 | 20 | @Override 21 | public void invokeAndWait(Runnable runnable) { 22 | if (EventQueue.isDispatchThread()) { 23 | runnable.run(); 24 | } else { 25 | try { 26 | SwingUtilities.invokeAndWait(runnable); 27 | } catch (InterruptedException | InvocationTargetException e) { 28 | log.error("Error while invoking method in UI thread", e); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/model/handler/ChatMessageResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.model.handler; 2 | 3 | import com.blogspot.sontx.chatsocket.client.model.ChattingManager; 4 | import com.blogspot.sontx.chatsocket.lib.bean.ChatMessage; 5 | import com.blogspot.sontx.chatsocket.lib.bean.Response; 6 | import com.blogspot.sontx.chatsocket.lib.bo.ObjectTransmission; 7 | 8 | public class ChatMessageResponseHandler extends AbstractResponseHandler { 9 | private final ChattingManager chattingManager; 10 | 11 | ChatMessageResponseHandler(ChattingManager chattingManager) { 12 | this.chattingManager = chattingManager; 13 | } 14 | 15 | @Override 16 | public void handle(ObjectTransmission transmission, Response response) throws Exception { 17 | if (response.getExtra() instanceof ChatMessage) { 18 | ChatMessage chatMessage = (ChatMessage) response.getExtra(); 19 | chattingManager.processReceivedChatMessage(chatMessage); 20 | } else { 21 | chattingManager.processSentChatMessage(response); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 No Em 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/presenter/Log4jUIAppender.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.presenter; 2 | 3 | import com.blogspot.sontx.chatsocket.server.view.LogView; 4 | import org.apache.log4j.AppenderSkeleton; 5 | import org.apache.log4j.PatternLayout; 6 | import org.apache.log4j.spi.LoggingEvent; 7 | 8 | /** 9 | * Tracks the log from {@link org.apache.log4j.Logger} log4j and shows to the UI. 10 | */ 11 | class Log4jUIAppender extends AppenderSkeleton { 12 | private final LogView logView; 13 | 14 | Log4jUIAppender(LogView logView) { 15 | this.logView = logView; 16 | setLayout(new PatternLayout("%m")); 17 | } 18 | 19 | private void appendLine(String msg) { 20 | logView.appendLog(msg); 21 | } 22 | 23 | @Override 24 | protected void append(LoggingEvent event) { 25 | String formattedMessage = getLayout().format(event); 26 | appendLine(formattedMessage); 27 | } 28 | 29 | @Override 30 | public void close() { 31 | logView.clearLog(); 32 | } 33 | 34 | @Override 35 | public boolean requiresLayout() { 36 | return false; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/view/swing/custom/FriendEntry.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.view.swing.custom; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | import com.blogspot.sontx.chatsocket.lib.bo.ImagesResource; 5 | import lombok.Getter; 6 | 7 | import javax.swing.*; 8 | 9 | public class FriendEntry { 10 | @Getter 11 | private final Profile profile; 12 | 13 | public FriendEntry(Profile profile) { 14 | this.profile = profile; 15 | } 16 | 17 | public boolean contains(Profile profile) { 18 | return profile != null && profile.getId() == this.profile.getId(); 19 | } 20 | 21 | public String getDisplayName() { 22 | return profile.getDisplayName(); 23 | } 24 | 25 | ImageIcon getImage() { 26 | String imageName = profile.isOnline() ? "online.png" : "offline.png"; 27 | return new ImageIcon(ImagesResource.getInstance().getImageByName(imageName)); 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return getDisplayName(); 33 | } 34 | 35 | String getStatus() { 36 | return profile.getStatus(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/view/FitImageJLabel.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.view; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | 6 | public class FitImageJLabel extends JLabel { 7 | 8 | private float getScaleValue(int iconWidth, int iconHeight) { 9 | int deltaWidth = iconWidth - getWidth(); 10 | int deltaHeight = iconHeight - getHeight(); 11 | return deltaWidth > deltaHeight ? getWidth() / (float) iconWidth : getHeight() / (float) iconHeight; 12 | } 13 | 14 | private Icon convertIconToFit(Icon icon) { 15 | ImageIcon imageIcon = (ImageIcon) icon; 16 | Image image = imageIcon.getImage(); 17 | float scale = getScaleValue(icon.getIconWidth(), icon.getIconHeight()); 18 | Image scaledImage = image.getScaledInstance( 19 | (int) (scale * icon.getIconWidth()), 20 | (int) (scale * icon.getIconHeight()), 21 | Image.SCALE_SMOOTH); 22 | return new ImageIcon(scaledImage); 23 | } 24 | 25 | @Override 26 | public void setIcon(Icon icon) { 27 | super.setIcon(icon != null ? convertIconToFit(icon) : null); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/view/javafx/AddressDialog.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.view.javafx; 2 | 3 | import com.blogspot.sontx.chatsocket.AppConfig; 4 | import com.blogspot.sontx.chatsocket.lib.Callback; 5 | import com.blogspot.sontx.chatsocket.lib.utils.NetworkUtils; 6 | import javafx.scene.control.ChoiceDialog; 7 | import lombok.Setter; 8 | 9 | import java.util.List; 10 | import java.util.Optional; 11 | 12 | public class AddressDialog { 13 | @Setter 14 | private Callback onSelectedAddress; 15 | 16 | public void show() { 17 | List addresses = NetworkUtils.getAllAddresses(); 18 | if (!addresses.isEmpty()) { 19 | ChoiceDialog choiceDialog = new ChoiceDialog<>(addresses.get(0), addresses); 20 | choiceDialog.setTitle(AppConfig.getDefault().getAppName()); 21 | choiceDialog.setHeaderText("Available Addresses"); 22 | Optional result = choiceDialog.showAndWait(); 23 | if (onSelectedAddress != null && result.isPresent()) 24 | onSelectedAddress.call(result.get()); 25 | choiceDialog.close(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/Component.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.platform.Platform; 4 | import com.blogspot.sontx.chatsocket.lib.settings.SettingsLoader; 5 | import lombok.AccessLevel; 6 | import lombok.Getter; 7 | import lombok.Setter; 8 | 9 | public abstract class Component { 10 | @Setter 11 | @Getter(AccessLevel.PROTECTED) 12 | private Platform platform; 13 | 14 | protected T getSetting(Class settingType) { 15 | return platform.getSettings().get(settingType); 16 | } 17 | 18 | protected void post(Object event) { 19 | platform.postEvent(event); 20 | } 21 | 22 | protected void runOnUiThread(Runnable callback) { 23 | runOnUiThread(callback, true); 24 | } 25 | 26 | protected void runOnUiThread(Runnable callback, boolean wait) { 27 | if (wait) 28 | platform.getThreadInvoker().invokeAndWait(callback); 29 | else 30 | platform.getThreadInvoker().invokeLater(callback); 31 | } 32 | 33 | protected void runAsync(Runnable callback) { 34 | platform.getThreadInvoker().executeAsync(callback); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/view/swing/custom/FriendCellRenderer.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.view.swing.custom; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.view.TwoLineJLabel; 4 | 5 | import javax.swing.*; 6 | import java.awt.*; 7 | 8 | public class FriendCellRenderer extends TwoLineJLabel implements ListCellRenderer { 9 | private static final Color HIGHLIGHT_COLOR = new Color(0, 0, 128); 10 | 11 | public FriendCellRenderer() { 12 | setOpaque(true); 13 | setIconTextGap(12); 14 | } 15 | 16 | public Component getListCellRendererComponent( 17 | @SuppressWarnings("rawtypes") JList list, 18 | FriendEntry value, 19 | int index, 20 | boolean isSelected, 21 | boolean cellHasFocus) { 22 | 23 | setText(value.getDisplayName(), value.getStatus()); 24 | setIcon(value.getImage()); 25 | 26 | if (isSelected) { 27 | setBackground(HIGHLIGHT_COLOR); 28 | setForeground(Color.white); 29 | } else { 30 | setBackground(Color.white); 31 | setForeground(Color.black); 32 | } 33 | 34 | return this; 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/view/ClickableJLabel.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.view; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | import java.awt.event.ActionEvent; 6 | import java.awt.event.ActionListener; 7 | import java.awt.event.MouseAdapter; 8 | import java.awt.event.MouseEvent; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public class ClickableJLabel extends JLabel { 13 | private List actionListeners = new ArrayList<>(); 14 | 15 | public ClickableJLabel(String s) { 16 | super(s); 17 | setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 18 | addMouseListener(new MouseAdapter() { 19 | @Override 20 | public void mouseClicked(MouseEvent e) { 21 | ActionEvent event = new ActionEvent(ClickableJLabel.this, ActionEvent.ACTION_PERFORMED, "clicked"); 22 | for (ActionListener actionListener : actionListeners) { 23 | actionListener.actionPerformed(event); 24 | } 25 | } 26 | }); 27 | } 28 | 29 | public void addActionListener(ActionListener l) { 30 | actionListeners.add(l); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/resources/layouts/friend-list-view-cell.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 27 | 29 | 30 |
31 | 32 | 33 | 34 |
35 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/utils/NetworkUtils.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.utils; 2 | 3 | import java.net.InetAddress; 4 | import java.net.NetworkInterface; 5 | import java.net.SocketException; 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | import java.util.Enumeration; 9 | import java.util.List; 10 | 11 | public final class NetworkUtils { 12 | 13 | private NetworkUtils() { 14 | 15 | } 16 | 17 | public static List getAllAddresses() { 18 | Enumeration interfaces; 19 | try { 20 | interfaces = NetworkInterface.getNetworkInterfaces(); 21 | } catch (SocketException ignored) { 22 | return Collections.emptyList(); 23 | } 24 | 25 | List ips = new ArrayList<>(); 26 | while (interfaces.hasMoreElements()) { 27 | NetworkInterface netInterface = interfaces.nextElement(); 28 | Enumeration addresses = netInterface.getInetAddresses(); 29 | while (addresses.hasMoreElements()) { 30 | InetAddress address = addresses.nextElement(); 31 | if (address.getAddress().length == 4) 32 | ips.add(address.getHostAddress()); 33 | } 34 | } 35 | return ips; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/model/handler/FriendListRequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.model.handler; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Profile; 4 | import com.blogspot.sontx.chatsocket.lib.bean.Response; 5 | import com.blogspot.sontx.chatsocket.server.event.RequestReceivedEvent; 6 | import com.blogspot.sontx.chatsocket.server.model.account.AccountManager; 7 | 8 | import java.util.List; 9 | import java.util.stream.Collectors; 10 | 11 | /** 12 | * Handles friend list request from the client. 13 | */ 14 | class FriendListRequestHandler extends AbstractRequestHandler { 15 | private final AccountManager accountManager; 16 | 17 | FriendListRequestHandler(AccountManager accountManager) { 18 | this.accountManager = accountManager; 19 | } 20 | 21 | @Override 22 | Response handleWithAuthenticated(RequestReceivedEvent event) throws Exception { 23 | List accounts = accountManager.getAllAccounts(); 24 | 25 | String exceptId = event.getProfile().getId(); 26 | List friends = accounts 27 | .stream() 28 | .filter(account -> !account.getId().equals(exceptId)) 29 | .collect(Collectors.toList()); 30 | 31 | return okResponse(friends, event.getRequest().getCode()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/client/platform/ClientJavaFxPlatform.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.client.platform; 2 | 3 | import com.blogspot.sontx.chatsocket.client.view.javafx.JavaFxViewFactory; 4 | import com.blogspot.sontx.chatsocket.lib.platform.JavaFxPlatform; 5 | import com.blogspot.sontx.chatsocket.lib.view.ViewFactory; 6 | import javafx.application.Application; 7 | import javafx.stage.Stage; 8 | import lombok.Getter; 9 | 10 | public class ClientJavaFxPlatform extends JavaFxPlatform { 11 | private static final Object lock = new Object(); 12 | @Getter 13 | private final ViewFactory viewFactory; 14 | 15 | public ClientJavaFxPlatform() { 16 | viewFactory = new JavaFxViewFactory(); 17 | startEntryApp(); 18 | } 19 | 20 | private void startEntryApp() { 21 | new Thread(() -> Application.launch(App.class)).start(); 22 | synchronized (lock) { 23 | try { 24 | lock.wait(); 25 | } catch (InterruptedException e) { 26 | e.printStackTrace(); 27 | } 28 | } 29 | } 30 | 31 | public static class App extends Application { 32 | 33 | @Override 34 | public void start(Stage primaryStage) { 35 | synchronized (lock) { 36 | lock.notify(); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/platform/ServerJavaFxPlatform.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.platform; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.platform.JavaFxPlatform; 4 | import com.blogspot.sontx.chatsocket.lib.view.ViewFactory; 5 | import com.blogspot.sontx.chatsocket.server.view.javafx.JavaFxViewFactory; 6 | import javafx.application.Application; 7 | import javafx.stage.Stage; 8 | import lombok.Getter; 9 | 10 | public class ServerJavaFxPlatform extends JavaFxPlatform { 11 | private static final Object lock = new Object(); 12 | @Getter 13 | private final ViewFactory viewFactory; 14 | 15 | public ServerJavaFxPlatform() { 16 | viewFactory = new JavaFxViewFactory(); 17 | startEntryApp(); 18 | } 19 | 20 | private void startEntryApp() { 21 | new Thread(() -> Application.launch(App.class)).start(); 22 | synchronized (lock) { 23 | try { 24 | lock.wait(); 25 | } catch (InterruptedException e) { 26 | e.printStackTrace(); 27 | } 28 | } 29 | } 30 | 31 | public static class App extends Application { 32 | 33 | @Override 34 | public void start(Stage primaryStage) { 35 | synchronized (lock) { 36 | lock.notify(); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/lib/platform/AbstractPlatform.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.lib.platform; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.settings.JsonSettingsLoader; 4 | import com.blogspot.sontx.chatsocket.lib.settings.SettingsLoader; 5 | import com.blogspot.sontx.chatsocket.lib.thread.ThreadInvoker; 6 | import com.google.common.eventbus.EventBus; 7 | import lombok.Getter; 8 | 9 | public abstract class AbstractPlatform implements Platform { 10 | @Getter 11 | private final ThreadInvoker threadInvoker; 12 | private final EventBus eventBus; 13 | @Getter 14 | private final SettingsLoader settings; 15 | 16 | protected AbstractPlatform(ThreadInvoker threadInvoker) { 17 | this.threadInvoker = threadInvoker; 18 | eventBus = new EventBus(getClass().getName()); 19 | settings = new JsonSettingsLoader("app.json"); 20 | } 21 | 22 | @Override 23 | public void attach(Object service) { 24 | eventBus.register(service); 25 | } 26 | 27 | @Override 28 | public void detach(Object service) { 29 | eventBus.unregister(service); 30 | } 31 | 32 | @Override 33 | public void postEvent(Object event) { 34 | eventBus.post(event); 35 | } 36 | 37 | @Override 38 | public void exit() { 39 | settings.save(); 40 | System.exit(0); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/blogspot/sontx/chatsocket/server/model/RequestRouter.java: -------------------------------------------------------------------------------- 1 | package com.blogspot.sontx.chatsocket.server.model; 2 | 3 | import com.blogspot.sontx.chatsocket.lib.bean.Request; 4 | import com.blogspot.sontx.chatsocket.lib.service.AbstractService; 5 | import com.blogspot.sontx.chatsocket.server.event.RequestReceivedEvent; 6 | import com.blogspot.sontx.chatsocket.server.model.handler.RequestHandler; 7 | import com.blogspot.sontx.chatsocket.server.model.handler.RequestHandlerFactory; 8 | import com.google.common.eventbus.Subscribe; 9 | import lombok.extern.log4j.Log4j; 10 | 11 | /** 12 | * Routes incomming requests from clients to handlers. 13 | */ 14 | @Log4j 15 | public class RequestRouter extends AbstractService { 16 | private final RequestHandlerFactory requestHandlerFactory; 17 | 18 | public RequestRouter(RequestHandlerFactory requestHandlerFactory) { 19 | this.requestHandlerFactory = requestHandlerFactory; 20 | } 21 | 22 | @Subscribe 23 | public void onRequestReceived(RequestReceivedEvent event) { 24 | runAsync(() -> { 25 | Request request = event.getRequest(); 26 | RequestHandler handler = requestHandlerFactory.create(request.getCode()); 27 | try { 28 | handler.handle(event); 29 | } catch (Exception e) { 30 | log.error("Handle request", e); 31 | } 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/resources/layouts/chat-window.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 |
16 |
17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 | -------------------------------------------------------------------------------- /src/main/resources/layouts/register-window.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |