diff --git a/openvidu-js-java/README.md b/openvidu-js-java/README.md index 700d7eb4..2f4e41a0 100644 --- a/openvidu-js-java/README.md +++ b/openvidu-js-java/README.md @@ -1,6 +1,6 @@ # openvidu-js-java -A secure OpenVidu sample app with a Java backend and a SPA frontend. It makes use of openvidu-java-client to get the necessary params from OpenVidu Server. +A secure OpenVidu sample app with a Java backend and a SPA frontend. It makes use of _openvidu-java-client_ to get the necessary params from OpenVidu Server. ## Understanding this example @@ -48,7 +48,7 @@ OpenVidu is composed by the modules displayed on the image above. ## Understanding the code -This is a very basic web application with a pretty simple vanilla JS/HTML/CSS frontend and a straightforward Java backend. OpenVidu assumes you can identify your users so you can tell which users can connect to which video-calls, and what role (and therefore what permissions) each one of them will have in the calls. You can do this as you prefer. Here our backend will manage the users and their sessions by using the non-intrusive _HttpSession_ API. In these posts multiple options for user session management in Java are explained, inlcuding the one used in this tutorial: [journaldev.com](http://www.journaldev.com/1907/java-session-management-servlet-httpsession-url-rewriting), [studytonight.com](http://www.studytonight.com/servlet/session-management.php). +This is a very basic web application with a pretty simple vanilla JS/HTML/CSS frontend and a straightforward Java backend. OpenVidu assumes you can identify your users so you can tell which users can connect to which video-calls, and what role (and therefore what permissions) each one of them will have in the calls. You can do this as you prefer. Here our backend will manage the users and their sessions with the easy-to-use and non-intrusive _HttpSession_ API. In these posts multiple options for user session management in Java are explained, inlcuding the one used in this tutorial: [journaldev.com](http://www.journaldev.com/1907/java-session-management-servlet-httpsession-url-rewriting), [studytonight.com](http://www.studytonight.com/servlet/session-management.php). - Backend: SpringBoot app with the following classes (`src/main/java` path, `io.openvidu.js.java` package) - `App.java` : entrypoint for the app @@ -179,7 +179,7 @@ Let's describe the code following this scenario: a user logs in to the app and c 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 two params we will need a little further on: `role` and `serverData`. + 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) @@ -199,6 +199,9 @@ Let's describe the code following this scenario: a user logs in to the app and c // 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(); ``` @@ -217,9 +220,8 @@ Let's describe the code following this scenario: a user logs in to the app and c Session session = this.openVidu.createSession(); // Get the sessionId String sessionId = session.getSessionId(); - // Generate a new token with the recently retrieved serverData and role - String token = session.generateToken(new - TokenOptions.Builder().data(serverData).role(role).build()); + // 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); @@ -325,10 +327,8 @@ Let's describe the code following this scenario: a user logs in to the app and c // 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 retrieved serverData and role - String token = this.mapSessions.get(sessionName) - .generateToken(new - TokenOptions.Builder().data(serverData).role(role).build()); + // 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); diff --git a/openvidu-js-java/src/main/java/io/openvidu/js/java/SessionController.java b/openvidu-js-java/src/main/java/io/openvidu/js/java/SessionController.java index b7c50d9b..ac6ba214 100644 --- a/openvidu-js-java/src/main/java/io/openvidu/js/java/SessionController.java +++ b/openvidu-js-java/src/main/java/io/openvidu/js/java/SessionController.java @@ -55,6 +55,8 @@ public class SessionController { OpenViduRole role = LoginController.users.get(httpSession.getAttribute("loggedUser")).role; String serverData = "{\"serverData\": \"" + httpSession.getAttribute("loggedUser") + "\"}"; + TokenOptions tokenOptions = new TokenOptions.Builder().data(serverData).role(role).build(); + JSONObject responseJson = new JSONObject(); if (this.mapSessions.get(sessionName) != null) { @@ -62,8 +64,7 @@ public class SessionController { System.out.println("Existing session " + sessionName); try { String sessionId = this.mapSessions.get(sessionName).getSessionId(); - String token = this.mapSessions.get(sessionName) - .generateToken(new TokenOptions.Builder().data(serverData).role(role).build()); + String token = this.mapSessions.get(sessionName).generateToken(tokenOptions); this.mapSessionIdsTokens.get(sessionId).put(token, OpenViduRole.PUBLISHER); @@ -81,7 +82,7 @@ public class SessionController { try { Session session = this.openVidu.createSession(); String sessionId = session.getSessionId(); - String token = session.generateToken(new TokenOptions.Builder().data(serverData).role(role).build()); + String token = session.generateToken(tokenOptions); this.mapSessions.put(sessionName, session); this.mapSessionIdsTokens.put(sessionId, new ConcurrentHashMap<>()); diff --git a/openvidu-js-java/src/main/resources/static/app.js b/openvidu-js-java/src/main/resources/static/app.js index 9ce7126d..db9cc23b 100644 --- a/openvidu-js-java/src/main/resources/static/app.js +++ b/openvidu-js-java/src/main/resources/static/app.js @@ -19,7 +19,7 @@ window.onbeforeunload = function () { // Gracefully leave session function appendUserData(videoElement, connection) { var clientDataJSON = JSON.parse(connection.data.split('%/%')[0]); var serverDataJSON = JSON.parse(connection.data.split('%/%')[1]); - $("
Nickname: " + clientDataJSON.clientData + + $("
Nickname: " + clientDataJSON.clientData +
"
Username: " + serverDataJSON.serverData + "
+
+
+
+
+
+
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('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 `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. + +--- + +> At this point we have covered all the important code from the tutorial. With this scenario we have seen the most common use-case, but you can modify whatever you want to suit your needs. And remember that this is just one of the many possible approaches: **you can implement your frontend and your backend as you want**. +> +> The only actual requirements are getting ***sessionId*** and ***token*** params from ***openvidu-server*** (by using one of the available clients or with the REST API) and using them along with ***openvidu-browser*** to connect your clients to the sessions. \ No newline at end of file diff --git a/openvidu-js-node/public/app.js b/openvidu-js-node/public/app.js index 21c92391..db9cc23b 100644 --- a/openvidu-js-node/public/app.js +++ b/openvidu-js-node/public/app.js @@ -121,6 +121,7 @@ function httpRequest(method, url, body, errorMsg, callback) { /* APPLICATION BACKEND METHODS */ + /* OPENVIDU METHODS */ function joinSession() { @@ -128,8 +129,8 @@ function joinSession() { // 1) Get an OpenVidu object and init a session with a sessionId - OV = new OpenVidu("wss://" + location.hostname + ":8443/"); - session = OV.initSession("apikey", sessionId); + OV = new OpenVidu(); + session = OV.initSession(sessionId); // 2) Specify the actions when events take place diff --git a/openvidu-js-node/server.js b/openvidu-js-node/server.js index 23ed155a..f651d353 100644 --- a/openvidu-js-node/server.js +++ b/openvidu-js-node/server.js @@ -1,9 +1,11 @@ +/* CONFIGURATION */ + var OpenVidu = require('openvidu-node-client').OpenVidu; var Session = require('openvidu-node-client').Session; var OpenViduRole = require('openvidu-node-client').OpenViduRole; var TokenOptions = require('openvidu-node-client').TokenOptions; -// Check launch arguments +// Check launch arguments: must receive openvidu-server URL and the secret if (process.argv.length != 4) { console.log("Usage: node " + __filename + " OPENVIDU_URL OPENVIDU_SECRET"); process.exit(-1); @@ -11,30 +13,30 @@ if (process.argv.length != 4) { // For demo purposes we ignore self-signed certificate process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0" -// Imports +// Node imports var express = require('express'); var fs = require('fs'); var session = require('express-session'); var https = require('https'); -var bodyParser = require('body-parser'); // pull information from HTML POST (express4) -var app = express(); // create our app w/ express +var bodyParser = require('body-parser'); // Pull information from HTML POST (express4) +var app = express(); // Create our app with express -// Configuration +// Server configuration app.use(session({ saveUninitialized: true, resave: false, secret: 'MY_SECRET' })); -app.use(express.static(__dirname + '/public')); // set the static files location /public/img will be /img for users +app.use(express.static(__dirname + '/public')); // Set the static files location app.use(bodyParser.urlencoded({ 'extended': 'true' -})); // parse application/x-www-form-urlencoded -app.use(bodyParser.json()); // parse application/json +})); // Parse application/x-www-form-urlencoded +app.use(bodyParser.json()); // Parse application/json app.use(bodyParser.json({ type: 'application/vnd.api+json' -})); // parse application/vnd.api+json as json +})); // Parse application/vnd.api+json as json -// listen (start app with node server.js) +// Listen (start app with node server.js) var options = { key: fs.readFileSync('openvidukey.pem'), cert: fs.readFileSync('openviducert.pem') @@ -69,8 +71,13 @@ var OV = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET); var mapSessionNameSession = {}; var mapSessionIdTokens = {}; +/* CONFIGURATION */ -// APIRest + + +/* REST API */ + +// Login app.post('/api-login/login', function (req, res) { var user = req.body.user; var pass = req.body.pass; @@ -87,12 +94,14 @@ app.post('/api-login/login', function (req, res) { } }); +// Logout app.get('/api-login/logout', function (req, res) { console.log("'" + req.session.loggedUser + "' has logged out"); req.session.destroy(); res.status(200).send(); }); +// Get sessionId and token (add new user to session) app.post('/api-sessions/get-sessionid-token', function (req, res) { if (!isLogged(req.session)) { req.session.destroy(); @@ -103,14 +112,14 @@ app.post('/api-sessions/get-sessionid-token', function (req, res) { var serverData = '{"serverData": "' + req.session.loggedUser + '"}'; console.log("Getting sessionId and token | {sessionName}={" + sessionName + "}"); - var mySession = mapSessionNameSession[sessionName]; var tokenOptions = new TokenOptions.Builder() + .data(serverData) .role(role) - .data('{"serverData": "' + req.session.loggedUser + '"}') .build(); - if (mySession) { + if (mapSessionNameSession[sessionName]) { console.log('Existing session ' + sessionName); + var mySession = mapSessionNameSession[sessionName]; mySession.generateToken(tokenOptions, function (token) { var sessionId = mySession.getSessionId(); mapSessionIdTokens[sessionId].push(token); @@ -123,7 +132,7 @@ app.post('/api-sessions/get-sessionid-token', function (req, res) { }); } else { console.log('New session ' + sessionName); - mySession = OV.createSession(); + var mySession = OV.createSession(); mySession.getSessionId(function (sessionId) { mapSessionNameSession[sessionName] = mySession; mapSessionIdTokens[sessionId] = []; @@ -142,6 +151,7 @@ app.post('/api-sessions/get-sessionid-token', function (req, res) { } }); +// Remove user from session app.post('/api-sessions/remove-user', function (req, res) { if (!isLogged(req.session)) { req.session.destroy(); @@ -182,6 +192,11 @@ app.post('/api-sessions/remove-user', function (req, res) { } }); +/* REST API */ + + + +/* AUXILIARY METHODS */ function login(user, pass) { return (users.find(u => (u.user === user) && (u.pass === pass))); @@ -193,4 +208,6 @@ function isLogged(session) { function getBasicAuth() { return 'Basic ' + (new Buffer('OPENVIDUAPP:' + OPENVIDU_SECRET).toString('base64')); -} \ No newline at end of file +} + +/* AUXILIARY METHODS */ diff --git a/openvidu-mvc-java/README.md b/openvidu-mvc-java/README.md index 5a782b25..2841cbdc 100644 --- a/openvidu-mvc-java/README.md +++ b/openvidu-mvc-java/README.md @@ -50,7 +50,7 @@ OpenVidu is composed by the modules displayed on the image above. This is a very basic web application with a pretty simple vanilla JS/HTML/CSS frontend and a straightforward Java backend that serves HTML files with a MVC approach, building the templates with the help of [Thymeleaf](http://www.thymeleaf.org/). -OpenVidu assumes you can identify your users so you can tell which users can connect to which video-calls, and what role (and therefore what permissions) each one of them will have in the calls. You can do this as you prefer. Here our backend will manage the users and their sessions by using the non-intrusive _HttpSession_ API. In these posts multiple options for user session management in Java are explained, inlcuding the one used in this tutorial: [journaldev.com](http://www.journaldev.com/1907/java-session-management-servlet-httpsession-url-rewriting), [studytonight.com](http://www.studytonight.com/servlet/session-management.php). +OpenVidu assumes you can identify your users so you can tell which users can connect to which video-calls, and what role (and therefore what permissions) each one of them will have in the calls. You can do this as you prefer. Here our backend will manage the users and their sessions with the easy-to-use and non-intrusive _HttpSession_ API. In these posts multiple options for user session management in Java are explained, inlcuding the one used in this tutorial: [journaldev.com](http://www.journaldev.com/1907/java-session-management-servlet-httpsession-url-rewriting), [studytonight.com](http://www.studytonight.com/servlet/session-management.php). - **Backend**: SpringBoot app with the following classes (`src/main/java` path, `io.openvidu.js.java` package) - `App.java` : entrypoint for the app @@ -169,7 +169,7 @@ Let's describe the code following this scenario: a user logs in to the app and c 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 two params we will need a little further on: `role` and `serverData`. + 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) @@ -184,6 +184,9 @@ Let's describe the code following this scenario: a user logs in to the app and c // 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? @@ -201,9 +204,8 @@ Let's describe the code following this scenario: a user logs in to the app and c Session session = this.openVidu.createSession(); // Get the sessionId String sessionId = session.getSessionId(); - // Generate a new token with the recently retrieved serverData and role - String token = session.generateToken(new - TokenOptions.Builder().data(serverData).role(role).build()); + // 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); @@ -314,10 +316,8 @@ Let's describe the code following this scenario: a user logs in to the app and c // 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 retrieved serverData and role - String token = this.mapSessions.get(sessionName) - .generateToken(new - TokenOptions.Builder().data(serverData).role(role).build()); + // 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); diff --git a/openvidu-mvc-java/src/main/java/io/openvidu/mvc/java/SessionController.java b/openvidu-mvc-java/src/main/java/io/openvidu/mvc/java/SessionController.java index 9b967cd9..e7898331 100644 --- a/openvidu-mvc-java/src/main/java/io/openvidu/mvc/java/SessionController.java +++ b/openvidu-mvc-java/src/main/java/io/openvidu/mvc/java/SessionController.java @@ -47,14 +47,14 @@ public class SessionController { OpenViduRole role = LoginController.users.get(httpSession.getAttribute("loggedUser")).role; String serverData = "{\"serverData\": \"" + httpSession.getAttribute("loggedUser") + "\"}"; + TokenOptions tokenOptions = new TokenOptions.Builder().data(serverData).role(role).build(); if (this.mapSessions.get(sessionName) != null) { // Session already exists: return existing sessionId and a new token System.out.println("Existing session " + sessionName); try { String sessionId = this.mapSessions.get(sessionName).getSessionId(); - String token = this.mapSessions.get(sessionName) - .generateToken(new TokenOptions.Builder().data(serverData).role(role).build()); + String token = this.mapSessions.get(sessionName).generateToken(tokenOptions); this.mapSessionIdsTokens.get(sessionId).put(token, OpenViduRole.PUBLISHER); @@ -76,7 +76,7 @@ public class SessionController { try { Session session = this.openVidu.createSession(); String sessionId = session.getSessionId(); - String token = session.generateToken(new TokenOptions.Builder().data(serverData).role(role).build()); + String token = session.generateToken(tokenOptions); this.mapSessions.put(sessionName, session); this.mapSessionIdsTokens.put(sessionId, new ConcurrentHashMap<>()); diff --git a/openvidu-mvc-node/server.js b/openvidu-mvc-node/server.js index e6931f9a..8e7b9049 100644 --- a/openvidu-mvc-node/server.js +++ b/openvidu-mvc-node/server.js @@ -130,14 +130,14 @@ app.post('/session', (req, res) => { var serverData = '{"serverData": "' + req.session.loggedUser + '"}'; console.log("Getting sessionId and token | {sessionName}={" + sessionName + "}"); - var mySession = mapSessionNameSession[sessionName]; var tokenOptions = new TokenOptions.Builder() + .data(serverData) .role(role) - .data('{"serverData": "' + req.session.loggedUser + '"}') .build(); - if (mySession) { + if (mapSessionNameSession[sessionName]) { console.log('Existing session ' + sessionName); + var mySession = mapSessionNameSession[sessionName]; mySession.generateToken(tokenOptions, function (token) { var sessionId = mySession.getSessionId(); mapSessionIdTokens[sessionId].push(token); @@ -153,7 +153,7 @@ app.post('/session', (req, res) => { }); } else { console.log('New session ' + sessionName); - mySession = OV.createSession(); + var mySession = OV.createSession(); mySession.getSessionId(function (sessionId) { mapSessionNameSession[sessionName] = mySession; mapSessionIdTokens[sessionId] = [];