From 84e5c64d9d7831ec65edd965a54902285356e3bf Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Fri, 16 Jun 2017 13:40:02 +0200 Subject: [PATCH] README steps presentation improved --- openvidu-js-java/README.md | 615 ++++++++++++++++++------------------ openvidu-js-node/README.md | 585 +++++++++++++++++----------------- openvidu-mvc-java/README.md | 592 +++++++++++++++++----------------- openvidu-mvc-node/README.md | 568 +++++++++++++++++---------------- 4 files changed, 1195 insertions(+), 1165 deletions(-) diff --git a/openvidu-js-java/README.md b/openvidu-js-java/README.md index 2f4e41a0..2675242f 100644 --- a/openvidu-js-java/README.md +++ b/openvidu-js-java/README.md @@ -70,345 +70,352 @@ This is a very basic web application with a pretty simple vanilla JS/HTML/CSS fr Let's describe the code following this scenario: a user logs in to the app and connects to the video-call "TUTORIAL", where he publishes his webcam. A second user will connect to the same video-call just after that and publish its own webcam. Both of them will leave the call after a while. -1. **User logs in** +--- - We have implemented a method for making HTTP requests to the backend, as we will need to make at least three of them: one for logging in, one for getting the sessionId and a valid token from openvidu-server and a one for letting know our backend when any user leaves the video-call. The header of the method looks like this: - - ```javascript - function httpRequest(method, url, body, errorMsg, callback) - ``` - - Where `method` is whether "POST" or "GET", `url` the path of the REST operation, `body` the data to be passed, `errorMsg` the output error message if something goes wrong and `callback` the function to execute in case of success. As mentioned above, we need to call this method three times for each user that LOGS IN 🡒 CONNECTS TO A VIDEO-CALL 🡒 LEAVES THE VIDEO-CALL. +### 1) User logs in - `index.html` will first show a form to log in: +We have implemented a method for making HTTP requests to the backend, as we will need to make at least three of them: one for logging in, one for getting the sessionId and a valid token from openvidu-server and a one for letting know our backend when any user leaves the video-call. The header of the method looks like this: -

- -

+```javascript +function httpRequest(method, url, body, errorMsg, callback) +``` - `app.js` sends an HTTP request to "/api-login/login" passing the username and the password retrieved from the HTML form whenever "Log in" button is clicked: - - ```javascript - function logIn() { - var user = $("#user").val(); // Username - var pass = $("#pass").val(); // Password - var jsonBody = JSON.stringify({ // Body of POST request - 'user': user, - 'pass': pass - }); - - httpRequest('POST', '/api-login/login', jsonBody, 'Login WRONG', - function successCallback(response){ // Send POST request - console.warn(userName + ' login'); - // HTML shows logged-in page ... - }); - } - ``` +Where `method` is whether "POST" or "GET", `url` the path of the REST operation, `body` the data to be passed, `errorMsg` the output error message if something goes wrong and `callback` the function to execute in case of success. As mentioned above, we need to call this method three times for each user that LOGS IN 🡒 CONNECTS TO A VIDEO-CALL 🡒 LEAVES THE VIDEO-CALL. - `LoginController.java` checks the params are correct and if so sets an _HttpSession_ for the newly logged user (adding a "loggedUser" attribute with its username in the HttpSession object): +`index.html` will first show a form to log in: - ```java - @RequestMapping(value = "/api-login/login", method = RequestMethod.POST) - public ResponseEntity login(@RequestBody String userPass, HttpSession httpSession) - throws ParseException { +

+ +

- // Retrieve params from POST body - JSONObject userPassJson = (JSONObject) new JSONParser().parse(userPass); - String user = (String) userPassJson.get("user"); - String pass = (String) userPassJson.get("pass"); +`app.js` sends an HTTP request to "/api-login/login" passing the username and the password retrieved from the HTML form whenever "Log in" button is clicked: - if (login(user, pass)){ // Correct user-pass - // Validate session and return OK - // Value stored in HttpSession allows us to identify the user in future requests - httpSession.setAttribute("loggedUser", user); - return new ResponseEntity<>(HttpStatus.OK); - } else { // Wrong user-pass - // Invalidate session and return error - httpSession.invalidate(); - return new ResponseEntity<>("User/Pass incorrect", HttpStatus.UNAUTHORIZED); - } - } - ``` - -2. **User connects to "TUTORIAL" video-call** - - HTML will display now the user has logged in a different form, asking for the video-call to connect and the nickname the user wants to have in it. So our 'publisher1' user would write TUTORIAL in "Session" field and press "Join!" button: - -

- -

