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;
});
}
}