PageRenderTime 43ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/java/com/fw/lw/websocket/WebRTCRoomBean.java

https://bitbucket.org/jonaspfab/lw_web_socket
Java | 506 lines | 241 code | 45 blank | 220 comment | 95 complexity | 612d1284526180de32f5eba02ec84614 MD5 | raw file
  1. package com.fw.lw.websocket;
  2. import java.util.HashMap;
  3. import java.util.LinkedList;
  4. import java.util.Map;
  5. import java.util.Queue;
  6. import javax.json.Json;
  7. import javax.json.JsonObject;
  8. import javax.websocket.Session;
  9. import org.apache.log4j.Logger;
  10. /**
  11. *
  12. * @author fasiha.anjum
  13. */
  14. public class WebRTCRoomBean {
  15. private static final Logger LOG = Logger.getLogger(WebRTCRoomBean.class);
  16. /**
  17. * The INSTANCE bean
  18. */
  19. private static final WebRTCRoomBean INSTANCE = new WebRTCRoomBean();
  20. /**
  21. * Private class that represents a room member. A member is the username,
  22. * the session (web socket) and a queue of un delivered messages for the user
  23. * (messages to send as soon as he joins).
  24. */
  25. private class RoomMember {
  26. /**
  27. * username
  28. */
  29. private String username;
  30. /**
  31. * session
  32. */
  33. private Session session;
  34. /**
  35. * undelivered messages for this user
  36. */
  37. private Queue<JsonObject> undeliveredMessages;
  38. /**
  39. * Empty constructor
  40. */
  41. public RoomMember() {
  42. undeliveredMessages = new LinkedList<>();
  43. }
  44. /**
  45. * Getter for username
  46. *
  47. * @return the username
  48. */
  49. public String getUsername() {
  50. return username;
  51. }
  52. /**
  53. * Setter for username
  54. *
  55. * @param username the new username
  56. */
  57. public void setUsername(String username) {
  58. this.username = username;
  59. }
  60. /**
  61. * Getter for the session
  62. *
  63. * @return the session
  64. */
  65. public Session getSession() {
  66. return session;
  67. }
  68. /**
  69. * Setter for the session
  70. *
  71. * @param session the new session
  72. */
  73. public void setSession(Session session) {
  74. this.session = session;
  75. }
  76. /**
  77. * Getter for the queue of undelivered messages
  78. *
  79. * @return The queue
  80. */
  81. public Queue<JsonObject> getUndeliveredMessages() {
  82. return undeliveredMessages;
  83. }
  84. /**
  85. * String representation
  86. *
  87. * @return
  88. */
  89. @Override
  90. public String toString() {
  91. return new StringBuilder()
  92. .append(getUsername())
  93. .append("(")
  94. .append((getSession() == null) ? "null" : getSession().getId())
  95. .append(")")
  96. .toString();
  97. }
  98. }
  99. /**
  100. * Private class that represents a room. A room is composed by two members
  101. * and the room key. The members are always created (they are never null,
  102. * but the username or session can be null).
  103. */
  104. private class Room {
  105. /**
  106. * The room key
  107. */
  108. private String key;
  109. /**
  110. * First member
  111. */
  112. private RoomMember member1;
  113. /**
  114. * Second member
  115. */
  116. private RoomMember member2;
  117. /**
  118. * Constructor using the key.
  119. *
  120. * @param key The room key
  121. */
  122. public Room(String key) {
  123. this.key = key;
  124. member1 = new RoomMember();
  125. member2 = new RoomMember();
  126. }
  127. /**
  128. * Getter for the key
  129. *
  130. * @return the key
  131. */
  132. public String getKey() {
  133. return key;
  134. }
  135. /**
  136. * Getter for user member 1
  137. *
  138. * @return the member1
  139. */
  140. public RoomMember getMember1() {
  141. return member1;
  142. }
  143. /**
  144. * Getter for member 2
  145. *
  146. * @return the member 2
  147. */
  148. public RoomMember getMember2() {
  149. return member2;
  150. }
  151. /**
  152. * A new member is joined to the room. The username and session is
  153. * filled and the un delivered messages are sent (if there were any).
  154. *
  155. * @param username The username which is joining
  156. * @param session The session which is joining
  157. */
  158. public void addMember(String userName, Session session) {
  159. if (userName != null && session != null) {
  160. if (!userName.equals(member1.getUsername()) && !userName.equals(member2.getUsername())) {
  161. if (member1.getUsername() == null) {
  162. this.member1.setUsername(userName);
  163. this.member1.setSession(session);
  164. // process undelivered messages
  165. while (!member1.getUndeliveredMessages().isEmpty()) {
  166. LOG.info("Sending undelivered messages for username=" + member1.getUsername());
  167. WebRTCEndPoint.send(member1.getSession(), member1.getUndeliveredMessages().poll());
  168. }
  169. } else if (member2.getUsername() == null) {
  170. this.member2.setUsername(userName);
  171. this.member2.setSession(session);
  172. // process undelivered messages
  173. while (!member2.getUndeliveredMessages().isEmpty()) {
  174. LOG.info("Sending undelivered messages for username=" + member2.getUsername());
  175. WebRTCEndPoint.send(member2.getSession(), member2.getUndeliveredMessages().poll());
  176. }
  177. }
  178. }
  179. }
  180. }
  181. /**
  182. * Return the member for this session or null
  183. *
  184. * @param session The session to search the member
  185. * @return The member with this session or null
  186. */
  187. public RoomMember getMember(Session session) {
  188. if (session != null) {
  189. if (member1.getSession() != null
  190. && session.getId().equals(member1.getSession().getId())) {
  191. return member1;
  192. } else if (member2.getSession() != null
  193. && session.getId().equals(member2.getSession().getId())) {
  194. return member2;
  195. }
  196. }
  197. return null;
  198. }
  199. /**
  200. * return the member for this username or null.
  201. *
  202. * @param username The username to search the member
  203. * @return The member for this username or null
  204. */
  205. public RoomMember getMember(String userName) {
  206. if (userName != null) {
  207. if (userName.equals(member1.getUsername())) {
  208. return member1;
  209. } else if (userName.equals(member2.getUsername())) {
  210. return member2;
  211. }
  212. }
  213. return null;
  214. }
  215. /**
  216. * return the other member, the one which is not the parameter.
  217. *
  218. * @param sesstion The session of the user
  219. * @return The member or null (the member can be null / empty)
  220. */
  221. public RoomMember getOtherMember(Session session) {
  222. if (session != null) {
  223. if (member1.getSession() != null
  224. && session.getId().equals(member1.getSession().getId())) {
  225. return member2;
  226. } else if (member2.getSession() != null
  227. && session.getId().equals(member2.getSession().getId())) {
  228. return member1;
  229. }
  230. }
  231. return null;
  232. }
  233. /**
  234. * return the other member, the one which is not the parameter.
  235. *
  236. * @param username The username to search the counterpart
  237. * @return The member or null (the member can be null / empty)
  238. */
  239. public RoomMember getOtherMember(String userName) {
  240. if (userName != null) {
  241. if (userName.equals(member1.getUsername())) {
  242. return member2;
  243. } else if (userName.equals(member2.getUsername())) {
  244. return member1;
  245. }
  246. }
  247. return null;
  248. }
  249. /**
  250. * Remove the member from the room. The session is closed if it exists.
  251. *
  252. * @param username The username to remove from the room
  253. */
  254. public void removeMember(String userName) {
  255. if (userName != null) {
  256. if (userName.equals(member1.getUsername())) {
  257. member1.setUsername(null);
  258. WebRTCEndPoint.closeSession(member1.getSession());
  259. member1.setSession(null);
  260. member1.getUndeliveredMessages().clear();
  261. } else if (userName.equals(member2.getUsername())) {
  262. member2.setUsername(null);
  263. WebRTCEndPoint.closeSession(member2.getSession());
  264. member2.setSession(null);
  265. member2.getUndeliveredMessages().clear();
  266. }
  267. }
  268. }
  269. /**
  270. * Check if the room is full (both members are in the room).
  271. *
  272. * @return true if the two members are filled
  273. */
  274. public boolean isFull() {
  275. return member1.getUsername() != null && member2.getUsername() != null;
  276. }
  277. /**
  278. * Check if the room is empty (both members are null)
  279. *
  280. * @return true if the two members are null
  281. */
  282. public boolean isEmpty() {
  283. return member1.getUsername() == null && member2.getUsername() == null;
  284. }
  285. /**
  286. * Check if in this room there is one user which is waiting and it is
  287. * not the parameter username.
  288. *
  289. * @param username the username which is asking
  290. * @return true if there is one username waiting in this room
  291. */
  292. public boolean isTheOtherMemberWaiting(String userName) {
  293. if (userName != null) {
  294. return ((member1.getUsername() != null && !userName.equals(member1.getUsername()))
  295. || (member2.getUsername() != null && !userName.equals(member2.getUsername())))
  296. && !isFull();
  297. }
  298. return false;
  299. }
  300. /**
  301. * Debug representation.
  302. *
  303. * @return
  304. */
  305. @Override
  306. public String toString() {
  307. return new StringBuilder()
  308. .append(key)
  309. .append(" -> ")
  310. .append(getMember1().toString())
  311. .append("-")
  312. .append(getMember2().toString())
  313. .toString();
  314. }
  315. }
  316. /**
  317. * rooms indexed by room key
  318. */
  319. private Map<String, Room> roomsByKey;
  320. /**
  321. * rooms indexed by session id
  322. */
  323. private Map<String, Room> roomsBySession;
  324. /**
  325. * Private constructor.
  326. */
  327. private WebRTCRoomBean() {
  328. this.roomsByKey = new HashMap<>();
  329. this.roomsBySession = new HashMap<>();
  330. }
  331. /**
  332. * The INSTANCE method. TODO: A EJB is not injected in the WebSocket
  333. * endpoint.
  334. *
  335. * @return The room bean INSTANCE
  336. */
  337. static public WebRTCRoomBean getRoomBean() {
  338. return INSTANCE;
  339. }
  340. /**
  341. * It checks if in the specified room there is one user waiting.
  342. *
  343. * @param roomKey The room to search
  344. * @param userName The user asking for the other user
  345. * @return true if in that room there is another user waiting
  346. */
  347. synchronized public boolean isTheOtherMemberWaiting(String roomKey, String userName) {
  348. Room room = roomsByKey.get(roomKey);
  349. return room != null && room.isTheOtherMemberWaiting(userName);
  350. }
  351. /**
  352. * It returns if a specific room is full.
  353. *
  354. * @param roomKey The room to check
  355. * @return true if it is full, false otherwise
  356. */
  357. synchronized public boolean isTheRoomFull(String roomKey) {
  358. Room room = roomsByKey.get(roomKey);
  359. return room != null && room.isFull();
  360. }
  361. /**
  362. * A user is connecting to a room. The room is created if it didn't exist
  363. * and the user is added to that room. If the room already existed the user
  364. * is added to that room.
  365. *
  366. * @param roomKey The room key
  367. * @param userName The username which is joining
  368. * @param session The session (websocket) of the user
  369. */
  370. synchronized public void connect(String roomKey, String userName, Session session) {
  371. Room room = roomsByKey.get(roomKey);
  372. if (room == null) {
  373. // new room => create and assign user 1
  374. room = new Room(roomKey);
  375. room.addMember(userName, session);
  376. roomsByKey.put(roomKey, room);
  377. roomsBySession.put(session.getId(), room);
  378. } else if (!room.isFull()) {
  379. room.addMember(userName, session);
  380. roomsBySession.put(session.getId(), room);
  381. }
  382. }
  383. /**
  384. * The user disconnects from the room using the session id. The room is
  385. * located and the user removed from that room.
  386. *
  387. * @param session The session of the user
  388. */
  389. synchronized public void disconnect(Session session) {
  390. Room room = roomsBySession.get(session.getId());
  391. if (room != null) {
  392. RoomMember member = room.getMember(session);
  393. if (member != null) {
  394. disconnect(room, member);
  395. }
  396. }
  397. }
  398. /**
  399. * The user disconnects from the room using the username. The room is
  400. * located and the user removed from that room.
  401. *
  402. * @param roomKey The roomKey of the user
  403. * @param userName The username
  404. */
  405. synchronized public void disconnect(String roomKey, String userName) {
  406. Room room = roomsByKey.get(roomKey);
  407. if (room != null) {
  408. RoomMember member = room.getMember(userName);
  409. if (member != null) {
  410. disconnect(room, member);
  411. }
  412. }
  413. }
  414. /**
  415. * The user is removed from the session. If there is a partner in the room a
  416. * "bye" message is sent to him in order to close the conversation (it is
  417. * done in the hangup).
  418. *
  419. * @param room The room with the user which is disconnecting
  420. * @param member The user member
  421. */
  422. private void disconnect(Room room, RoomMember member) {
  423. if (room != null && member != null) {
  424. RoomMember otherMember = room.getOtherMember(member.getUsername());
  425. // remove the index by the session id
  426. roomsBySession.remove(member.getSession().getId());
  427. // remove the member
  428. room.removeMember(member.getUsername());
  429. if (otherMember == null || otherMember.getUsername() == null) {
  430. // the room is empty => delete completely
  431. roomsByKey.remove(room.getKey());
  432. } else {
  433. // send "bye" to the other member
  434. JsonObject bye = Json.createObjectBuilder().add("type", "bye").build();
  435. WebRTCEndPoint.send(otherMember.getSession(), bye);
  436. }
  437. }
  438. }
  439. /**
  440. * Method that sends a message to the partner member of a room. The room is
  441. * located and the message is sent to the the other partner.
  442. *
  443. * @param session The session of the user
  444. * @param data The data to send
  445. */
  446. synchronized public void sendToTheOther(Session session, JsonObject data) {
  447. Room room = roomsBySession.get(session.getId());
  448. if (room != null) {
  449. RoomMember otherMember = room.getOtherMember(session);
  450. if (otherMember != null && otherMember.getSession() != null) {
  451. // the other user is connected
  452. LOG.info("Sending message to username=" + otherMember.getUsername());
  453. WebRTCEndPoint.send(otherMember.getSession(), data);
  454. } else if (otherMember != null) {
  455. // the other user is not connected => add the offer
  456. LOG.info("Saving message to username=" + otherMember.getUsername());
  457. otherMember.getUndeliveredMessages().add(data);
  458. }
  459. }
  460. }
  461. }