- - `app.js` will execute `joinSession()` method, which starts like this: - - ```javascript - function joinSession() { - getSessionIdAndToken(function () { ... - ``` - So the first thing to do here is to retrieve a _sessionId_ and a _token_ from our backend. Only when we have them available in the browser we will continue with the _join_ operation. Let's see what `getSessionIdAndToken()` looks like: - - ```javascript - function getSessionIdAndToken(callback) { - sessionName = $("#sessionName").val(); // Video-call to connect ("TUTORIAL") - var jsonBody = JSON.stringify({ // Body of POST request - 'session': sessionName - }); - - // Send POST request - httpRequest('POST', '/api-sessions/get-sessionid-token', jsonBody, - 'Request of SESSIONID and TOKEN gone WRONG:', function successCallback(response){ - sessionId = response[0]; // Get sessionId from response - token = response[1]; // Get token from response - callback(); // Continue the join operation - }); - } - ``` - Here is the second time we must call our `httpRequest()` method, sending the session we want to connect ("TUTORIAL") and waiting to get a _sessionId_ and a _token_ as response. The interesting part here is in `SessionController.java`. First of all there are some important attributes in this class we must mention: - - ```java - // OpenVidu object to ask openvidu-server for sessionId and token - private OpenVidu openVidu; - - // Collection to pair session names and OpenVidu Session objects - private Map mapSessions = new ConcurrentHashMap<>(); - // Collection to pair sessionId's and tokens (the inner Map pairs tokens and role associated) - private Map> mapSessionIdsTokens = new ConcurrentHashMap<>(); - - // URL where our OpenVidu server is listening - private String OPENVIDU_URL; - // Secret shared with our OpenVidu server - private String SECRET; - ``` - - Rest controller method begins retrieving the param send by the client, which in this case is the video-call name ("TUTORIAL"), as well as preparing a param we will need a little further on: `tokenOptions`. - - ```java - @RequestMapping(value = "/api-sessions/get-sessionid-token", method = RequestMethod.POST) - public ResponseEntity getSessionIdToken(@RequestBody String sessionNameParam, - HttpSession httpSession) throws ParseException { - // Check the user is logged ... - - JSONObject sessionJSON = (JSONObject) new JSONParser().parse(sessionNameParam); - - // The video-call to connect ("TUTORIAL") - String sessionName = (String) sessionJSON.get("session"); - - // Role associated to this user - OpenViduRole role = LoginController.users.get(httpSession.getAttribute("loggedUser")).role; - - // Optional data to be passed to other users when this user connects to the video-call - // In this case, a JSON with the value we stored in the HttpSession object on login - String serverData = "{\"serverData\": \"" + httpSession.getAttribute("loggedUser") + "\"}"; - - // Build tokenOptions object with the serverData and the role - TokenOptions tokenOptions = new TokenOptions.Builder().data(serverData).role(role).build(); - - JSONObject responseJson = new JSONObject(); - ``` - - Just after that an _if-else_ statement comes into play: does the session "TUTORIAL" already exitsts? - ```java - if (this.mapSessions.get(sessionName) != null) { ... - ``` - In this case it doesn't because 'publisher1' is the first user connecting to it. So we focus on the _else_ branch: - - ```java - else { - // New session: return a new sessionId and a new token - try { - - // Create a new OpenVidu Session - Session session = this.openVidu.createSession(); - // Get the sessionId - String sessionId = session.getSessionId(); - // Generate a new token with the recently created tokenOptions - String token = session.generateToken(tokenOptions); - - // Store the session and the token in our collections - this.mapSessions.put(sessionName, session); - this.mapSessionIdsTokens.put(sessionId, new ConcurrentHashMap<>()); - this.mapSessionIdsTokens.get(sessionId).put(token, OpenViduRole.PUBLISHER); - - // Prepare the response with the sessionId and the token - responseJson.put(0, sessionId); - responseJson.put(1, token); - - // Return the response to the client - return new ResponseEntity<>(responseJson, HttpStatus.OK); - - } catch (Exception e) { - // If error generate an error message and return it to client - return getErrorResponse(e); - } - } - ``` - We are almost there! Now in `app.js` we can init a new Session with _sessionId_ and connect to it with _token_: - - ```javascript - // --- 1) Get an OpenVidu object and init a session with the retrieved sessionId --- - - OV = new OpenVidu(); - session = OV.initSession(sessionId); - - - // --- 2) Specify the actions when events take place --- - - // On every new Stream received... - session.on('streamCreated', function (event) { - - // Subscribe to the Stream to receive it - // HTML video will be appended to element with 'subscriber' id - var subscriber = session.subscribe(event.stream, 'subscriber'); - - // When the HTML video has been appended to DOM... - subscriber.on('videoElementCreated', function (event) { - - // Add a new

