PageRenderTime 70ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 1ms

/src/main/java/activitystreamer/server/Control.java

https://bitbucket.org/kinsey1/distributed-client-server
Java | 961 lines | 698 code | 112 blank | 151 comment | 160 complexity | 91c0ba8f0f090b610c1ffc247e3c1f7f MD5 | raw file
  1. package activitystreamer.server;
  2. import activitystreamer.Server;
  3. import activitystreamer.util.Settings;
  4. import org.apache.logging.log4j.LogManager;
  5. import org.apache.logging.log4j.Logger;
  6. import org.json.simple.JSONArray;
  7. import org.json.simple.JSONObject;
  8. import org.json.simple.parser.ParseException;
  9. import java.io.IOException;
  10. import java.net.Socket;
  11. import java.util.ArrayList;
  12. import java.util.HashMap;
  13. import java.util.Iterator;
  14. /**
  15. * Class which handles controlling all connections
  16. */
  17. public class Control extends Thread {
  18. private static final Logger log = LogManager.getLogger();
  19. protected static Control control = null;
  20. private static ArrayList<Connection> connections;
  21. private static ArrayList<Client> clients;
  22. private static ArrayList<ServerInfo> servers;
  23. //TODO fill this arraylist @kinsey
  24. private static HashMap<Connection, ServerInfo> serverConnections;
  25. private static HashMap<Connection, Client> clientConnections;
  26. private static HashMap<String, RegisterLock> registerLocks;
  27. private static boolean term = false;
  28. private static Listener listener;
  29. private static Connection remoteServer;
  30. private static String ID;
  31. private Client anon;
  32. private Integer check = 0;
  33. public Control() {
  34. Server.starting = true;
  35. // Initialize maps which map connections to clients / servers
  36. this.ID = Settings.generateID();
  37. log.info("SERVER ID : " + this.ID);
  38. connections = new ArrayList<Connection>();
  39. clientConnections = new HashMap<Connection, Client>();
  40. serverConnections = new HashMap<Connection, ServerInfo>();
  41. registerLocks = new HashMap<String, RegisterLock>();
  42. //Initiate and add a client to the clients array
  43. clients = new ArrayList<Client>();
  44. //Anonymous client
  45. anon = new Client("anonymous", "null");
  46. clients.add(anon);
  47. //Initiate an outgoing connection to the first other server
  48. if (Settings.getRemoteHostname() != null) {
  49. initiateConnection(true, Settings.getRemoteHostname(), Settings.getRemotePort(), null);
  50. } else {
  51. log.info("First server in the network");
  52. }
  53. try {
  54. listener = new Listener();
  55. } catch (IOException e1) {
  56. log.fatal("failed to startup a listening thread: " + e1);
  57. System.exit(-1);
  58. }
  59. this.start();
  60. Server.starting = false;
  61. }
  62. public static Control getInstance() {
  63. while (Server.starting) {
  64. log.debug("Waiting on instantiation");
  65. continue;
  66. }
  67. if (control == null) {
  68. control = new Control();
  69. }
  70. return control;
  71. }
  72. /**
  73. * Connect to another server. Used to create the first connection and
  74. * then the subsequent connections once all the server info has been
  75. * received.
  76. *
  77. * @param initial if this is the first connection
  78. */
  79. public boolean initiateConnection(boolean initial, String remoteHost, int remotePort, String id) {
  80. log.info("Attempt connecting to : rp:" + remotePort + " rh : " + remoteHost);
  81. Socket socket;
  82. Connection remoteCon;
  83. try {
  84. socket = new Socket(remoteHost, remotePort);
  85. remoteCon = outgoingConnection(socket);
  86. OutgoingMessage authMsg;
  87. if (initial) {
  88. authMsg = new OutgoingMessage("AUTHENTICATE", Settings.getSecret(), this.ID, Settings.getLocalHostname(), Settings.getLocalPort());
  89. log.info("Sending init auth message: " + authMsg.getJsonReply());
  90. remoteCon.setAuthenticate(true);
  91. } else {
  92. authMsg = new OutgoingMessage("CONNECTION_REQUEST", Settings.getSecret(), this.ID, Settings.getLocalHostname(), Settings.getLocalPort());
  93. log.info("Sending connection request auth message: " + authMsg.getJsonReply());
  94. remoteCon.setAuthenticate(true);
  95. }
  96. remoteCon.writeMsg(authMsg.getJsonReply());
  97. connections.add(remoteCon);
  98. log.debug("adding new server");
  99. if (id != null) {
  100. ServerInfo authServer = new ServerInfo(remoteHost, remotePort, id);
  101. this.serverConnections.put(remoteCon, authServer);
  102. } else {
  103. ServerInfo authServer = new ServerInfo(remoteHost, remotePort);
  104. this.serverConnections.put(remoteCon, authServer);
  105. }
  106. return true;
  107. } catch (IOException e) {
  108. log.error("failed to make connection to " + Settings.getRemoteHostname() + ":" + Settings.getRemotePort() + " :" + e);
  109. return false;
  110. }
  111. }
  112. /**
  113. * Authenticates an incoming server connection.
  114. *
  115. * @return
  116. * @throws IOException
  117. */
  118. private boolean authenticateServer(boolean initial, Connection con, IncomingMessage incoming) {
  119. //check if secret is correct and its not already an authenticated server
  120. String secret = incoming.jsonMessage.get("secret").toString();
  121. String host = incoming.jsonMessage.get("lh").toString();
  122. String ID = incoming.jsonMessage.get("id").toString();
  123. int port = Integer.parseInt(incoming.jsonMessage.get("port").toString());
  124. if (secret.equals(Settings.getSecret())) {
  125. con.setAuthenticate(true);
  126. con.setServer(true);
  127. ServerInfo authServer = new ServerInfo(host, port, ID);
  128. if (initial) {
  129. log.debug("SENDING AUTH_INFO MESSAGE");
  130. OutgoingMessage msg = new OutgoingMessage(serverConnections, clients, this.ID);
  131. con.writeMsg(msg.getJsonReply().toString());
  132. }
  133. //Send the auth server info then add it to the connections
  134. this.serverConnections.put(con, authServer);
  135. return true;
  136. }
  137. return false;
  138. }
  139. /**
  140. * Processes incoming server announce. Updates the serverInfo object
  141. * which is mapped to this connection.
  142. *
  143. * @param msg
  144. * @param con
  145. * @return
  146. */
  147. private boolean processAnnounce(IncomingMessage msg, Connection con) {
  148. if (con.isAuthenticated() && serverConnections.containsKey(con)) {
  149. ServerInfo announcingServer = serverConnections.get(con);
  150. String hostname = msg.jsonMessage.get("hostname").toString();
  151. String ID = msg.jsonMessage.get("id").toString();
  152. JSONArray clients = (JSONArray) msg.jsonMessage.get("clients");
  153. for (int i = 0; i < clients.size(); i++) {
  154. String username = clients.get(i).toString();
  155. announcingServer.setConnectedClient(username);
  156. }
  157. int port = Integer.parseInt(msg.jsonMessage.get("port").toString());
  158. int load = Integer.parseInt(msg.jsonMessage.get("load").toString());
  159. announcingServer.setHostname(hostname);
  160. announcingServer.setID(ID);
  161. announcingServer.setLoad(load);
  162. announcingServer.setPort(port);
  163. announcingServer.setValid(true);
  164. JSONArray servClients = (JSONArray) msg.jsonMessage.get("remoteServerDB");
  165. log.debug("Checking message number ");
  166. for (int i = 0; i < servClients.size(); i++) {
  167. JSONObject client = (JSONObject) servClients.get(i);
  168. String username = (String) client.get("clientUsername");
  169. Integer messageNumber = Integer.parseInt(client.get("messageNumber").toString());
  170. log.debug("Going through the current servers client connections now");
  171. for (Client clie : clientConnections.values()) {
  172. if (clie.getUsername().equals(username)) {
  173. log.debug("Check if condition on message number ");
  174. log.debug(messageNumber != clie.getMessageNumber());
  175. if (messageNumber != clie.getMessageNumber()) {
  176. MessageInfo retMsg = new MessageInfo();
  177. retMsg = clie.getFromMessageDB(messageNumber);
  178. while (retMsg != null) {
  179. log.debug("SENDING CORRECT MESSAGE ORDER");
  180. OutgoingMessage repeatMsg = new OutgoingMessage(
  181. "CORRECT_MESSAGE_ORDER", messageNumber + 1, retMsg
  182. );
  183. log.debug("Correct order message is " + repeatMsg.getJsonReply());
  184. con.writeMsg(repeatMsg.getJsonReply());
  185. retMsg = clie.getFromMessageDB(++messageNumber);
  186. try {
  187. wait(1000);
  188. } catch (Exception e) {
  189. }
  190. }
  191. }
  192. }
  193. }
  194. }
  195. return true;
  196. }
  197. return false;
  198. }
  199. /**
  200. * Processes an incoming login message and
  201. * returns the relevant OutgoingMessge type.
  202. *
  203. * @param msg
  204. * @param con
  205. * @return
  206. */
  207. private OutgoingMessage processLogin(IncomingMessage msg, Connection con) {
  208. String username, secret;
  209. // The following block ensures that a username and secret is sent by the client
  210. try {
  211. //Anonymous login
  212. username = msg.jsonMessage.get("username").toString();
  213. if (username.equals("anonymous")) {
  214. clientConnections.put(con, anon);
  215. con.setServer(false);
  216. return new OutgoingMessage("LOGIN_SUCCESS",
  217. " Unauthenticated login as : " + username);
  218. } else {
  219. secret = msg.jsonMessage.get("secret").toString();
  220. }
  221. } catch (Exception e) {
  222. return new OutgoingMessage("INVALID_MESSAGE",
  223. "No secret provided");
  224. }
  225. for (Client cli : clients) {
  226. if (cli.getUsername().equals(username)) {
  227. if (cli.getSecret().equals(secret)) {
  228. clientConnections.put(con, cli);
  229. return new OutgoingMessage("LOGIN_SUCCESS",
  230. "logged in as user " + username);
  231. } else {
  232. return new OutgoingMessage("LOGIN_FAILED",
  233. "incorrect secret - " + secret
  234. + " provided for user " + username);
  235. }
  236. }
  237. }
  238. return new OutgoingMessage("LOGIN_FAILED",
  239. "no registered user with username - " + username);
  240. }
  241. /**
  242. * Registers a new client, takes an incoming message
  243. * which has the type of register. Broadcasts the request
  244. * to other servers as a LOCK_REQUEST.
  245. * The user is not registered until LOCK_ALLOWED has come back
  246. * from every server
  247. *
  248. * @param incoming
  249. * @return
  250. */
  251. private boolean registerClient(IncomingMessage incoming, Connection con) {
  252. String username = incoming.jsonMessage.get("username").toString();
  253. String secret = incoming.jsonMessage.get("secret").toString();
  254. Boolean available = true;
  255. for (Client curClient : clients) {
  256. if (curClient.getUsername().equals(username)) {
  257. available = false;
  258. }
  259. }
  260. // If its locally available send out lock request
  261. if (available) {
  262. OutgoingMessage lockMessage = new OutgoingMessage("LOCK_REQUEST", username, secret);
  263. log.debug("BROADCASTING " + lockMessage.getJsonReply());
  264. broadcastActivity(lockMessage, false, true);
  265. registerLocks.put(username, new RegisterLock(new Client(username, secret), serverConnections.size(), con));
  266. return true;
  267. }
  268. return false;
  269. }
  270. /**
  271. * Checks the registered users if a user is already registered
  272. * -> used for lock requests
  273. *
  274. * @param incoming
  275. * @return
  276. */
  277. private boolean checkRegisteredUsers(IncomingMessage incoming) {
  278. String username = incoming.jsonMessage.get("username").toString();
  279. for (Client client : clients) {
  280. if (username.equals(client.getUsername())) {
  281. return false;
  282. }
  283. }
  284. return true;
  285. }
  286. public void activityMessageBroadcast(OutgoingMessage message, boolean client, boolean server) {
  287. if (!server) {
  288. for (Connection con : serverConnections.keySet()) {
  289. ServerInfo ser = serverConnections.get(con);
  290. if (ser.getMessageDB().containsKey(message.reply.get("clientUsername").toString())) {
  291. Integer prevMsgNumber = ser.getMessageDB().get(message.reply.get("clientUsername").toString());
  292. if (!((prevMsgNumber + 1) ==
  293. (Integer.parseInt(message.reply.get("messageNumber").toString())))) {
  294. /*Integer msgNumber = Integer.parseInt(message.reply.get("messageNumber").toString());
  295. if( ((check==0 || check==1) && ((msgNumber==2) || (msgNumber==3)))){
  296. check++;
  297. return;
  298. }
  299. if ( (msgNumber ==4) && (this.check ==0)){
  300. */
  301. log.debug("SENDING INCORRECT MESSAGE PROTOCOL");
  302. JSONObject incorrectOrder = new JSONObject();
  303. incorrectOrder.put("command", "INCORRECT_MESSAGE_ORDER");
  304. incorrectOrder.put("serverId", Control.ID);
  305. incorrectOrder.put("clientUsername", message.reply.get("clientUsername").toString());
  306. incorrectOrder.put("previousMsgNumber", prevMsgNumber);
  307. con.writeMsg(incorrectOrder.toJSONString());
  308. this.check++;
  309. return;
  310. }
  311. ser.getMessageDB().replace(message.reply.get("clientUsername").toString(),
  312. Integer.parseInt(message.reply.get("messageNumber").toString()));
  313. } else {
  314. ser.getMessageDB().put(message.reply.get("clientUsername").toString(),
  315. Integer.parseInt(message.reply.get("messageNumber").toString()));
  316. }
  317. }
  318. } else if (server) {
  319. for (Connection con : serverConnections.keySet()) {
  320. log.debug("broadcast to servers" + message.getJsonReply());
  321. con.writeMsg(message.getJsonReply());
  322. }
  323. }
  324. if (client) {
  325. log.debug("I AM DISPLAYING THE 4th MESSAGE");
  326. log.debug("THE MESSAGE IS " + message.getJsonReply());
  327. JSONArray snapshot = (JSONArray) message.reply.get("snapshot");
  328. for (int i = 0; i < snapshot.size(); i++) {
  329. JSONObject serverClients = (JSONObject) snapshot.get(i);
  330. log.debug("EVERY SERVER ID " + serverClients.get("SID").toString());
  331. if (serverClients.get("SID").toString().equals(Control.ID)) {
  332. log.debug("I AM IN THE SERVER AND NOW PREPARED TO SEND THE MESSAGE");
  333. JSONArray clients = (JSONArray) serverClients.get("Clients");
  334. for (int j = 0; j < clients.size(); j++) {
  335. log.debug("The client array is " + clients.toJSONString());
  336. for (Connection con : clientConnections.keySet()) {
  337. log.debug("CHECK IF CONDITION");
  338. log.debug(clientConnections.get(con).getUsername().equals(clients.get(j).toString()));
  339. if (clientConnections.get(con).getUsername().equals(clients.get(j).toString())) {
  340. JSONObject cliMsg = new JSONObject();
  341. cliMsg.put("command", "ACTIVITY_BROADCAST");
  342. cliMsg.put("activity", message.reply.get("activity"));
  343. log.error("broadcast to client " + clients.get(j).toString() + " "
  344. + message.getJsonReply());
  345. con.writeMsg(cliMsg.toJSONString());
  346. }
  347. }
  348. }
  349. }
  350. }
  351. }
  352. }
  353. /**
  354. * Function to broadcast a message to connected clients and servers
  355. *
  356. * @param message
  357. * @param client to indicate whether this message came from a client
  358. * or was broadcast from other servers.
  359. * @param server
  360. */
  361. public void broadcastActivity(OutgoingMessage message, boolean client, boolean server) {
  362. if (client) {
  363. for (Connection con : clientConnections.keySet()) {
  364. log.debug("broadcast to clients" + message.getJsonReply());
  365. con.writeMsg(message.getJsonReply());
  366. }
  367. }
  368. if (server) {
  369. for (Connection con : serverConnections.keySet()) {
  370. log.debug("broadcast to servers" + message.getJsonReply());
  371. con.writeMsg(message.getJsonReply());
  372. }
  373. }
  374. }
  375. /**
  376. * Function which takes the incoming message and processes it. Sends a
  377. * connection too all other servers its connecting to.
  378. *
  379. * @return
  380. */
  381. public void processAuthenticationInfo(IncomingMessage message) {
  382. JSONArray clients = (JSONArray) message.jsonMessage.get("clients");
  383. JSONArray servers = (JSONArray) message.jsonMessage.get("servers");
  384. log.debug(message.toString());
  385. for (int i = 0; i < servers.size(); i++) {
  386. JSONObject server = (JSONObject) servers.get(i);
  387. String rh = server.get("rh").toString();
  388. String id = server.get("id").toString();
  389. int rp = Integer.parseInt(server.get("rp").toString());
  390. initiateConnection(false, rh, rp, id);
  391. }
  392. for (int i = 0; i < clients.size(); i++) {
  393. JSONObject jsonClient = (JSONObject) clients.get(i);
  394. String username = jsonClient.get("username").toString();
  395. String secret = jsonClient.get("secret").toString();
  396. Client client = new Client(username, secret);
  397. clients.add(client);
  398. }
  399. }
  400. /**
  401. * Main process function. This method checks the type of message by using
  402. * the IncomingMessage class. Depending on type, it delegates work and
  403. * decides whether to respond, close or maintain the connection
  404. *
  405. * @param con
  406. * @param msg
  407. * @return
  408. * @throws IOException
  409. * @throws ParseException
  410. */
  411. public synchronized boolean process(Connection con, String msg) {
  412. //TODO we need to work out if we want to throw and exception or
  413. //TODO or try catch each writeMsg()
  414. if (!term) {//in connection
  415. IncomingMessage incoming = new IncomingMessage(msg, con);
  416. if (incoming.type == IncomingMessage.Type.AUTHENTICATE) {
  417. if (con.isAuthenticated()) {
  418. con.writeMsg(new OutgoingMessage("INVALID_MESSAGE",
  419. "server already Authenticated").getJsonReply());
  420. removeConnections(con);
  421. return true;
  422. }
  423. String secret = incoming.jsonMessage.get("secret").toString();
  424. if (authenticateServer(true, con, incoming)) {
  425. log.info("AUTH_REQUEST FROM server with secret: " + secret);
  426. return false;
  427. }
  428. con.writeMsg(new OutgoingMessage("AUTHENTICATION_FAIL", secret).getJsonReply());
  429. log.info(con + "failed to authenticate with : " + secret + " actual : " + Settings.getSecret());
  430. removeConnections(con);
  431. return true;
  432. } else if (incoming.type == IncomingMessage.Type.AUTHENTICATE_INFO) {
  433. log.debug("INCOMING AUTHENTICATION INFO");
  434. processAuthenticationInfo(incoming);
  435. serverConnections.get(con).setID(incoming.jsonMessage.get("id").toString());
  436. return false;
  437. } else if (incoming.type == IncomingMessage.Type.CONNECTION_REQUEST) {
  438. if (con.isAuthenticated()) {
  439. con.writeMsg(new OutgoingMessage("INVALID_MESSAGE",
  440. "server already Authenticated").getJsonReply());
  441. removeConnections(con);
  442. return true;
  443. }
  444. String secret = incoming.jsonMessage.get("secret").toString();
  445. if (authenticateServer(false, con, incoming)) {
  446. log.info("AUTH_REQUEST FROM server with secret: " + secret);
  447. return false;
  448. }
  449. return true;
  450. } else if (incoming.type == IncomingMessage.Type.LOGIN) {
  451. if (con.isAuthenticated()) {
  452. OutgoingMessage message = new OutgoingMessage("INVALID_MESSAGE", "Already logged in. ");
  453. con.writeMsg(message.getJsonReply());
  454. removeConnections(con);
  455. return true;
  456. }
  457. OutgoingMessage validLoginmsg = processLogin(incoming, con);
  458. if (validLoginmsg.reply.get("command").equals("LOGIN_SUCCESS")) {
  459. for (Connection servCon : serverConnections.keySet()) {
  460. ServerInfo serv = serverConnections.get(servCon);
  461. //If our server has 2 more connected clients
  462. //send it a redirect
  463. if (clientConnections.size() >= 3 + serv.getLoad() && serv.isValid()) {
  464. con.writeMsg(validLoginmsg.getJsonReply());
  465. con.writeMsg(new OutgoingMessage("REDIRECT",
  466. serv.getHostname(), serv.getPort().toString()).getJsonReply());
  467. removeConnections(con);
  468. return true;
  469. }
  470. }
  471. con.setAuthenticate(true);
  472. con.writeMsg(validLoginmsg.getJsonReply());
  473. return false;
  474. } else {
  475. //send message back to client accordingly
  476. con.writeMsg(validLoginmsg.getJsonReply());
  477. removeConnections(con);
  478. return true;
  479. }
  480. } else if (incoming.type == IncomingMessage.Type.LOGOUT) {
  481. removeConnections(con);
  482. return true;
  483. } else if (incoming.type == IncomingMessage.Type.DISCONNECT_CLIENT) {
  484. log.debug("WITHING DISCONNECT_CLIENT");
  485. for (ServerInfo server : serverConnections.values()) {
  486. log.debug("iterating through server connections");
  487. log.debug(server.getID().equals(incoming.jsonMessage.get("serverId").toString()));
  488. if (server.getID().equals(incoming.jsonMessage.get("serverId").toString())) {
  489. server.removeFromStorage(incoming.jsonMessage.get("clientUsername").toString());
  490. }
  491. }
  492. return false;
  493. } else if (incoming.type == IncomingMessage.Type.INVALID) {
  494. OutgoingMessage message = new OutgoingMessage("INVALID_MESSAGE", incoming.invalidString);
  495. con.writeMsg(message.getJsonReply());
  496. removeConnections(con);
  497. return true;
  498. } else if (incoming.type == IncomingMessage.Type.AUTHENTICATION_FAIL) {
  499. log.debug("Tried to connect.. but failed");
  500. removeConnections(con);
  501. return true;
  502. } else if (incoming.type == IncomingMessage.Type.REGISTER) {
  503. String username = incoming.jsonMessage.get("username").toString();
  504. if (registerClient(incoming, con)) {
  505. log.debug("INCOMING REGISTER : " + incoming.jsonMessage.toJSONString());
  506. //If this is the first server
  507. if (registerLocks.get(username).status()) {
  508. OutgoingMessage message = new OutgoingMessage("REGISTER_SUCCESS", username + " registered successfully");
  509. con.writeMsg(message.getJsonReply());
  510. }
  511. return false;
  512. } else {
  513. con.writeMsg(new OutgoingMessage("REGISTER_FAILED", "").getJsonReply());
  514. removeConnections(con);
  515. return true;
  516. }
  517. } else if (incoming.type == IncomingMessage.Type.SERVER_ANNOUNCE) {
  518. log.info("Received server announce : ");
  519. log.info(con.getSocket().getRemoteSocketAddress());
  520. if (processAnnounce(incoming, con)) {
  521. return false;
  522. } else {
  523. con.writeMsg(new OutgoingMessage("INVALID_MESSAGE",
  524. "Invalid server announce").getJsonReply());
  525. removeConnections(con);
  526. return true;
  527. }
  528. } else if (incoming.type == IncomingMessage.Type.ACTIVITY_MESSAGE) {
  529. JSONObject activity = (JSONObject) incoming.jsonMessage.get("activity");
  530. if (con.isAuthenticated()) {
  531. Client cli = clientConnections.get(con);
  532. if (((cli.getUsername().equals(incoming.jsonMessage.get("username")))
  533. && (cli.getSecret().equals(incoming.jsonMessage.get("secret"))))
  534. || incoming.jsonMessage.get("username").equals("anonymous")) {
  535. String username = clientConnections.get(con).getUsername();
  536. activity.put("authenticated_user", username);
  537. MessageInfo message = new MessageInfo(
  538. this.generateSnapShot(), Control.ID, cli.getUsername(), activity);
  539. log.debug("PRINTING MESSAGE INFORMATION");
  540. log.debug("SNAPSHOT : " + message.getSnapshot().toJSONString());
  541. cli.addToMessageDB(message);
  542. OutgoingMessage abMessage =
  543. new OutgoingMessage("ACTIVITY_BROADCAST",
  544. cli.getMessageNumber(), message);
  545. /*OutgoingMessage activityBroadcastMessage =
  546. new OutgoingMessage("ACTIVITY_BROADCAST",
  547. activity);
  548. */
  549. activityMessageBroadcast(abMessage, true, true);
  550. //broadcastActivity(abMessage, true, true);
  551. return false;
  552. } else {
  553. OutgoingMessage outgoingMessage = new OutgoingMessage(
  554. "AUTHENTICATION_FAIL", "Incorrect secret or username provided"
  555. );
  556. removeConnections(con);
  557. con.writeMsg(outgoingMessage.getJsonReply());
  558. return true;
  559. }
  560. } else {
  561. OutgoingMessage outgoingMessage = new OutgoingMessage(
  562. "AUTHENTICATION_FAIL", "Not Logged In"
  563. );
  564. removeConnections(con);
  565. con.writeMsg(outgoingMessage.getJsonReply());
  566. return true;
  567. }
  568. } else if (incoming.type == IncomingMessage.Type.ACTIVITY_BROADCAST) {
  569. if (con.isAuthenticated()) {
  570. log.debug("ACTIVITY MESSAGE COMING IN");
  571. log.debug(incoming.jsonMessage.toString());
  572. MessageInfo message = new MessageInfo(
  573. (JSONArray) incoming.jsonMessage.get("snapshot"),
  574. (String) incoming.jsonMessage.get("serverId"),
  575. (String) incoming.jsonMessage.get("clientUsername"),
  576. (JSONObject) incoming.jsonMessage.get("activity")
  577. );
  578. OutgoingMessage outgoing = new OutgoingMessage("ACTIVITY_BROADCAST",
  579. Integer.parseInt(incoming.jsonMessage.get("messageNumber").toString()), message);
  580. //TODO unpack the broadcast and send without the message info
  581. activityMessageBroadcast(outgoing, true, false);
  582. return false;
  583. } else {
  584. new OutgoingMessage("INVALID_MESSAGE", "Unauthenticated Server");
  585. removeConnections(con);
  586. return true;
  587. }
  588. } else if (incoming.type == IncomingMessage.Type.LOCK_REQUEST) {
  589. String username = incoming.jsonMessage.get("username").toString();
  590. String secret = incoming.jsonMessage.get("secret").toString();
  591. if (checkRegisteredUsers(incoming)) {
  592. clients.add(new Client(username, secret));
  593. broadcastActivity(
  594. new OutgoingMessage("LOCK_ALLOWED", username, secret),
  595. false, true);
  596. } else {
  597. broadcastActivity(
  598. new OutgoingMessage("LOCK_DENIED", username, secret),
  599. false, true);
  600. }
  601. } else if (incoming.type == IncomingMessage.Type.LOCK_DENIED) {
  602. String username = incoming.jsonMessage.get("username").toString();
  603. if (registerLocks.containsKey(username)) {
  604. registerLocks.remove(username);
  605. OutgoingMessage message = new OutgoingMessage("REGISTER_FAILED", username + " is already registered");
  606. con.writeMsg(message.getJsonReply());
  607. removeConnections(con);
  608. return true;
  609. }
  610. Iterator<Client> clientsIt = clients.iterator();
  611. while (clientsIt.hasNext()) {
  612. if (clientsIt.next().getUsername().equals(username)) {
  613. clientsIt.remove();
  614. }
  615. }
  616. return false;
  617. } else if (incoming.type == IncomingMessage.Type.LOCK_ALLOWED) {
  618. String username = incoming.jsonMessage.get("username").toString();
  619. if (registerLocks.containsKey(username)) {
  620. RegisterLock lock = registerLocks.get(username);
  621. lock.addAllowed();
  622. if (lock.status()) {
  623. clients.add(lock.getClient());
  624. OutgoingMessage message = new OutgoingMessage("REGISTER_SUCCESS", username + " registered successfully");
  625. lock.getCon().writeMsg(message.getJsonReply());
  626. }
  627. }
  628. } else if (incoming.type == IncomingMessage.Type.INCORRECT_MESSAGE_ORDER) {
  629. log.debug("RECEIVING INCORRECT MESSAGE PROTOCOL");
  630. for (Connection cliCon : clientConnections.keySet()) {
  631. Client cli = clientConnections.get(cliCon);
  632. if (cli.getUsername().equals(incoming.jsonMessage.get("clientUsername").toString())) {
  633. MessageInfo retMsg = new MessageInfo();
  634. Integer prevNumber = Integer.parseInt(incoming.jsonMessage.get("previousMsgNumber").toString());
  635. retMsg = cli.getFromMessageDB(prevNumber);
  636. while (retMsg != null) {
  637. int count = 0;
  638. for (Connection serCon : serverConnections.keySet()) {
  639. ServerInfo ser = serverConnections.get(serCon);
  640. if (ser.getID().equals(incoming.jsonMessage.get("serverId").toString())) {
  641. log.debug("SENDING CORRECT MESSAGE ORDER");
  642. OutgoingMessage repeatMsg = new OutgoingMessage(
  643. "CORRECT_MESSAGE_ORDER", prevNumber + 1, retMsg
  644. );
  645. count++;
  646. serCon.writeMsg(repeatMsg.getJsonReply());
  647. retMsg = cli.getFromMessageDB(++prevNumber);
  648. try {
  649. wait(1000);
  650. } catch (Exception e) {
  651. }
  652. }
  653. }
  654. if (count == 0)
  655. retMsg = null;
  656. }
  657. }
  658. }
  659. return false;
  660. } else if (incoming.type == IncomingMessage.Type.CORRECT_MESSAGE_ORDER) {
  661. /*JSONArray snapshot = new JSONArray();
  662. JSONObject servCli = new JSONObject();
  663. JSONArray cli = new JSONArray();
  664. for (Connection c : clientConnections.keySet()){
  665. Client cliCon = clientConnections.get(c);
  666. cli.add(cliCon.getUsername());
  667. }
  668. servCli.put("Clients",cli);
  669. servCli.put("SID",Control.ID);
  670. snapshot.add(servCli);
  671. */
  672. MessageInfo message = new MessageInfo(
  673. (JSONArray) incoming.jsonMessage.get("snapshot"),
  674. (String) incoming.jsonMessage.get("serverId"),
  675. (String) incoming.jsonMessage.get("clientUsername"),
  676. (JSONObject) incoming.jsonMessage.get("activity")
  677. );
  678. OutgoingMessage outgoing = new OutgoingMessage("ACTIVITY_BROADCAST",
  679. Integer.parseInt(incoming.jsonMessage.get("messageNumber").toString()), message);
  680. log.debug("THIS IS THE 4th MESSAGE " + outgoing.getJsonReply());
  681. activityMessageBroadcast(outgoing, true, false);
  682. return false;
  683. }
  684. return false;
  685. }
  686. return false;
  687. }
  688. /*
  689. * The connection has been closed by the other party.
  690. */
  691. public synchronized void connectionClosed(Connection con) {
  692. if (!term) connections.remove(con);
  693. }
  694. /**
  695. * Creates a new connection type for incoming socket connections
  696. *
  697. * @param s
  698. * @return
  699. * @throws IOException
  700. */
  701. public synchronized Connection incomingConnection(Socket s) throws IOException {
  702. log.debug("incoming connection: " + Settings.socketAddress(s));
  703. Connection c = new Connection(s);
  704. connections.add(c);
  705. return c;
  706. }
  707. /**
  708. * A new outgoing connection has been established, and a reference is returned to it
  709. *
  710. * @param s
  711. * @return
  712. * @throws IOException
  713. */
  714. public synchronized Connection outgoingConnection(Socket s) throws IOException {
  715. log.debug("outgoing connection: " + Settings.socketAddress(s));
  716. Connection c = new Connection(s);
  717. connections.add(c);
  718. return c;
  719. }
  720. /**
  721. * Overridden run method to allow the broadcasting to happen in a separate
  722. * thread to the incoming message processing.
  723. */
  724. @Override
  725. public void run() {
  726. log.info("using activity interval of " + Settings.getActivityInterval() + " milliseconds");
  727. while (!term) {
  728. // do something with 5 second intervals in between
  729. try {
  730. Thread.sleep(Settings.getActivityInterval());
  731. } catch (InterruptedException e) {
  732. log.info("Received an interrupt, system is shutting down");
  733. break;
  734. }
  735. if (!term) {
  736. logInfo();
  737. term = broadcastServerAnnounce();
  738. }
  739. }
  740. log.info("closing " + connections.size() + " connections");
  741. // clean up
  742. for (Connection connection : connections) {
  743. connection.closeCon();
  744. }
  745. listener.setTerm(true);
  746. }
  747. public void logInfo() {
  748. log.info("Current server list: ");
  749. for (Connection servConnection : serverConnections.keySet()) {
  750. ServerInfo server = serverConnections.get(servConnection);
  751. log.info("Server : " + server.getID() +
  752. " load: " + server.getLoad() +
  753. " Client Messages " + server.getMessageDB().toString());
  754. server.printConnectedClients();
  755. }
  756. log.info("Connected client list: ");
  757. for (Connection clientConnection : clientConnections.keySet()) {
  758. log.info(clientConnections.get(clientConnection).getUsername());
  759. }
  760. log.debug("Known client list: ");
  761. for (Client client : clients) {
  762. log.info(client.getUsername());
  763. }
  764. }
  765. /**
  766. * Activity function for announcing servers
  767. *
  768. * @return
  769. */
  770. private boolean broadcastServerAnnounce() {
  771. ArrayList<Client> connectedClients = new ArrayList<>(clientConnections.values());
  772. for (Connection connection : serverConnections.keySet()) {
  773. ServerInfo server = serverConnections.get(connection);
  774. HashMap<String, Integer> db = new HashMap<String, Integer>();
  775. db = server.getMessageDB();
  776. JSONArray serverClients = new JSONArray();
  777. for (String username : db.keySet()) {
  778. JSONObject cli = new JSONObject();
  779. cli.put("clientUsername", username);
  780. cli.put("messageNumber", db.get(username));
  781. serverClients.add(cli);
  782. }
  783. String msg = new OutgoingMessage("SERVER_ANNOUNCE",
  784. this.ID, clientConnections.size() + "",
  785. Settings.getLocalHostname(),
  786. Settings.getLocalPort() + "", connectedClients, serverClients).getJsonReply();
  787. if (connection.isOpen())
  788. connection.writeMsg(msg);
  789. }
  790. return false;
  791. }
  792. public HashMap<String, ArrayList<String>> generateSnapShot() {
  793. ArrayList<String> cliUsernames = new ArrayList<String>();
  794. for (Connection con : clientConnections.keySet()) {
  795. Client cli = clientConnections.get(con);
  796. cliUsernames.add(cli.getUsername());
  797. }
  798. HashMap<String, ArrayList<String>> snapShot = new HashMap<String, ArrayList<String>>();
  799. for (Connection con : serverConnections.keySet()) {
  800. ServerInfo server = serverConnections.get(con);
  801. snapShot.put(server.getID(), server.getConnectedClients());
  802. }
  803. snapShot.put(Control.ID, cliUsernames);
  804. return snapShot;
  805. }
  806. public final void setTerm(boolean t) {
  807. term = t;
  808. }
  809. public final ArrayList<Connection> getConnections() {
  810. return connections;
  811. }
  812. public void removeConnections(Connection con) {
  813. if (clientConnections.containsKey(con)) {
  814. Client cli = clientConnections.get(con);
  815. OutgoingMessage outgoing = new OutgoingMessage("DISCONNECT_CLIENT", Control.ID, cli.getUsername());
  816. broadcastActivity(outgoing, false, true);
  817. clientConnections.remove(con);
  818. }
  819. if (serverConnections.containsKey(con))
  820. serverConnections.remove(con);
  821. }
  822. public ServerInfo getServer(Connection con) {
  823. return this.serverConnections.get(con);
  824. }
  825. /**
  826. * Function which reconnects to a socket
  827. * It removes the old connection and then
  828. * creates a new connection object
  829. *
  830. * @param oldCon
  831. * @param badServer
  832. * @param socket
  833. */
  834. public void resetConnection(Connection oldCon, ServerInfo badServer, Socket socket) {
  835. try {
  836. Connection newCon = this.outgoingConnection(socket);
  837. serverConnections.remove(oldCon);
  838. serverConnections.put(newCon, badServer);
  839. log.debug("SENDING RECONNECT REQUEST");
  840. OutgoingMessage authMsg = new OutgoingMessage("CONNECTION_REQUEST",
  841. Settings.getSecret(), this.ID, Settings.getLocalHostname(), Settings.getLocalPort());
  842. newCon.writeMsg(authMsg.getJsonReply());
  843. newCon.setAuthenticate(true);
  844. oldCon.closeCon();
  845. } catch (IOException e) {
  846. e.printStackTrace();
  847. }
  848. }
  849. }