From 7a48d52958dd593872568cae465fc43136e308fe Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Thu, 19 Jul 2018 12:59:58 +0200 Subject: [PATCH] openvidu-java-client: update to support 2.3.0 --- openvidu-java-client/pom.xml | 6 + .../io/openvidu/java/client/Connection.java | 117 ++++++ .../io/openvidu/java/client/MediaMode.java | 4 + .../io/openvidu/java/client/OpenVidu.java | 202 +++++++-- .../io/openvidu/java/client/OpenViduRole.java | 12 +- .../io/openvidu/java/client/Publisher.java | 122 ++++++ .../io/openvidu/java/client/Recording.java | 6 + .../openvidu/java/client/RecordingLayout.java | 6 + .../openvidu/java/client/RecordingMode.java | 4 + .../java/client/RecordingProperties.java | 9 +- .../java/io/openvidu/java/client/Session.java | 392 ++++++++++++++++-- .../java/client/SessionProperties.java | 3 + .../io/openvidu/java/client/TokenOptions.java | 3 + .../openvidu-instance.component.ts | 6 +- 14 files changed, 823 insertions(+), 69 deletions(-) create mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/Connection.java create mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/Publisher.java diff --git a/openvidu-java-client/pom.xml b/openvidu-java-client/pom.xml index 22e6be8b..6fbd4934 100644 --- a/openvidu-java-client/pom.xml +++ b/openvidu-java-client/pom.xml @@ -84,6 +84,12 @@ ${version.json-simple} + + org.slf4j + slf4j-api + 1.7.25 + + diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/Connection.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/Connection.java new file mode 100644 index 00000000..0b6e1765 --- /dev/null +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/Connection.java @@ -0,0 +1,117 @@ +/* + * (C) Copyright 2017-2018 OpenVidu (https://openvidu.io/) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package io.openvidu.java.client; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * See {@link io.openvidu.java.client.Session#getActiveConnections()} + */ +public class Connection { + + private String connectionId; + private OpenViduRole role; + private String token; + private String serverData; + private String clientData; + + protected Map publishers; + protected List subscribers; + + protected Connection(String connectionId, OpenViduRole role, String token, String serverData, String clientData, + Map publishers, List subscribers) { + this.connectionId = connectionId; + this.role = role; + this.token = token; + this.serverData = serverData; + this.clientData = clientData; + this.publishers = publishers; + this.subscribers = subscribers; + } + + /** + * Returns the identifier of the connection. You can call + * {@link io.openvidu.java.client.Session#forceDisconnect(String)} passing this + * property as parameter + */ + public String getConnectionId() { + return connectionId; + } + + /** + * Returns the role of the connection + */ + public OpenViduRole getRole() { + return role; + } + + /** + * Returns the token associated to the connection + */ + public String getToken() { + return token; + } + + /** + * Returns the data associated to the connection on the server-side. This value + * is set with {@link io.openvidu.java.client.TokenOptions.Builder#data(String)} + * when calling {@link io.openvidu.java.client.Session#generateToken()} + */ + public String getServerData() { + return serverData; + } + + /** + * Returns the data associated to the connection on the client-side. This value + * is set with second parameter of method Session.connect in OpenVidu Browser + */ + public String getClientData() { + return clientData; + } + + /** + * Returns the list of Publisher objects this particular Connection is + * publishing to the Session (each Publisher object has one Stream, uniquely + * identified by its streamId). You can call + * {@link io.openvidu.java.client.Session#forceUnpublish(Publisher)} passing any + * of this values as parameter + */ + public List getPublishers() { + return new ArrayList<>(this.publishers.values()); + } + + /** + * Returns the list of streams (their streamId properties) this + * particular Connection is subscribed to. Each one always corresponds to one + * Publisher of some other Connection: each string of the returned list must be + * equal to the returned value of some + * {@link io.openvidu.java.client.Publisher#getStreamId()} + */ + public List getSubscribers() { + return this.subscribers; + } + + protected void setSubscribers(List subscribers) { + this.subscribers = subscribers; + } + +} diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/MediaMode.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/MediaMode.java index 950798f8..5924ff4e 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/MediaMode.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/MediaMode.java @@ -17,6 +17,10 @@ package io.openvidu.java.client; +/** + * See + * {@link io.openvidu.java.client.SessionProperties.Builder#mediaMode(MediaMode)} + */ public enum MediaMode { /** * (not available yet) The session will attempt to transmit streams diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java index aefcb367..a07b17b6 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java @@ -25,7 +25,13 @@ import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import javax.net.ssl.SSLContext; @@ -35,13 +41,14 @@ import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.ssl.TrustStrategy; import org.apache.http.util.EntityUtils; @@ -49,16 +56,23 @@ import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class OpenVidu { + private static final Logger log = LoggerFactory.getLogger(OpenVidu.class); + private String urlOpenViduServer; private String secret; private HttpClient myHttpClient; + protected static Map activeSessions = new ConcurrentHashMap<>(); - final static String API_RECORDINGS = "api/recordings"; - final static String API_RECORDINGS_START = "/start"; - final static String API_RECORDINGS_STOP = "/stop"; + protected final static String API_SESSIONS = "api/sessions"; + protected final static String API_TOKENS = "api/tokens"; + protected final static String API_RECORDINGS = "api/recordings"; + protected final static String API_RECORDINGS_START = "/start"; + protected final static String API_RECORDINGS_STOP = "/stop"; /** * @param urlOpenViduServer @@ -96,9 +110,13 @@ public class OpenVidu { throw new RuntimeException(e); } - this.myHttpClient = HttpClients.custom().setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) - .setSSLContext(sslContext).setDefaultCredentialsProvider(provider).build(); + RequestConfig.Builder requestBuilder = RequestConfig.custom(); + requestBuilder = requestBuilder.setConnectTimeout(30000); + requestBuilder = requestBuilder.setConnectionRequestTimeout(30000); + this.myHttpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestBuilder.build()) + .setConnectionTimeToLive(30, TimeUnit.SECONDS).setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) + .setSSLContext(sslContext).setDefaultCredentialsProvider(provider).build(); } /** @@ -111,6 +129,7 @@ public class OpenVidu { */ public Session createSession() throws OpenViduJavaClientException, OpenViduHttpException { Session s = new Session(myHttpClient, urlOpenViduServer); + OpenVidu.activeSessions.put(s.getSessionId(), s); return s; } @@ -135,6 +154,7 @@ public class OpenVidu { public Session createSession(SessionProperties properties) throws OpenViduJavaClientException, OpenViduHttpException { Session s = new Session(myHttpClient, urlOpenViduServer, properties); + OpenVidu.activeSessions.put(s.getSessionId(), s); return s; } @@ -193,11 +213,15 @@ public class OpenVidu { throw new OpenViduJavaClientException(e2.getMessage(), e2.getCause()); } - int statusCode = response.getStatusLine().getStatusCode(); - if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { - return new Recording(httpResponseToJson(response)); - } else { - throw new OpenViduHttpException(statusCode); + try { + int statusCode = response.getStatusLine().getStatusCode(); + if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { + return new Recording(httpResponseToJson(response)); + } else { + throw new OpenViduHttpException(statusCode); + } + } finally { + EntityUtils.consumeQuietly(response.getEntity()); } } @@ -300,11 +324,15 @@ public class OpenVidu { throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); } - int statusCode = response.getStatusLine().getStatusCode(); - if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { - return new Recording(httpResponseToJson(response)); - } else { - throw new OpenViduHttpException(statusCode); + try { + int statusCode = response.getStatusLine().getStatusCode(); + if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { + return new Recording(httpResponseToJson(response)); + } else { + throw new OpenViduHttpException(statusCode); + } + } finally { + EntityUtils.consumeQuietly(response.getEntity()); } } @@ -332,11 +360,15 @@ public class OpenVidu { throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); } - int statusCode = response.getStatusLine().getStatusCode(); - if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { - return new Recording(httpResponseToJson(response)); - } else { - throw new OpenViduHttpException(statusCode); + try { + int statusCode = response.getStatusLine().getStatusCode(); + if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { + return new Recording(httpResponseToJson(response)); + } else { + throw new OpenViduHttpException(statusCode); + } + } finally { + EntityUtils.consumeQuietly(response.getEntity()); } } @@ -358,17 +390,21 @@ public class OpenVidu { throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); } - int statusCode = response.getStatusLine().getStatusCode(); - if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { - List recordings = new ArrayList<>(); - JSONObject json = httpResponseToJson(response); - JSONArray array = (JSONArray) json.get("items"); - array.forEach(item -> { - recordings.add(new Recording((JSONObject) item)); - }); - return recordings; - } else { - throw new OpenViduHttpException(statusCode); + try { + int statusCode = response.getStatusLine().getStatusCode(); + if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { + List recordings = new ArrayList<>(); + JSONObject json = httpResponseToJson(response); + JSONArray array = (JSONArray) json.get("items"); + array.forEach(item -> { + recordings.add(new Recording((JSONObject) item)); + }); + return recordings; + } else { + throw new OpenViduHttpException(statusCode); + } + } finally { + EntityUtils.consumeQuietly(response.getEntity()); } } @@ -399,9 +435,105 @@ public class OpenVidu { throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); } - int statusCode = response.getStatusLine().getStatusCode(); - if (!(statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) { - throw new OpenViduHttpException(statusCode); + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (!(statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) { + throw new OpenViduHttpException(statusCode); + } + } finally { + EntityUtils.consumeQuietly(response.getEntity()); + } + } + + /** + * Returns the list of active sessions. This value will remain unchanged + * since the last time method {@link io.openvidu.java.client.OpenVidu#fetch()} + * was called. Exceptions to this rule are: + *
    + *
  • Calling {@link io.openvidu.java.client.Session#fetch()} updates that + * specific Session status
  • + *
  • Calling {@link io.openvidu.java.client.Session#close()} automatically + * removes the Session from the list of active Sessions
  • + *
  • Calling + * {@link io.openvidu.java.client.Session#forceDisconnect(Connection)} + * automatically updates the inner affected connections for that specific + * Session
  • + *
  • Calling {@link io.openvidu.java.client.Session#forceUnpublish(Publisher)} + * also automatically updates the inner affected connections for that specific + * Session
  • + *
+ *
+ * To get the list of active sessions with their current actual value, you must + * call first {@link io.openvidu.java.client.OpenVidu#fetch()} and then + * {@link io.openvidu.java.client.OpenVidu#getActiveSessions()} + */ + public List getActiveSessions() { + return new ArrayList<>(OpenVidu.activeSessions.values()); + } + + /** + * Updates every property of every active Session with the current status they + * have in OpenVidu Server. After calling this method you can acces the updated + * list of active sessions by calling + * {@link io.openvidu.java.client.OpenVidu#getActiveSessions()} + * + * @throws OpenViduHttpException + * @throws OpenViduJavaClientException + */ + @SuppressWarnings("unchecked") + public boolean fetch() throws OpenViduJavaClientException, OpenViduHttpException { + HttpGet request = new HttpGet(this.urlOpenViduServer + API_SESSIONS); + + HttpResponse response; + try { + response = myHttpClient.execute(request); + } catch (IOException e) { + throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); + } + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { + JSONObject jsonSessions = httpResponseToJson(response); + JSONArray jsonArraySessions = (JSONArray) jsonSessions.get("content"); + + // Set to store fetched sessionIds and later remove closed sessions + Set fetchedSessionIds = new HashSet<>(); + // Boolean to store if any Session has changed + boolean hasChanged = false; + jsonArraySessions.forEach(session -> { + String sessionId = (String) ((JSONObject) session).get("sessionId"); + fetchedSessionIds.add(sessionId); + OpenVidu.activeSessions.computeIfPresent(sessionId, (sId, s) -> { + String beforeJSON = s.toJson(); + s = s.resetSessionWithJson((JSONObject) session); + String afterJSON = s.toJson(); + log.info("Available session '{}' info fetched. Any change: {}", sessionId, + !beforeJSON.equals(afterJSON)); + return s; + }); + OpenVidu.activeSessions.computeIfAbsent(sessionId, sId -> { + log.info("New session '{}' fetched", sessionId); + return new Session((JSONObject) session); + }); + }); + + // Remove closed sessions from activeSessions map + OpenVidu.activeSessions = OpenVidu.activeSessions.entrySet().stream().filter(entry -> { + if (fetchedSessionIds.contains(entry.getKey())) { + return true; + } else { + log.info("Removing closed session {}" + entry.getKey()); + return false; + } + }).collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())); + log.info("Active sessions info fetched: {}", OpenVidu.activeSessions.keySet()); + return hasChanged; + } else { + throw new OpenViduHttpException(statusCode); + } + } finally { + EntityUtils.consumeQuietly(response.getEntity()); } } diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenViduRole.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenViduRole.java index c47bb5fd..295ff9a7 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenViduRole.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenViduRole.java @@ -17,6 +17,9 @@ package io.openvidu.java.client; +/** + * See {@link io.openvidu.java.client.TokenOptions.Builder#role(OpenViduRole)} + */ public enum OpenViduRole { /** @@ -25,13 +28,16 @@ public enum OpenViduRole { SUBSCRIBER, /** - * SUBSCRIBER permissions + can publish their own Streams (call Session.publish()) + * SUBSCRIBER permissions + can publish their own Streams (call + * Session.publish()) */ PUBLISHER, /** - * SUBSCRIBER + PUBLISHER permissions + can force the unpublishing or disconnection over a third-party Stream or Connection - * (call Session.forceUnpublish() and Session.forceDisconnect()) + * SUBSCRIBER + PUBLISHER permissions + can force the unpublishing or + * disconnection over a third-party Stream or Connection (call + * Session.forceUnpublish() and + * Session.forceDisconnect()) */ MODERATOR; } diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/Publisher.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/Publisher.java new file mode 100644 index 00000000..a64d4c30 --- /dev/null +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/Publisher.java @@ -0,0 +1,122 @@ +/* + * (C) Copyright 2017-2018 OpenVidu (https://openvidu.io/) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package io.openvidu.java.client; + +import org.json.simple.JSONObject; + +/** + * See {@link io.openvidu.java.client.Connection#getPublishers()} + */ +public class Publisher { + + class MediaOptions { + + protected MediaOptions(boolean hasAudio, boolean hasVideo, Boolean audioActive, Boolean videoActive, + Integer frameRate, String typeOfVideo, String videoDimensions) { + this.hasAudio = hasAudio; + this.hasVideo = hasVideo; + this.audioActive = audioActive; + this.videoActive = videoActive; + this.frameRate = frameRate; + this.typeOfVideo = typeOfVideo; + this.videoDimensions = videoDimensions; + } + + boolean hasVideo; + boolean hasAudio; + Boolean audioActive; + Boolean videoActive; + Integer frameRate; + String typeOfVideo; + String videoDimensions; + + } + + private String streamId; + private MediaOptions mediaOptions; + + public Publisher(String streamId, boolean hasAudio, boolean hasVideo, Object audioActive, Object videoActive, + Object frameRate, Object typeOfVideo, Object videoDimensions) { + this.streamId = streamId; + Boolean audioActiveAux = null; + Boolean videoActiveAux = null; + Integer frameRateAux = null; + String typeOfVideoAux = null; + String videoDimensionsAux = null; + if (hasAudio) { + audioActiveAux = (boolean) audioActive; + } + if (hasVideo) { + videoActiveAux = (boolean) videoActive; + if (frameRate != null) { + frameRateAux = ((Long) frameRate).intValue(); + } + typeOfVideoAux = (String) typeOfVideo; + videoDimensionsAux = (String) videoDimensions; + } + this.mediaOptions = new MediaOptions(hasAudio, hasVideo, audioActiveAux, videoActiveAux, frameRateAux, + typeOfVideoAux, videoDimensionsAux); + } + + public String getStreamId() { + return streamId; + } + + public boolean hasVideo() { + return this.mediaOptions.hasVideo; + } + + public boolean hasAudio() { + return this.mediaOptions.hasAudio; + } + + public Boolean isAudioActive() { + return this.mediaOptions.audioActive; + } + + public Boolean isVideoActive() { + return this.mediaOptions.videoActive; + } + + public Integer getFrameRate() { + return this.mediaOptions.frameRate; + } + + public String getTypeOfVideo() { + return this.mediaOptions.typeOfVideo; + } + + public String getVideoDimensions() { + return this.mediaOptions.videoDimensions; + } + + @SuppressWarnings("unchecked") + protected JSONObject toJson() { + JSONObject json = new JSONObject(); + json.put("streamId", this.streamId); + json.put("hasAudio", this.hasAudio()); + json.put("hasVideo", this.hasVideo()); + json.put("audioActive", this.isAudioActive()); + json.put("videoActive", this.isVideoActive()); + json.put("frameRate", this.getFrameRate()); + json.put("typeOfVideo", this.getTypeOfVideo()); + json.put("videoDimensions", this.getVideoDimensions()); + return json; + } + +} diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/Recording.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/Recording.java index 6557661a..9b424c24 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/Recording.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/Recording.java @@ -19,8 +19,14 @@ package io.openvidu.java.client; import org.json.simple.JSONObject; +/** + * See {@link io.openvidu.java.client.OpenVidu#startRecording(String)} + */ public class Recording { + /** + * See {@link io.openvidu.java.client.Recording#getStatus()} + */ public enum Status { /** diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingLayout.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingLayout.java index 31620858..b3f4cd88 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingLayout.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingLayout.java @@ -17,6 +17,12 @@ package io.openvidu.java.client; +/** + * See + * {@link io.openvidu.java.client.SessionProperties.Builder#defaultRecordingLayout(RecordingLayout)} + * and + * {@link io.openvidu.java.client.RecordingProperties.Builder#recordingLayout(RecordingLayout)} + */ public enum RecordingLayout { /** diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingMode.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingMode.java index 089ef08b..fa8dc12e 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingMode.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingMode.java @@ -17,6 +17,10 @@ package io.openvidu.java.client; +/** + * See + * {@link io.openvidu.java.client.SessionProperties.Builder#recordingMode(RecordingMode)} + */ public enum RecordingMode { /** diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingProperties.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingProperties.java index 37bdb55b..3161947a 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingProperties.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingProperties.java @@ -17,17 +17,24 @@ package io.openvidu.java.client; +/** + * See + * {@link io.openvidu.java.client.OpenVidu#startRecording(String, RecordingProperties)} + */ public class RecordingProperties { private String name; private RecordingLayout recordingLayout; private String customLayout; + /** + * Builder for {@link io.openvidu.java.client.RecordingProperties} + */ public static class Builder { private String name = ""; private RecordingLayout recordingLayout; - private String customLayout; + private String customLayout = ""; /** * Builder for {@link io.openvidu.java.client.RecordingProperties} diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/Session.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/Session.java index 561e18e5..89adae68 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/Session.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/Session.java @@ -19,33 +19,43 @@ package io.openvidu.java.client; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.util.EntityUtils; +import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Session { + private static final Logger log = LoggerFactory.getLogger(Session.class); + private HttpClient httpClient; private String urlOpenViduServer; private String sessionId; private SessionProperties properties; - - final static String API_SESSIONS = "api/sessions"; - final static String API_TOKENS = "api/tokens"; + private Map activeConnections = new ConcurrentHashMap<>(); + private boolean recording = false; protected Session(HttpClient httpClient, String urlOpenViduServer) throws OpenViduJavaClientException, OpenViduHttpException { this.httpClient = httpClient; this.urlOpenViduServer = urlOpenViduServer; - this.properties = new SessionProperties(); + this.properties = new SessionProperties.Builder().build(); this.getSessionIdHttp(); } @@ -57,6 +67,10 @@ public class Session { this.getSessionIdHttp(); } + protected Session(JSONObject json) { + this.resetSessionWithJson(json); + } + /** * Gets the unique identifier of the Session * @@ -97,7 +111,7 @@ public class Session { this.getSessionId(); } - HttpPost request = new HttpPost(this.urlOpenViduServer + API_TOKENS); + HttpPost request = new HttpPost(this.urlOpenViduServer + OpenVidu.API_TOKENS); JSONObject json = new JSONObject(); json.put("session", this.sessionId); @@ -120,12 +134,17 @@ public class Session { throw new OpenViduJavaClientException(e2.getMessage(), e2.getCause()); } - int statusCode = response.getStatusLine().getStatusCode(); - if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { - System.out.println("Returning a TOKEN"); - return (String) httpResponseToJson(response).get("id"); - } else { - throw new OpenViduHttpException(statusCode); + try { + int statusCode = response.getStatusLine().getStatusCode(); + if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { + String token = (String) httpResponseToJson(response).get("id"); + log.info("Returning a TOKEN: {}", token); + return token; + } else { + throw new OpenViduHttpException(statusCode); + } + } finally { + EntityUtils.consumeQuietly(response.getEntity()); } } @@ -137,7 +156,7 @@ public class Session { * @throws OpenViduHttpException */ public void close() throws OpenViduJavaClientException, OpenViduHttpException { - HttpDelete request = new HttpDelete(this.urlOpenViduServer + API_SESSIONS + "/" + this.sessionId); + HttpDelete request = new HttpDelete(this.urlOpenViduServer + OpenVidu.API_SESSIONS + "/" + this.sessionId); request.setHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded"); HttpResponse response; @@ -147,14 +166,236 @@ public class Session { throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); } - int statusCode = response.getStatusLine().getStatusCode(); - if ((statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) { - System.out.println("Session closed"); - } else { - throw new OpenViduHttpException(statusCode); + try { + int statusCode = response.getStatusLine().getStatusCode(); + if ((statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) { + OpenVidu.activeSessions.remove(this.sessionId); + log.info("Session {} closed", this.sessionId); + } else { + throw new OpenViduHttpException(statusCode); + } + } finally { + EntityUtils.consumeQuietly(response.getEntity()); } } + /** + * Updates every property of the Session with the current status it has in + * OpenVidu Server. This is especially useful for getting the list of active + * connections to the Session + * ({@link io.openvidu.java.client.Session#getActiveConnections()}) and use + * those values to call + * {@link io.openvidu.java.client.Session#forceDisconnect(String)} or + * {@link io.openvidu.java.client.Session#forceUnpublish(String)} + * + * @return true if the Session status has changed with respect to the server, + * false if not. This applies to any property or sub-property of the + * object + * + * @throws OpenViduHttpException + * @throws OpenViduJavaClientException + */ + public boolean fetch() throws OpenViduJavaClientException, OpenViduHttpException { + String beforeJSON = this.toJson(); + HttpGet request = new HttpGet(this.urlOpenViduServer + OpenVidu.API_SESSIONS + "/" + this.sessionId); + request.setHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded"); + + HttpResponse response; + try { + response = httpClient.execute(request); + } catch (IOException e) { + throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); + } + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { + this.resetSessionWithJson(httpResponseToJson(response)); + String afterJSON = this.toJson(); + boolean hasChanged = !beforeJSON.equals(afterJSON); + log.info("Session info fetched for session '{}'. Any change: {}", this.sessionId, hasChanged); + return hasChanged; + } else { + throw new OpenViduHttpException(statusCode); + } + } finally { + EntityUtils.consumeQuietly(response.getEntity()); + } + } + + /** + * Forces the user represented by connection to leave the session. + * OpenVidu Browser will trigger the proper events on the client-side + * (streamDestroyed, connectionDestroyed, + * sessionDisconnected) with reason set to + * "forceDisconnectByServer"
+ * + * You can get connection parameter with + * {@link io.openvidu.java.client.Session#fetch()} and then + * {@link io.openvidu.java.client.Session#getActiveConnections()} + * + * @throws OpenViduJavaClientException + * @throws OpenViduHttpException + */ + public void forceDisconnect(Connection connection) throws OpenViduJavaClientException, OpenViduHttpException { + this.forceDisconnect(connection.getConnectionId()); + } + + /** + * Forces the user with Connection connectionId to leave the + * session. OpenVidu Browser will trigger the proper events on the client-side + * (streamDestroyed, connectionDestroyed, + * sessionDisconnected) with reason set to + * "forceDisconnectByServer"
+ * + * You can get connectionId parameter with + * {@link io.openvidu.java.client.Session#fetch()} (use + * {@link io.openvidu.java.client.Connection#getConnectionId()} to get the + * `connectionId` you want) + * + * @throws OpenViduJavaClientException + * @throws OpenViduHttpException + */ + public void forceDisconnect(String connectionId) throws OpenViduJavaClientException, OpenViduHttpException { + HttpDelete request = new HttpDelete( + this.urlOpenViduServer + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/connection/" + connectionId); + request.setHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded"); + + HttpResponse response = null; + try { + response = httpClient.execute(request); + } catch (IOException e) { + throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); + } + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if ((statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) { + // Remove connection from activeConnections map + Connection connectionClosed = this.activeConnections.remove(connectionId); + // Remove every Publisher of the closed connection from every subscriber list of + // other connections + if (connectionClosed != null) { + for (Publisher publisher : connectionClosed.getPublishers()) { + this.removeSubscribersForPublisher(publisher); + } + } else { + log.warn( + "The closed connection wasn't fetched in OpenVidu Java Client. No changes in the collection of active connections of the Session"); + } + log.info("Connection {} closed", connectionId); + } else { + throw new OpenViduHttpException(statusCode); + } + } finally + + { + EntityUtils.consumeQuietly(response.getEntity()); + } + } + + /** + * Forces some user to unpublish a Stream. OpenVidu Browser will trigger the + * proper events on the client-side (streamDestroyed) with reason + * set to "forceUnpublishByServer".
+ * + * You can get publisher parameter with + * {@link io.openvidu.java.client.Session#getActiveConnections()} and then for + * each Connection you can call + * {@link io.openvidu.java.client.Connection#getPublishers()}. Remember to call + * {@link io.openvidu.java.client.Session#fetch()} before to fetch the current + * actual properties of the Session from OpenVidu Server + * + * @throws OpenViduJavaClientException + * @throws OpenViduHttpException + */ + public void forceUnpublish(Publisher publisher) throws OpenViduJavaClientException, OpenViduHttpException { + this.forceUnpublish(publisher.getStreamId()); + } + + /** + * Forces some user to unpublish a Stream. OpenVidu Browser will trigger the + * proper events on the client-side (streamDestroyed) with reason + * set to "forceUnpublishByServer".
+ * + * You can get streamId parameter with + * {@link io.openvidu.java.client.Session#getActiveConnections()} and then for + * each Connection you can call + * {@link io.openvidu.java.client.Connection#getPublishers()}. Finally + * {@link io.openvidu.java.client.Publisher#getStreamId()}) will give you the + * streamId. Remember to call + * {@link io.openvidu.java.client.Session#fetch()} before to fetch the current + * actual properties of the Session from OpenVidu Server + * + * @throws OpenViduJavaClientException + * @throws OpenViduHttpException + */ + public void forceUnpublish(String streamId) throws OpenViduJavaClientException, OpenViduHttpException { + HttpDelete request = new HttpDelete( + this.urlOpenViduServer + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/stream/" + streamId); + request.setHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded"); + + HttpResponse response; + try { + response = httpClient.execute(request); + } catch (IOException e) { + throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); + } + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if ((statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) { + for (Connection connection : this.activeConnections.values()) { + // Try to remove the Publisher from the Connection publishers collection + if (connection.publishers.remove(streamId) != null) { + continue; + } + // Try to remove the Publisher from the Connection subscribers collection + connection.subscribers.remove(streamId); + } + log.info("Stream {} unpublished", streamId); + } else { + throw new OpenViduHttpException(statusCode); + } + } finally { + EntityUtils.consumeQuietly(response.getEntity()); + } + } + + /** + * Returns the list of active connections to the session. This value + * will remain unchanged since the last time method + * {@link io.openvidu.java.client.Session#fetch()} was called. + * Exceptions to this rule are: + *
    + *
  • Calling {@link io.openvidu.java.client.Session#forceUnpublish(String)} + * updates each affected Connection status
  • + *
  • Calling {@link io.openvidu.java.client.Session#forceDisconnect(String)} + * updates each affected Connection status
  • + *
+ *
+ * To get the list of active connections with their current actual value, you + * must call first {@link io.openvidu.java.client.Session#fetch()} and then + * {@link io.openvidu.java.client.Session#getActiveConnections()} + */ + public List getActiveConnections() { + return new ArrayList<>(this.activeConnections.values()); + } + + /** + * Returns whether the session is being recorded or not. This value will + * be the same as the one that returned method + * {@link io.openvidu.java.client.Session#fetch()} the last time it was + * called. + * + * To get the current actual value, you must call first + * {@link io.openvidu.java.client.Session#fetch()} and then + * {@link io.openvidu.java.client.Session#isBeingRecorded()} + */ + public boolean isBeingRecorded() { + return this.recording; + } + /** * Returns the properties defining the session */ @@ -177,7 +418,7 @@ public class Session { return; } - HttpPost request = new HttpPost(this.urlOpenViduServer + API_SESSIONS); + HttpPost request = new HttpPost(this.urlOpenViduServer + OpenVidu.API_SESSIONS); JSONObject json = new JSONObject(); json.put("mediaMode", properties.mediaMode().name()); @@ -201,16 +442,20 @@ public class Session { } catch (IOException e2) { throw new OpenViduJavaClientException(e2.getMessage(), e2.getCause()); } - int statusCode = response.getStatusLine().getStatusCode(); - if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { - System.out.println("Returning a SESSIONID"); - String id = (String) httpResponseToJson(response).get("id"); - this.sessionId = id; - } else if (statusCode == org.apache.http.HttpStatus.SC_CONFLICT) { - // 'customSessionId' already existed - this.sessionId = properties.customSessionId(); - } else { - throw new OpenViduHttpException(statusCode); + try { + int statusCode = response.getStatusLine().getStatusCode(); + if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { + String id = (String) httpResponseToJson(response).get("id"); + log.info("Returning a SESSIONID: {}", id); + this.sessionId = id; + } else if (statusCode == org.apache.http.HttpStatus.SC_CONFLICT) { + // 'customSessionId' already existed + this.sessionId = properties.customSessionId(); + } else { + throw new OpenViduHttpException(statusCode); + } + } finally { + EntityUtils.consumeQuietly(response.getEntity()); } } @@ -225,4 +470,95 @@ public class Session { return json; } + private void removeSubscribersForPublisher(Publisher publisher) { + String streamId = publisher.getStreamId(); + for (Connection connection : this.activeConnections.values()) { + connection.setSubscribers(connection.getSubscribers().stream() + .filter(subscriber -> !streamId.equals(subscriber)).collect(Collectors.toList())); + } + } + + @SuppressWarnings("unchecked") + protected Session resetSessionWithJson(JSONObject json) { + this.sessionId = (String) json.get("sessionId"); + this.recording = (boolean) json.get("recording"); + SessionProperties.Builder builder = new SessionProperties.Builder() + .mediaMode(MediaMode.valueOf((String) json.get("mediaMode"))) + .recordingMode(RecordingMode.valueOf((String) json.get("recordingMode"))) + .defaultRecordingLayout(RecordingLayout.valueOf((String) json.get("defaultRecordingLayout"))); + if (json.containsKey("defaultCustomLayout")) { + builder.defaultCustomLayout((String) json.get("defaultCustomLayout")); + } + if (this.properties != null && this.properties.customSessionId() != null) { + builder.customSessionId(this.properties.customSessionId()); + } + this.properties = builder.build(); + JSONArray jsonArrayConnections = (JSONArray) ((JSONObject) json.get("connections")).get("content"); + this.activeConnections.clear(); + jsonArrayConnections.forEach(connection -> { + JSONObject con = (JSONObject) connection; + + Map publishers = new ConcurrentHashMap<>(); + JSONArray jsonArrayPublishers = (JSONArray) con.get("publishers"); + jsonArrayPublishers.forEach(publisher -> { + JSONObject pubJson = (JSONObject) publisher; + JSONObject mediaOptions = (JSONObject) pubJson.get("mediaOptions"); + Publisher pub = new Publisher((String) pubJson.get("streamId"), (boolean) mediaOptions.get("hasAudio"), + (boolean) mediaOptions.get("hasVideo"), mediaOptions.get("audioActive"), + mediaOptions.get("videoActive"), mediaOptions.get("frameRate"), mediaOptions.get("typeOfVideo"), + mediaOptions.get("videoDimensions")); + publishers.put(pub.getStreamId(), pub); + }); + + List subscribers = new ArrayList<>(); + JSONArray jsonArraySubscribers = (JSONArray) con.get("subscribers"); + jsonArraySubscribers.forEach(subscriber -> { + subscribers.add((String) ((JSONObject) subscriber).get("streamId")); + }); + + this.activeConnections.put((String) con.get("connectionId"), + new Connection((String) con.get("connectionId"), OpenViduRole.valueOf((String) con.get("role")), + (String) con.get("token"), (String) con.get("serverData"), (String) con.get("clientData"), + publishers, subscribers)); + }); + return this; + } + + @SuppressWarnings("unchecked") + protected String toJson() { + JSONObject json = new JSONObject(); + json.put("sessionId", this.sessionId); + json.put("customSessionId", this.properties.customSessionId()); + json.put("recording", this.recording); + json.put("mediaMode", this.properties.mediaMode()); + json.put("recordingMode", this.properties.recordingMode()); + json.put("defaultRecordingLayout", this.properties.defaultRecordingLayout()); + json.put("defaultCustomLayout", this.properties.defaultCustomLayout()); + JSONObject connections = new JSONObject(); + connections.put("numberOfElements", this.getActiveConnections().size()); + JSONArray jsonArrayConnections = new JSONArray(); + this.getActiveConnections().forEach(con -> { + JSONObject c = new JSONObject(); + c.put("connectionId", con.getConnectionId()); + c.put("role", con.getRole()); + c.put("token", con.getToken()); + c.put("clientData", con.getClientData()); + c.put("serverData", con.getServerData()); + JSONArray pubs = new JSONArray(); + con.getPublishers().forEach(p -> { + pubs.add(p.toJson()); + }); + JSONArray subs = new JSONArray(); + con.getSubscribers().forEach(s -> { + subs.add(s); + }); + c.put("publishers", pubs); + c.put("subscribers", subs); + jsonArrayConnections.add(c); + }); + connections.put("content", jsonArrayConnections); + json.put("connections", connections); + return json.toJSONString(); + } + } diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java index 844d9d6e..89e97586 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java @@ -17,6 +17,9 @@ package io.openvidu.java.client; +/** + * See {@link io.openvidu.java.client.OpenVidu#createSession(SessionProperties)} + */ public class SessionProperties { private MediaMode mediaMode; diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/TokenOptions.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/TokenOptions.java index 4d416426..2e09e9d7 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/TokenOptions.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/TokenOptions.java @@ -17,6 +17,9 @@ package io.openvidu.java.client; +/** + * See {@link io.openvidu.java.client.Session#generateToken(TokenOptions)} + */ public class TokenOptions { private String data; diff --git a/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.ts b/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.ts index 24eba286..5a56acc3 100644 --- a/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.ts +++ b/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.ts @@ -358,8 +358,10 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { if (this.sessionEvents.sessionDisconnected) { this.session.on('sessionDisconnected', (event: SessionDisconnectedEvent) => { this.updateEventList('sessionDisconnected', 'No data'); - this.session = null; - this.OV = null; + this.subscribers = []; + delete this.publisher; + delete this.session; + delete this.OV; }); } }