element for the user's name and nickname just below its video - appendUserData(event.element, subscriber.stream.connection); - }); +```javascript +function logIn() { + var user = $("#user").val(); // Username + var pass = $("#pass").val(); // Password + var jsonBody = JSON.stringify({ // Body of POST request + 'user': user, + 'pass': pass }); - - // On every Stream destroyed... - session.on('streamDestroyed', function (event) { - // Delete the HTML element with the user's name and nickname - removeUserData(event.stream.connection); + + httpRequest('POST', '/api-login/login', jsonBody, 'Login WRONG', + function successCallback(response){ // Send POST request + console.warn(userName + ' login'); + // HTML shows logged-in page ... }); +} +``` + +`LoginController.java` checks the params are correct and if so sets an _HttpSession_ for the newly logged user (adding a "loggedUser" attribute with its username in the HttpSession object): + +```java +@RequestMapping(value = "/api-login/login", method = RequestMethod.POST) +public ResponseEntity login(@RequestBody String userPass, HttpSession httpSession) + throws ParseException { + + // Retrieve params from POST body + JSONObject userPassJson = (JSONObject) new JSONParser().parse(userPass); + String user = (String) userPassJson.get("user"); + String pass = (String) userPassJson.get("pass"); + + if (login(user, pass)){ // Correct user-pass + // Validate session and return OK + // Value stored in HttpSession allows us to identify the user in future requests + httpSession.setAttribute("loggedUser", user); + return new ResponseEntity<>(HttpStatus.OK); + } else { // Wrong user-pass + // Invalidate session and return error + httpSession.invalidate(); + return new ResponseEntity<>("User/Pass incorrect", HttpStatus.UNAUTHORIZED); + } +} +``` + +--- + +### 2) User connects to "TUTORIAL" video-call + +HTML will display now the user has logged in a different form, asking for the video-call to connect and the nickname the user wants to have in it. So our 'publisher1' user would write TUTORIAL in "Session" field and press "Join!" button: + +

+ +

+ +`app.js` will execute `joinSession()` method, which starts like this: + +```javascript +function joinSession() { + getSessionIdAndToken(function () { ... +``` +So the first thing to do here is to retrieve a _sessionId_ and a _token_ from our backend. Only when we have them available in the browser we will continue with the _join_ operation. Let's see what `getSessionIdAndToken()` looks like: + +```javascript +function getSessionIdAndToken(callback) { + sessionName = $("#sessionName").val(); // Video-call to connect ("TUTORIAL") + var jsonBody = JSON.stringify({ // Body of POST request + 'session': sessionName + }); + + // Send POST request + httpRequest('POST', '/api-sessions/get-sessionid-token', jsonBody, + 'Request of SESSIONID and TOKEN gone WRONG:', function successCallback(response){ + sessionId = response[0]; // Get sessionId from response + token = response[1]; // Get token from response + callback(); // Continue the join operation + }); +} +``` +Here is the second time we must call our `httpRequest()` method, sending the session we want to connect ("TUTORIAL") and waiting to get a _sessionId_ and a _token_ as response. The interesting part here is in `SessionController.java`. First of all there are some important attributes in this class we must mention: + +```java +// OpenVidu object to ask openvidu-server for sessionId and token +private OpenVidu openVidu; + +// Collection to pair session names and OpenVidu Session objects +private Map mapSessions = new ConcurrentHashMap<>(); +// Collection to pair sessionId's and tokens (the inner Map pairs tokens and role associated) +private Map> mapSessionIdsTokens = new ConcurrentHashMap<>(); + +// URL where our OpenVidu server is listening +private String OPENVIDU_URL; +// Secret shared with our OpenVidu server +private String SECRET; +``` + +Rest controller method begins retrieving the param send by the client, which in this case is the video-call name ("TUTORIAL"), as well as preparing a param we will need a little further on: `tokenOptions`. + +```java +@RequestMapping(value = "/api-sessions/get-sessionid-token", method = RequestMethod.POST) +public ResponseEntity getSessionIdToken(@RequestBody String sessionNameParam, + HttpSession httpSession) throws ParseException { + // Check the user is logged ... + + JSONObject sessionJSON = (JSONObject) new JSONParser().parse(sessionNameParam); + // The video-call to connect ("TUTORIAL") + String sessionName = (String) sessionJSON.get("session"); - // --- 3) Connect to the session passing the retrieved token and some more data from - // the client (in this case a JSON with the nickname chosen by the user) --- + // Role associated to this user + OpenViduRole role = LoginController.users.get(httpSession.getAttribute("loggedUser")).role; - session.connect(token, '{"clientData": "' + $("#participantName").val() + '"}', function (err) { + // Optional data to be passed to other users when this user connects to the video-call + // In this case, a JSON with the value we stored in the HttpSession object on login + String serverData = "{\"serverData\": \"" + httpSession.getAttribute("loggedUser") + "\"}"; + + // Build tokenOptions object with the serverData and the role + TokenOptions tokenOptions = new TokenOptions.Builder().data(serverData).role(role).build(); + + JSONObject responseJson = new JSONObject(); +``` + +Just after that an _if-else_ statement comes into play: does the session "TUTORIAL" already exitsts? +```java +if (this.mapSessions.get(sessionName) != null) { ... +``` +In this case it doesn't because 'publisher1' is the first user connecting to it. So we focus on the _else_ branch: + +```java +else { + // New session: return a new sessionId and a new token + try { - // If the connection is successful, initialize a publisher and publish to the session - if (!err) { + // Create a new OpenVidu Session + Session session = this.openVidu.createSession(); + // Get the sessionId + String sessionId = session.getSessionId(); + // Generate a new token with the recently created tokenOptions + String token = session.generateToken(tokenOptions); + + // Store the session and the token in our collections + this.mapSessions.put(sessionName, session); + this.mapSessionIdsTokens.put(sessionId, new ConcurrentHashMap<>()); + this.mapSessionIdsTokens.get(sessionId).put(token, OpenViduRole.PUBLISHER); + + // Prepare the response with the sessionId and the token + responseJson.put(0, sessionId); + responseJson.put(1, token); + + // Return the response to the client + return new ResponseEntity<>(responseJson, HttpStatus.OK); + + } catch (Exception e) { + // If error generate an error message and return it to client + return getErrorResponse(e); + } +} +``` +We are almost there! Now in `app.js` we can init a new Session with _sessionId_ and connect to it with _token_: + +```javascript +// --- 1) Get an OpenVidu object and init a session with the retrieved sessionId --- + +OV = new OpenVidu(); +session = OV.initSession(sessionId); + + +// --- 2) Specify the actions when events take place --- + +// On every new Stream received... +session.on('streamCreated', function (event) { + + // Subscribe to the Stream to receive it + // HTML video will be appended to element with 'subscriber' id + var subscriber = session.subscribe(event.stream, 'subscriber'); - // Here we check somehow if the user has at least 'PUBLISHER' role before - // trying to publish its stream. Even if someone modified the client's code and - // published the stream, it won't work if the token sent in Session.connect - // method doesn't belong to a 'PUBLIHSER' role - if (isPublisher()) { + // When the HTML video has been appended to DOM... + subscriber.on('videoElementCreated', function (event) { - // --- 4) Get your own camera stream and publish it --- - - var publisher = OV.initPublisher('publisher', { - audio: true, - video: true, - quality: 'MEDIUM' - }); - - - // --- 5) Publish your stream --- - - session.publish(publisher); - - } else { - console.warn('You don\'t have permissions to publish'); - } + // Add a new

element for the user's name and nickname just below its video + appendUserData(event.element, subscriber.stream.connection); + }); +}); + +// On every Stream destroyed... +session.on('streamDestroyed', function (event) { + // Delete the HTML element with the user's name and nickname + removeUserData(event.stream.connection); +}); + + +// --- 3) Connect to the session passing the retrieved token and some more data from +// the client (in this case a JSON with the nickname chosen by the user) --- + +session.connect(token, '{"clientData": "' + $("#participantName").val() + '"}', function (err) { + + // If the connection is successful, initialize a publisher and publish to the session + if (!err) { + + // Here we check somehow if the user has at least 'PUBLISHER' role before + // trying to publish its stream. Even if someone modified the client's code and + // published the stream, it won't work if the token sent in Session.connect + // method doesn't belong to a 'PUBLIHSER' role + if (isPublisher()) { + + // --- 4) Get your own camera stream and publish it --- + + var publisher = OV.initPublisher('publisher', { + audio: true, + video: true, + quality: 'MEDIUM' + }); + + + // --- 5) Publish your stream --- + + session.publish(publisher); + } else { - console.warn('Error connecting to the session:', error.code, error.message); + console.warn('You don\'t have permissions to publish'); } + } else { + console.warn('Error connecting to the session:', error.code, error.message); + } + + // HTML shows session page ... + +}); +``` +The user will now see its own video on the page. The connection to the session has completed! + +--- + +### 3) Another user connects to the video-call + +The process would be exactly the same as before until `SessionController.java` executes `getSessionIdAndToken()` method. Now session 'TUTORIAL' already exists, so in the _if-else_ statement the _if_ branch would be the one executed: + +```java +if (this.mapSessions.get(sessionName) != null) { + // Session already exists: return existing sessionId and a new token + try { + + // Get the existing sessionId from our collection with + // the sessionName param ("TUTORIAL") + String sessionId = this.mapSessions.get(sessionName).getSessionId(); + // Generate a new token with the recently created tokenOptions + String token = this.mapSessions.get(sessionName).generateToken(tokenOptions); - // HTML shows session page ... + // Update our collection storing the new token + this.mapSessionIdsTokens.get(sessionId).put(token, OpenViduRole.PUBLISHER); + // Prepare the response with the sessionId and the token + responseJson.put(0, sessionId); + responseJson.put(1, token); + + // Return the response to the client + return new ResponseEntity<>(responseJson, HttpStatus.OK); + + } catch (Exception e) { + // If error generate an error message and return it to client + return getErrorResponse(e); + } +} +``` +The code executed in `app.js` would also be the same. After the `Session.publish()` method has been succesful, both users will be seeing each other's video, as well as the username and the nickname below it. + +--- + +### 4) Users leave the video-call + +After a while both users decide to leave the session. Apart from calling `leaveSession()` (and therefore `session.disconnect()`) to destroy the connection on openvidu-server, we need to run the last HTTP operation: we must let the backend know that certain user has left the session so it can update the collections with the active sessions and tokens. To sum up, `session.disconnect()` updates our openvidu-server and the POST operation updates our backend. +For the POST operation, in `app.js` we run: + +```javascript +function removeUser() { + // Body of POST request with the name of the session and the token of the leaving user + var jsonBody = JSON.stringify({ + 'sessionName': sessionName, + 'token': token }); - ``` - The user will now see its own video on the page. The connection to the session has completed! + // Send POST request + httpRequest('POST', '/api-sessions/remove-user', jsonBody, + 'User couldn\'t be removed from session', function successCallback(response) { + console.warn(userName + ' correctly removed from session ' + sessionName); + }); +} +``` +And in `SessionController.java` we update the collections: -3. **Another user connects to the video-call** +```java +@RequestMapping(value = "/api-sessions/remove-user", method = RequestMethod.POST) +public ResponseEntity removeUser(@RequestBody String sessionNameToken, + HttpSession httpSession) throws Exception { + // Check the user is logged ... - The process would be exactly the same as before until `SessionController.java` executes `getSessionIdAndToken()` method. Now session 'TUTORIAL' already exists, so in the _if-else_ statement the _if_ branch would be the one executed: + // Retrieve the params from BODY + JSONObject sessionNameTokenJSON = (JSONObject) new JSONParser().parse(sessionNameToken); + String sessionName = (String) sessionNameTokenJSON.get("sessionName"); + String token = (String) sessionNameTokenJSON.get("token"); - ```java + // If the session exists ("TUTORIAL" in this case) if (this.mapSessions.get(sessionName) != null) { - // Session already exists: return existing sessionId and a new token - try { - - // Get the existing sessionId from our collection with - // the sessionName param ("TUTORIAL") - String sessionId = this.mapSessions.get(sessionName).getSessionId(); - // Generate a new token with the recently created tokenOptions - String token = this.mapSessions.get(sessionName).generateToken(tokenOptions); - - // Update our collection storing the new token - this.mapSessionIdsTokens.get(sessionId).put(token, OpenViduRole.PUBLISHER); - - // Prepare the response with the sessionId and the token - responseJson.put(0, sessionId); - responseJson.put(1, token); - - // Return the response to the client - return new ResponseEntity<>(responseJson, HttpStatus.OK); - - } catch (Exception e) { - // If error generate an error message and return it to client - return getErrorResponse(e); - } - } - ``` - The code executed in `app.js` would also be the same. After the `Session.publish()` method has been succesful, both users will be seeing each other's video, as well as the username and the nickname below it. + String sessionId = this.mapSessions.get(sessionName).getSessionId(); -4. **Users leave the video-call** - - After a while both users decide to leave the session. Apart from calling `leaveSession()` (and therefore `session.disconnect()`) to destroy the connection on openvidu-server, we need to run the last HTTP operation: we must let the backend know that certain user has left the session so it can update the collections with the active sessions and tokens. To sum up, `session.disconnect()` updates our openvidu-server and the POST operation updates our backend. - For the POST operation, in `app.js` we run: - - ```javascript - function removeUser() { - // Body of POST request with the name of the session and the token of the leaving user - var jsonBody = JSON.stringify({ - 'sessionName': sessionName, - 'token': token - }); - - // Send POST request - httpRequest('POST', '/api-sessions/remove-user', jsonBody, - 'User couldn\'t be removed from session', function successCallback(response) { - console.warn(userName + ' correctly removed from session ' + sessionName); - }); - } - ``` - And in `SessionController.java` we update the collections: - - ```java - @RequestMapping(value = "/api-sessions/remove-user", method = RequestMethod.POST) - public ResponseEntity removeUser(@RequestBody String sessionNameToken, - HttpSession httpSession) throws Exception { - // Check the user is logged ... - - // Retrieve the params from BODY - JSONObject sessionNameTokenJSON = (JSONObject) new JSONParser().parse(sessionNameToken); - String sessionName = (String) sessionNameTokenJSON.get("sessionName"); - String token = (String) sessionNameTokenJSON.get("token"); - - // If the session exists ("TUTORIAL" in this case) - if (this.mapSessions.get(sessionName) != null) { - String sessionId = this.mapSessions.get(sessionName).getSessionId(); - - if (this.mapSessionIdsTokens.containsKey(sessionId)) { - // If the token exists - if (this.mapSessionIdsTokens.get(sessionId).remove(token) != null) { - // Token has been removed - if (this.mapSessionIdsTokens.get(sessionId).isEmpty()) { - // Last user left: session "TUTORIAL" must be removed - this.mapSessions.remove(sessionName); - } - return new ResponseEntity<>(HttpStatus.OK); - } else { - // The TOKEN wasn't valid - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + if (this.mapSessionIdsTokens.containsKey(sessionId)) { + // If the token exists + if (this.mapSessionIdsTokens.get(sessionId).remove(token) != null) { + // Token has been removed + if (this.mapSessionIdsTokens.get(sessionId).isEmpty()) { + // Last user left: session "TUTORIAL" must be removed + this.mapSessions.remove(sessionName); } + return new ResponseEntity<>(HttpStatus.OK); } else { - // The SESSIONID wasn't valid + // The TOKEN wasn't valid return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } else { - // The SESSION does not exist + // The SESSIONID wasn't valid return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } + } else { + // The SESSION does not exist + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } - ``` - When the last user leaves the session `this.mapSessions.remove(sessionName);` will be executed: this means the session is empty and that it is going to be closed. The _sessionId_ and all _token_ params associated to it will be invalidated. +} +``` +When the last user leaves the session `this.mapSessions.remove(sessionName);` will be executed: this means the session is empty and that it is going to be closed. The _sessionId_ and all _token_ params associated to it will be invalidated. --- diff --git a/openvidu-js-node/README.md b/openvidu-js-node/README.md index 922382c4..05cde09a 100644 --- a/openvidu-js-node/README.md +++ b/openvidu-js-node/README.md @@ -70,261 +70,162 @@ This is a very basic web application with a pretty simple vanilla JS/HTML/CSS fr Let's describe the code following this scenario: a user logs in to the app and connects to the video-call "TUTORIAL", where he publishes his webcam. A second user will connect to the same video-call just after that and publish its own webcam. Both of them will leave the call after a while. -1. **User logs in** +--- - We have implemented a method for making HTTP requests to the backend, as we will need to make at least three of them: one for logging in, one for getting the sessionId and a valid token from openvidu-server and a one for letting know our backend when any user leaves the video-call. The header of the method looks like this: - - ```javascript - function httpRequest(method, url, body, errorMsg, callback) - ``` - - Where `method` is whether "POST" or "GET", `url` the path of the REST operation, `body` the data to be passed, `errorMsg` the output error message if something goes wrong and `callback` the function to execute in case of success. As mentioned above, we need to call this method three times for each user that LOGS IN 🡒 CONNECTS TO A VIDEO-CALL 🡒 LEAVES THE VIDEO-CALL. +### 1) User logs in - `index.html` will first show a form to log in: +We have implemented a method for making HTTP requests to the backend, as we will need to make at least three of them: one for logging in, one for getting the sessionId and a valid token from openvidu-server and a one for letting know our backend when any user leaves the video-call. The header of the method looks like this: -

- -

+```javascript +function httpRequest(method, url, body, errorMsg, callback) +``` - `app.js` sends an HTTP request to "/api-login/login" passing the username and the password retrieved from the HTML form whenever "Log in" button is clicked: - - ```javascript - function logIn() { - var user = $("#user").val(); // Username - var pass = $("#pass").val(); // Password - var jsonBody = JSON.stringify({ // Body of POST request - 'user': user, - 'pass': pass - }); - - httpRequest('POST', '/api-login/login', jsonBody, 'Login WRONG', - function successCallback(response){ // Send POST request - console.warn(userName + ' login'); - // HTML shows logged-in page ... - }); - } - ``` +Where `method` is whether "POST" or "GET", `url` the path of the REST operation, `body` the data to be passed, `errorMsg` the output error message if something goes wrong and `callback` the function to execute in case of success. As mentioned above, we need to call this method three times for each user that LOGS IN 🡒 CONNECTS TO A VIDEO-CALL 🡒 LEAVES THE VIDEO-CALL. - `server.js` at `/api-login/login` checks the params are correct and if so sets an active session for the newly logged user (adding a _loggedUser_ property with its username in the _req.session_ object): +`index.html` will first show a form to log in: - ```javascript - app.post('/api-login/login', function (req, res) { - - // Retrieve params from POST body - var user = req.body.user; - var pass = req.body.pass; - - if (login(user, pass)) { // Correct user-pass - // Validate session and return OK - // Value stored in req.session allows us to identify the user in future requests - req.session.loggedUser = user; - res.status(200).send(); - } else { // Wrong user-pass - // Invalidate session and return error - req.session.destroy(); - res.status(401).send('User/Pass incorrect'); - } +

+ +

+ +`app.js` sends an HTTP request to "/api-login/login" passing the username and the password retrieved from the HTML form whenever "Log in" button is clicked: + +```javascript +function logIn() { + var user = $("#user").val(); // Username + var pass = $("#pass").val(); // Password + var jsonBody = JSON.stringify({ // Body of POST request + 'user': user, + 'pass': pass }); - ``` -2. **User connects to "TUTORIAL" video-call** - - HTML will display now the user has logged in a different form, asking for the video-call to connect and the nickname the user wants to have in it. So our 'publisher1' user would write TUTORIAL in "Session" field and press "Join!" button: - -

- -

- - `app.js` will execute `joinSession()` method, which starts like this: - - ```javascript - function joinSession() { - getSessionIdAndToken(function () { ... - ``` - So the first thing to do here is to retrieve a _sessionId_ and a _token_ from our backend. Only when we have them available in the browser we will continue with the _join_ operation. Let's see what `getSessionIdAndToken()` looks like: - - ```javascript - function getSessionIdAndToken(callback) { - sessionName = $("#sessionName").val(); // Video-call to connect ("TUTORIAL") - var jsonBody = JSON.stringify({ // Body of POST request - 'session': sessionName - }); - - // Send POST request - httpRequest('POST', '/api-sessions/get-sessionid-token', jsonBody, - 'Request of SESSIONID and TOKEN gone WRONG:', function successCallback(response){ - sessionId = response[0]; // Get sessionId from response - token = response[1]; // Get token from response - callback(); // Continue the join operation - }); - } - ``` - Here is the second time we must call our `httpRequest()` method, sending the session we want to connect ("TUTORIAL") and waiting to get a _sessionId_ and a _token_ as response. The interesting part here is in `server.js` controller at `/api-sessions/get-sessionid-token`. First of all there are some important attributes in this class we must mention: - - ```javascript - // Environment variable: URL where our OpenVidu server is listening - var OPENVIDU_URL = process.argv[2]; - // Environment variable: secret shared with our OpenVidu server - var OPENVIDU_SECRET = process.argv[3]; - - // OpenVidu object to ask openvidu-server for sessionId and token - var OV = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET); - - // Collection to pair session names and OpenVidu Session objects - var mapSessionNameSession = {}; - // Collection to pair sessionId's (identifiers of Session objects) and tokens - var mapSessionIdTokens = {}; - ``` - - Rest controller method begins retrieving the param send by the client, which in this case is the video-call name ("TUTORIAL"), as well as preparing a param we will need a little further on: `tokenOptions`. - - - ```javascript - app.post('/api-sessions/get-sessionid-token', function (req, res) { - // Check the user is logged ... - - // The video-call to connect ("TUTORIAL") - var sessionName = req.body.session; - - // Role associated to this user - var role = users.find(u => (u.user === req.session.loggedUser)).role; - - // Optional data to be passed to other users when this user connects to the video-call - // In this case, a JSON with the value we stored in the req.session object on login - var serverData = '{"serverData": "' + req.session.loggedUser + '"}'; - - // Build tokenOptions object with the serverData and the role - var tokenOptions = new TokenOptions.Builder() - .data(serverData) - .role(role) - .build(); - ``` - - Just after that an _if-else_ statement comes into play: does the session "TUTORIAL" already exitsts? - ```javascript - if (mapSessionNameSession[sessionName]) { ... - ``` - In this case it doesn't because 'publisher1' is the first user connecting to it. So we focus on the _else_ branch: - - ```javascript - else { // New session: return a new sessionId and a new token - // Create a new OpenVidu Session - var mySession = OV.createSession(); - - // Get the sessionId asynchronously - mySession.getSessionId(function (sessionId) { - - // Store the new Session in the collection of Sessions - mapSessionNameSession[sessionName] = mySession; - // Store a new empty array in the collection of tokens - mapSessionIdTokens[sessionId] = []; - - // Generate a new token asynchronously with the recently created tokenOptions - mySession.generateToken(tokenOptions, function (token) { - - // Store the new token in the collection of tokens - mapSessionIdTokens[sessionId].push(token); - - // Return the sessionId and token to the client - res.status(200).send({ - 0: sessionId, - 1: token - }); - }); - }); - } - ``` - We are almost there! Now in `app.js` we can init a new Session with _sessionId_ and connect to it with _token_: - - ```javascript - // --- 1) Get an OpenVidu object and init a session with the retrieved sessionId --- - - OV = new OpenVidu(); - session = OV.initSession(sessionId); - - - // --- 2) Specify the actions when events take place --- - - // On every new Stream received... - session.on('streamCreated', function (event) { - - // Subscribe to the Stream to receive it - // HTML video will be appended to element with 'subscriber' id - var subscriber = session.subscribe(event.stream, 'subscriber'); - - // When the HTML video has been appended to DOM... - subscriber.on('videoElementCreated', function (event) { - - // Add a new

element for the user's name and nickname just below its video - appendUserData(event.element, subscriber.stream.connection); - }); + httpRequest('POST', '/api-login/login', jsonBody, 'Login WRONG', + function successCallback(response){ // Send POST request + console.warn(userName + ' login'); + // HTML shows logged-in page ... }); - - // On every Stream destroyed... - session.on('streamDestroyed', function (event) { - // Delete the HTML element with the user's name and nickname - removeUserData(event.stream.connection); +} +``` + +`server.js` at `/api-login/login` checks the params are correct and if so sets an active session for the newly logged user (adding a _loggedUser_ property with its username in the _req.session_ object): + +```javascript +app.post('/api-login/login', function (req, res) { + + // Retrieve params from POST body + var user = req.body.user; + var pass = req.body.pass; + + if (login(user, pass)) { // Correct user-pass + // Validate session and return OK + // Value stored in req.session allows us to identify the user in future requests + req.session.loggedUser = user; + res.status(200).send(); + } else { // Wrong user-pass + // Invalidate session and return error + req.session.destroy(); + res.status(401).send('User/Pass incorrect'); + } +}); +``` + +--- + +### 2) User connects to "TUTORIAL" video-call + +HTML will display now the user has logged in a different form, asking for the video-call to connect and the nickname the user wants to have in it. So our 'publisher1' user would write TUTORIAL in "Session" field and press "Join!" button: + +

+ +

+ +`app.js` will execute `joinSession()` method, which starts like this: + +```javascript +function joinSession() { + getSessionIdAndToken(function () { ... +``` +So the first thing to do here is to retrieve a _sessionId_ and a _token_ from our backend. Only when we have them available in the browser we will continue with the _join_ operation. Let's see what `getSessionIdAndToken()` looks like: + +```javascript +function getSessionIdAndToken(callback) { + sessionName = $("#sessionName").val(); // Video-call to connect ("TUTORIAL") + var jsonBody = JSON.stringify({ // Body of POST request + 'session': sessionName }); - - - // --- 3) Connect to the session passing the retrieved token and some more data from - // the client (in this case a JSON with the nickname chosen by the user) --- - - session.connect(token, '{"clientData": "' + $("#participantName").val() + '"}', function (err) { - - // If the connection is successful, initialize a publisher and publish to the session - if (!err) { - - // Here we check somehow if the user has at least 'PUBLISHER' role before - // trying to publish its stream. Even if someone modified the client's code and - // published the stream, it won't work if the token sent in Session.connect - // method doesn't belong to a 'PUBLIHSER' role - if (isPublisher()) { - - // --- 4) Get your own camera stream and publish it --- - - var publisher = OV.initPublisher('publisher', { - audio: true, - video: true, - quality: 'MEDIUM' - }); - - - // --- 5) Publish your stream --- - - session.publish(publisher); - - } else { - console.warn('You don\'t have permissions to publish'); - } - } else { - console.warn('Error connecting to the session:', error.code, error.message); - } - - // HTML shows session page ... - + + // Send POST request + httpRequest('POST', '/api-sessions/get-sessionid-token', jsonBody, + 'Request of SESSIONID and TOKEN gone WRONG:', function successCallback(response){ + sessionId = response[0]; // Get sessionId from response + token = response[1]; // Get token from response + callback(); // Continue the join operation }); - ``` - The user will now see its own video on the page. The connection to the session has completed! +} +``` +Here is the second time we must call our `httpRequest()` method, sending the session we want to connect ("TUTORIAL") and waiting to get a _sessionId_ and a _token_ as response. The interesting part here is in `server.js` controller at `/api-sessions/get-sessionid-token`. First of all there are some important attributes in this class we must mention: + +```javascript +// Environment variable: URL where our OpenVidu server is listening +var OPENVIDU_URL = process.argv[2]; +// Environment variable: secret shared with our OpenVidu server +var OPENVIDU_SECRET = process.argv[3]; + +// OpenVidu object to ask openvidu-server for sessionId and token +var OV = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET); + +// Collection to pair session names and OpenVidu Session objects +var mapSessionNameSession = {}; +// Collection to pair sessionId's (identifiers of Session objects) and tokens +var mapSessionIdTokens = {}; +``` + +Rest controller method begins retrieving the param send by the client, which in this case is the video-call name ("TUTORIAL"), as well as preparing a param we will need a little further on: `tokenOptions`. -3. **Another user connects to the video-call** +```javascript +app.post('/api-sessions/get-sessionid-token', function (req, res) { + // Check the user is logged ... - The process would be exactly the same as before until `server.js` executes controller at `/api-sessions/get-sessionid-token`. Now session 'TUTORIAL' already exists, so in the _if-else_ statement the _if_ branch would be the one executed: + // The video-call to connect ("TUTORIAL") + var sessionName = req.body.session; - ```javascript - if (mapSessionNameSession[sessionName]) { - // Session already exists: return existing sessionId and a new token + // Role associated to this user + var role = users.find(u => (u.user === req.session.loggedUser)).role; + + // Optional data to be passed to other users when this user connects to the video-call + // In this case, a JSON with the value we stored in the req.session object on login + var serverData = '{"serverData": "' + req.session.loggedUser + '"}'; + + // Build tokenOptions object with the serverData and the role + var tokenOptions = new TokenOptions.Builder() + .data(serverData) + .role(role) + .build(); +``` + +Just after that an _if-else_ statement comes into play: does the session "TUTORIAL" already exitsts? +```javascript +if (mapSessionNameSession[sessionName]) { ... +``` +In this case it doesn't because 'publisher1' is the first user connecting to it. So we focus on the _else_ branch: + +```javascript +else { // New session: return a new sessionId and a new token + // Create a new OpenVidu Session + var mySession = OV.createSession(); + + // Get the sessionId asynchronously + mySession.getSessionId(function (sessionId) { - // Get the existing Session from the collection - var mySession = mapSessionNameSession[sessionName]; + // Store the new Session in the collection of Sessions + mapSessionNameSession[sessionName] = mySession; + // Store a new empty array in the collection of tokens + mapSessionIdTokens[sessionId] = []; // Generate a new token asynchronously with the recently created tokenOptions mySession.generateToken(tokenOptions, function (token) { - // Get the existing sessionId - var sessionId = mySession.getSessionId(); - // Store the new token in the collection of tokens mapSessionIdTokens[sessionId].push(token); @@ -334,68 +235,174 @@ Let's describe the code following this scenario: a user logs in to the app and c 1: token }); }); - } - ``` - The code executed in `app.js` would also be the same. After the `Session.publish()` method has been succesful, both users will be seeing each other's video, as well as the username and the nickname below it. + }); +} +``` +We are almost there! Now in `app.js` we can init a new Session with _sessionId_ and connect to it with _token_: -4. **Users leave the video-call** +```javascript +// --- 1) Get an OpenVidu object and init a session with the retrieved sessionId --- - After a while both users decide to leave the session. Apart from calling `leaveSession()` (and therefore `session.disconnect()`) to destroy the connection on openvidu-server, we need to run the last HTTP operation: we must let the backend know that certain user has left the session so it can update the collections with the active sessions and tokens. To sum up, `session.disconnect()` updates our openvidu-server and the POST operation updates our backend. - For the POST operation, in `app.js` we run: +OV = new OpenVidu(); +session = OV.initSession(sessionId); - ```javascript - function removeUser() { - // Body of POST request with the name of the session and the token of the leaving user - var jsonBody = JSON.stringify({ - 'sessionName': sessionName, - 'token': token - }); + +// --- 2) Specify the actions when events take place --- + +// On every new Stream received... +session.on('streamCreated', function (event) { + + // Subscribe to the Stream to receive it + // HTML video will be appended to element with 'subscriber' id + var subscriber = session.subscribe(event.stream, 'subscriber'); - // Send POST request - httpRequest('POST', '/api-sessions/remove-user', jsonBody, - 'User couldn\'t be removed from session', function successCallback(response) { - console.warn(userName + ' correctly removed from session ' + sessionName); - }); - } - ``` - And in `server.js` we update the collections in `/api-sessions/remove-user`: + // When the HTML video has been appended to DOM... + subscriber.on('videoElementCreated', function (event) { - ```javascript - app.post('/api-sessions/remove-user', function (req, res) { - // Check the user is logged ... - - // Retrieve params from POST body - var sessionName = req.body.sessionName; - var token = req.body.token; - - // If the session exists ("TUTORIAL" in this case) - var mySession = mapSessionNameSession[sessionName]; - if (mySession) { - var tokens = mapSessionIdTokens[mySession.getSessionId()]; - if (tokens) { - var index = tokens.indexOf(token); - - // If the token exists - if (index !== -1) { - // Token removed! - tokens.splice(index, 1); - } else { - res.status(500).send('The TOKEN wasn\'t valid'); - } - if (mapSessionIdTokens[mySession.getSessionId()].length == 0) { - // Last user left: session "TUTORIAL" must be removed - delete mapSessionNameSession[sessionName]; - } - res.status(200).send(); - } else { - res.status(500).send('The SESSIONID wasn\'t valid'); - } + // Add a new

element for the user's name and nickname just below its video + appendUserData(event.element, subscriber.stream.connection); + }); +}); + +// On every Stream destroyed... +session.on('streamDestroyed', function (event) { + // Delete the HTML element with the user's name and nickname + removeUserData(event.stream.connection); +}); + + +// --- 3) Connect to the session passing the retrieved token and some more data from +// the client (in this case a JSON with the nickname chosen by the user) --- + +session.connect(token, '{"clientData": "' + $("#participantName").val() + '"}', function (err) { + + // If the connection is successful, initialize a publisher and publish to the session + if (!err) { + + // Here we check somehow if the user has at least 'PUBLISHER' role before + // trying to publish its stream. Even if someone modified the client's code and + // published the stream, it won't work if the token sent in Session.connect + // method doesn't belong to a 'PUBLIHSER' role + if (isPublisher()) { + + // --- 4) Get your own camera stream and publish it --- + + var publisher = OV.initPublisher('publisher', { + audio: true, + video: true, + quality: 'MEDIUM' + }); + + + // --- 5) Publish your stream --- + + session.publish(publisher); + } else { - res.status(500).send('The SESSION does not exist'); + console.warn('You don\'t have permissions to publish'); } + } else { + console.warn('Error connecting to the session:', error.code, error.message); } - ``` - When the last user leaves the session `delete mapSessionNameSession[sessionName]` will be executed: this means the session is empty and that it is going to be closed. The _sessionId_ and all _token_ params associated to it will be invalidated. + + // HTML shows session page ... + +}); +``` +The user will now see its own video on the page. The connection to the session has completed! + +--- + +### 3) Another user connects to the video-call + +The process would be exactly the same as before until `server.js` executes controller at `/api-sessions/get-sessionid-token`. Now session 'TUTORIAL' already exists, so in the _if-else_ statement the _if_ branch would be the one executed: + +```javascript +if (mapSessionNameSession[sessionName]) { + // Session already exists: return existing sessionId and a new token + + // Get the existing Session from the collection + var mySession = mapSessionNameSession[sessionName]; + + // Generate a new token asynchronously with the recently created tokenOptions + mySession.generateToken(tokenOptions, function (token) { + + // Get the existing sessionId + var sessionId = mySession.getSessionId(); + + // Store the new token in the collection of tokens + mapSessionIdTokens[sessionId].push(token); + + // Return the sessionId and token to the client + res.status(200).send({ + 0: sessionId, + 1: token + }); + }); +} +``` +The code executed in `app.js` would also be the same. After the `Session.publish()` method has been succesful, both users will be seeing each other's video, as well as the username and the nickname below it. + +--- + +### 4) Users leave the video-call + +After a while both users decide to leave the session. Apart from calling `leaveSession()` (and therefore `session.disconnect()`) to destroy the connection on openvidu-server, we need to run the last HTTP operation: we must let the backend know that certain user has left the session so it can update the collections with the active sessions and tokens. To sum up, `session.disconnect()` updates our openvidu-server and the POST operation updates our backend. +For the POST operation, in `app.js` we run: + +```javascript +function removeUser() { + // Body of POST request with the name of the session and the token of the leaving user + var jsonBody = JSON.stringify({ + 'sessionName': sessionName, + 'token': token + }); + + // Send POST request + httpRequest('POST', '/api-sessions/remove-user', jsonBody, + 'User couldn\'t be removed from session', function successCallback(response) { + console.warn(userName + ' correctly removed from session ' + sessionName); + }); +} +``` +And in `server.js` we update the collections in `/api-sessions/remove-user`: + +```javascript +app.post('/api-sessions/remove-user', function (req, res) { + // Check the user is logged ... + + // Retrieve params from POST body + var sessionName = req.body.sessionName; + var token = req.body.token; + + // If the session exists ("TUTORIAL" in this case) + var mySession = mapSessionNameSession[sessionName]; + if (mySession) { + var tokens = mapSessionIdTokens[mySession.getSessionId()]; + if (tokens) { + var index = tokens.indexOf(token); + + // If the token exists + if (index !== -1) { + // Token removed! + tokens.splice(index, 1); + } else { + res.status(500).send('The TOKEN wasn\'t valid'); + } + if (mapSessionIdTokens[mySession.getSessionId()].length == 0) { + // Last user left: session "TUTORIAL" must be removed + delete mapSessionNameSession[sessionName]; + } + res.status(200).send(); + } else { + res.status(500).send('The SESSIONID wasn\'t valid'); + } + } else { + res.status(500).send('The SESSION does not exist'); + } +} +``` +When the last user leaves the session `delete mapSessionNameSession[sessionName]` will be executed: this means the session is empty and that it is going to be closed. The _sessionId_ and all _token_ params associated to it will be invalidated. --- diff --git a/openvidu-mvc-java/README.md b/openvidu-mvc-java/README.md index db1861d3..cb42cb14 100644 --- a/openvidu-mvc-java/README.md +++ b/openvidu-mvc-java/README.md @@ -68,334 +68,342 @@ OpenVidu assumes you can identify your users so you can tell which users can con Let's describe the code following this scenario: a user logs in to the app and connects to the video-call "TUTORIAL", where he publishes his webcam. A second user will connect to the same video-call just after that and publish its own webcam. Both of them will leave the call after a while. -1. **User logs in** +--- - At path `/` a login form will be displayed: +### 1) User logs in -

- +At path `/` a login form will be displayed: + +

+ +

+ +The form will execute a POST operation to path `/dashboard` whenever "Log in" button is clicked, passing the username and the password: + +```html +
+

+

- - The form will execute a POST operation to path `/dashboard` whenever "Log in" button is clicked, passing the username and the password: - - ```html - -

- -

-

- -

-

- -

-
- ``` - - `LoginController.java` first checks if the user is already logged (maybe he has just refreshed `/dashboard` page), and if so it just redirects to the dashboard itself. If the user is actually logging in, the method checks that the params are correct and if so sets an _HttpSession_ for the newly logged user (adding a "loggedUser" attribute with its username in the HttpSession object). Finally it returns `dashboard.html` template: - - ```java - @RequestMapping(value = "/dashboard", method = { RequestMethod.GET, RequestMethod.POST }) - public String login(@RequestParam(name = "user", required = false) String user, - @RequestParam(name = "pass", required = false) String pass, - Model model, HttpSession httpSession) { - - // Check if the user is already logged in - String userName = (String) httpSession.getAttribute("loggedUser"); - if (userName != null) { - // User is already logged. Immediately return dashboard - model.addAttribute("username", userName); - return "dashboard"; - } - - // User wasn't logged and wants to - if (login(user, pass)) { // Correct user-pass - - // Validate session and return OK - // Value stored in HttpSession allows us to identify the user in future requests - httpSession.setAttribute("loggedUser", user); - model.addAttribute("username", user); - - // Return dashboard.html template - return "dashboard"; - - } else { // Wrong user-pass - // Invalidate session and redirect to index.html - httpSession.invalidate(); - return "redirect:/"; - } - } - ``` - -2. **User connects to "TUTORIAL" video-call** - - `dashboard.html` template will display a form asking for the video-call to connect and the nickname the user wants to have in it. So our 'publisher1' user would write TUTORIAL in "Session" field: - -

- +

+

+

+ +

+ +``` - The form will execute a POST operation to path `/session` whenever "Join!" button is clicked, passing the nickname and the session name: +`LoginController.java` first checks if the user is already logged (maybe he has just refreshed `/dashboard` page), and if so it just redirects to the dashboard itself. If the user is actually logging in, the method checks that the params are correct and if so sets an _HttpSession_ for the newly logged user (adding a "loggedUser" attribute with its username in the HttpSession object). Finally it returns `dashboard.html` template: - ```html -
-

- -

-

- -

-

- -

-
- ``` +```java +@RequestMapping(value = "/dashboard", method = { RequestMethod.GET, RequestMethod.POST }) +public String login(@RequestParam(name = "user", required = false) String user, + @RequestParam(name = "pass", required = false) String pass, + Model model, HttpSession httpSession) { - When `SessionController.java` receives a request at `/session` path is when things get interesting. - First of all there are some important attributes in this class we must mention: - - ```java - // OpenVidu object to ask openvidu-server for sessionId and token - private OpenVidu openVidu; - - // Collection to pair session names and OpenVidu Session objects - private Map mapSessions = new ConcurrentHashMap<>(); - // Collection to pair sessionId's and tokens (the inner Map pairs tokens and role associated) - private Map> mapSessionIdsTokens = new ConcurrentHashMap<>(); - - // URL where our OpenVidu server is listening - private String OPENVIDU_URL; - // Secret shared with our OpenVidu server - private String SECRET; - ``` - - Rest controller method receives both params sent by the client (whatever nickname the user has chosen and "TUTORIAL" as the sessionName). First it prepares a param we will need a little further on: `tokenOptions`. - - ```java - @RequestMapping(value = "/session", method = RequestMethod.POST) - public String joinSession(@RequestParam(name = "data") String clientData, - @RequestParam(name = "session-name") String sessionName, - Model model, HttpSession httpSession) { - // Check the user is logged ... - - // Role associated to this user - OpenViduRole role = LoginController.users.get(httpSession.getAttribute("loggedUser")).role; - - // Optional data to be passed to other users when this user connects to the video-call - // In this case, a JSON with the value we stored in the HttpSession object on login - String serverData = "{\"serverData\": \"" + httpSession.getAttribute("loggedUser") + "\"}"; - - // Build tokenOptions object with the serverData and the role - TokenOptions tokenOptions = new TokenOptions.Builder().data(serverData).role(role).build(); - ``` - - Just after that an _if-else_ statement comes into play: does the session "TUTORIAL" already exitsts? - ```java - if (this.mapSessions.get(sessionName) != null) { ... - ``` - In this case it doesn't because 'publisher1' is the first user connecting to it. So we focus on the _else_ branch: - - ```java - else { - // New session: return a new sessionId and a new token - try { - - // Create a new OpenVidu Session - Session session = this.openVidu.createSession(); - // Get the sessionId - String sessionId = session.getSessionId(); - // Generate a new token with the recently created tokenOptions - String token = session.generateToken(tokenOptions); - - // Store the session and the token in our collections - this.mapSessions.put(sessionName, session); - this.mapSessionIdsTokens.put(sessionId, new ConcurrentHashMap<>()); - this.mapSessionIdsTokens.get(sessionId).put(token, OpenViduRole.PUBLISHER); - - // Add all the needed attributes to the template - model.addAttribute("sessionId", sessionId); - model.addAttribute("token", token); - model.addAttribute("nickName", clientData); - model.addAttribute("userName", httpSession.getAttribute("loggedUser")); - model.addAttribute("sessionName", sessionName); - - // Return session.html template - return "session"; - - } catch (Exception e) { - // If error just return dashboard.html template - model.addAttribute("username", httpSession.getAttribute("loggedUser")); - return "dashboard"; - } + // Check if the user is already logged in + String userName = (String) httpSession.getAttribute("loggedUser"); + if (userName != null) { + // User is already logged. Immediately return dashboard + model.addAttribute("username", userName); + return "dashboard"; } - ``` - We are almost there! Now in `session.html` JavaScript code (preceded by a tag `