Java tutorial
/* * (C) Copyright 2017-2019 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.io.IOException; import java.io.UnsupportedEncodingException; import java.security.KeyManagementException; import java.security.KeyStoreException; 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; import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; 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.HttpClientBuilder; import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.ssl.TrustStrategy; 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 OpenVidu { private static final Logger log = LoggerFactory.getLogger(OpenVidu.class); private String secret; protected static String urlOpenViduServer; protected static HttpClient httpClient; protected static Map<String, Session> activeSessions = new ConcurrentHashMap<>(); 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 Public accessible IP where your instance of OpenVidu * Server is up an running * @param secret Secret used on OpenVidu Server initialization */ public OpenVidu(String urlOpenViduServer, String secret) { OpenVidu.urlOpenViduServer = urlOpenViduServer; if (!OpenVidu.urlOpenViduServer.endsWith("/")) { OpenVidu.urlOpenViduServer += "/"; } this.secret = secret; TrustStrategy trustStrategy = new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }; CredentialsProvider provider = new BasicCredentialsProvider(); UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("OPENVIDUAPP", this.secret); provider.setCredentials(AuthScope.ANY, credentials); SSLContext sslContext; try { sslContext = new SSLContextBuilder().loadTrustMaterial(null, trustStrategy).build(); } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) { throw new RuntimeException(e); } RequestConfig.Builder requestBuilder = RequestConfig.custom(); requestBuilder = requestBuilder.setConnectTimeout(30000); requestBuilder = requestBuilder.setConnectionRequestTimeout(30000); OpenVidu.httpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestBuilder.build()) .setConnectionTimeToLive(30, TimeUnit.SECONDS).setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) .setSSLContext(sslContext).setDefaultCredentialsProvider(provider).build(); } /** * Creates an OpenVidu session with the default settings * * @return The created session * * @throws OpenViduJavaClientException * @throws OpenViduHttpException */ public Session createSession() throws OpenViduJavaClientException, OpenViduHttpException { Session s = new Session(); OpenVidu.activeSessions.put(s.getSessionId(), s); return s; } /** * Creates an OpenVidu session * * @param properties The specific configuration for this session * * @return The created session * * @throws OpenViduJavaClientException * @throws OpenViduHttpException Value returned from * {@link io.openvidu.java.client.OpenViduHttpException#getStatus()} * <ul> * <li><code>409</code>: you are trying to * assign an already-in-use custom sessionId * to the session. See * {@link io.openvidu.java.client.SessionProperties#customSessionId()}</li> * </ul> */ public Session createSession(SessionProperties properties) throws OpenViduJavaClientException, OpenViduHttpException { Session s = new Session(properties); OpenVidu.activeSessions.put(s.getSessionId(), s); return s; } /** * Starts the recording of a {@link io.openvidu.java.client.Session} * * @param sessionId The sessionId of the session you want to start recording * @param properties The configuration for this recording * * @return The new created session * * @throws OpenViduJavaClientException * @throws OpenViduHttpException Value returned from * {@link io.openvidu.java.client.OpenViduHttpException#getStatus()} * <ul> * <li><code>404</code>: no session exists * for the passed <i>sessionId</i></li> * <li><code>406</code>: the session has no * connected participants</li> * <li><code>422</code>: "resolution" * parameter exceeds acceptable values (for * both width and height, min 100px and max * 1999px) or trying to start a recording * with both "hasAudio" and "hasVideo" to * false</li> * <li><code>409</code>: the session is not * configured for using * {@link io.openvidu.java.client.MediaMode#ROUTED} * or it is already being recorded</li> * <li><code>501</code>: OpenVidu Server * recording module is disabled * (<i>openvidu.recording</i> property set * to <i>false</i>)</li> * </ul> */ @SuppressWarnings("unchecked") public Recording startRecording(String sessionId, RecordingProperties properties) throws OpenViduJavaClientException, OpenViduHttpException { HttpPost request = new HttpPost(OpenVidu.urlOpenViduServer + API_RECORDINGS + API_RECORDINGS_START); JSONObject json = new JSONObject(); json.put("session", sessionId); json.put("name", properties.name()); json.put("outputMode", properties.outputMode().name()); json.put("hasAudio", properties.hasAudio()); json.put("hasVideo", properties.hasVideo()); if (Recording.OutputMode.COMPOSED.equals(properties.outputMode()) && properties.hasVideo()) { json.put("resolution", properties.resolution()); json.put("recordingLayout", (properties.recordingLayout() != null) ? properties.recordingLayout().name() : ""); if (RecordingLayout.CUSTOM.equals(properties.recordingLayout())) { json.put("customLayout", (properties.customLayout() != null) ? properties.customLayout() : ""); } } StringEntity params = null; try { params = new StringEntity(json.toString()); } catch (UnsupportedEncodingException e1) { throw new OpenViduJavaClientException(e1.getMessage(), e1.getCause()); } request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json"); request.setEntity(params); HttpResponse response; try { response = OpenVidu.httpClient.execute(request); } catch (IOException e2) { throw new OpenViduJavaClientException(e2.getMessage(), e2.getCause()); } try { int statusCode = response.getStatusLine().getStatusCode(); if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { Recording r = new Recording(httpResponseToJson(response)); Session activeSession = OpenVidu.activeSessions.get(r.getSessionId()); if (activeSession != null) { activeSession.setIsBeingRecorded(true); } else { log.warn("No active session found for sessionId '" + r.getSessionId() + "'. This instance of OpenVidu Java Client didn't create this session"); } return r; } else { throw new OpenViduHttpException(statusCode); } } finally { EntityUtils.consumeQuietly(response.getEntity()); } } /** * Starts the recording of a {@link io.openvidu.java.client.Session} * * @param sessionId The sessionId of the session you want to start recording * @param name The name you want to give to the video file. You can access * this same value in your clients on recording events * (recordingStarted, recordingStopped). <strong>WARNING: this * parameter follows an overwriting policy.</strong> If you * name two recordings the same, the newest MP4 file will * overwrite the oldest one * * @return The started recording. If this method successfully returns the * Recording object it means that the recording can be stopped with * guarantees * * @throws OpenViduJavaClientException * @throws OpenViduHttpException Value returned from * {@link io.openvidu.java.client.OpenViduHttpException#getStatus()} * <ul> * <li><code>404</code>: no session exists * for the passed <i>sessionId</i></li> * <li><code>406</code>: the session has no * connected participants</li> * <li><code>422</code>: "resolution" * parameter exceeds acceptable values (for * both width and height, min 100px and max * 1999px) or trying to start a recording * with both "hasAudio" and "hasVideo" to * false</li> * <li><code>409</code>: the session is not * configured for using * {@link io.openvidu.java.client.MediaMode#ROUTED} * or it is already being recorded</li> * <li><code>501</code>: OpenVidu Server * recording module is disabled * (<i>openvidu.recording</i> property set * to <i>false</i>)</li> * </ul> */ public Recording startRecording(String sessionId, String name) throws OpenViduJavaClientException, OpenViduHttpException { if (name == null) { name = ""; } return this.startRecording(sessionId, new RecordingProperties.Builder().name(name).build()); } /** * Starts the recording of a {@link io.openvidu.java.client.Session} * * @param sessionId The sessionId of the session you want to start recording * * @return The started recording. If this method successfully returns the * Recording object it means that the recording can be stopped with * guarantees * * @throws OpenViduJavaClientException * @throws OpenViduHttpException Value returned from * {@link io.openvidu.java.client.OpenViduHttpException#getStatus()} * <ul> * <li><code>404</code>: no session exists * for the passed <i>sessionId</i></li> * <li><code>406</code>: the session has no * connected participants</li> * <li><code>422</code>: "resolution" * parameter exceeds acceptable values (for * both width and height, min 100px and max * 1999px) or trying to start a recording * with both "hasAudio" and "hasVideo" to * false</li> * <li><code>409</code>: the session is not * configured for using * {@link io.openvidu.java.client.MediaMode#ROUTED} * or it is already being recorded</li> * <li><code>501</code>: OpenVidu Server * recording module is disabled * (<i>openvidu.recording</i> property set * to <i>false</i>)</li> * </ul> */ public Recording startRecording(String sessionId) throws OpenViduJavaClientException, OpenViduHttpException { return this.startRecording(sessionId, ""); } /** * Stops the recording of a {@link io.openvidu.java.client.Session} * * @param recordingId The id property of the recording you want to stop * * @return The stopped recording * * @throws OpenViduJavaClientException * @throws OpenViduHttpException Value returned from * {@link io.openvidu.java.client.OpenViduHttpException#getStatus()} * <ul> * <li><code>404</code>: no recording exists * for the passed <i>recordingId</i></li> * <li><code>406</code>: recording has * <i>starting</i> status. Wait until * <i>started</i> status before stopping the * recording</li> * </ul> */ public Recording stopRecording(String recordingId) throws OpenViduJavaClientException, OpenViduHttpException { HttpPost request = new HttpPost( OpenVidu.urlOpenViduServer + API_RECORDINGS + API_RECORDINGS_STOP + "/" + recordingId); HttpResponse response; try { response = OpenVidu.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)) { Recording r = new Recording(httpResponseToJson(response)); Session activeSession = OpenVidu.activeSessions.get(r.getSessionId()); if (activeSession != null) { activeSession.setIsBeingRecorded(false); } else { log.warn("No active session found for sessionId '" + r.getSessionId() + "'. This instance of OpenVidu Java Client didn't create this session"); } return r; } else { throw new OpenViduHttpException(statusCode); } } finally { EntityUtils.consumeQuietly(response.getEntity()); } } /** * Gets an existing recording * * @param recordingId The id property of the recording you want to retrieve * * @throws OpenViduJavaClientException * @throws OpenViduHttpException Value returned from * {@link io.openvidu.java.client.OpenViduHttpException#getStatus()} * <ul> * <li><code>404</code>: no recording exists * for the passed <i>recordingId</i></li> * </ul> */ public Recording getRecording(String recordingId) throws OpenViduJavaClientException, OpenViduHttpException { HttpGet request = new HttpGet(OpenVidu.urlOpenViduServer + API_RECORDINGS + "/" + recordingId); HttpResponse response; try { response = OpenVidu.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)) { return new Recording(httpResponseToJson(response)); } else { throw new OpenViduHttpException(statusCode); } } finally { EntityUtils.consumeQuietly(response.getEntity()); } } /** * Lists all existing recordings * * @return A {@link java.util.List} with all existing recordings * * @throws OpenViduJavaClientException * @throws OpenViduHttpException */ @SuppressWarnings("unchecked") public List<Recording> listRecordings() throws OpenViduJavaClientException, OpenViduHttpException { HttpGet request = new HttpGet(OpenVidu.urlOpenViduServer + API_RECORDINGS); HttpResponse response; try { response = OpenVidu.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)) { List<Recording> 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()); } } /** * Deletes a recording. The recording must have status * {@link io.openvidu.java.client.Recording.Status#stopped} or * {@link io.openvidu.java.client.Recording.Status#available} * * @param recordingId The id property of the recording you want to delete * * @throws OpenViduJavaClientException * @throws OpenViduHttpException Value returned from * {@link io.openvidu.java.client.OpenViduHttpException#getStatus()} * <ul> * <li><code>404</code>: no recording exists * for the passed <i>recordingId</i></li> * <li><code>409</code>: the recording has * <i>started</i> status. Stop it before * deletion</li> * </ul> */ public void deleteRecording(String recordingId) throws OpenViduJavaClientException, OpenViduHttpException { HttpDelete request = new HttpDelete(OpenVidu.urlOpenViduServer + API_RECORDINGS + "/" + recordingId); HttpResponse response; try { response = OpenVidu.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)) { throw new OpenViduHttpException(statusCode); } } finally { EntityUtils.consumeQuietly(response.getEntity()); } } /** * Returns the list of active sessions. <strong>This value will remain unchanged * since the last time method {@link io.openvidu.java.client.OpenVidu#fetch()} * was called</strong>. Exceptions to this rule are: * <ul> * <li>Calling {@link io.openvidu.java.client.Session#fetch()} updates that * specific Session status</li> * <li>Calling {@link io.openvidu.java.client.Session#close()} automatically * removes the Session from the list of active Sessions</li> * <li>Calling * {@link io.openvidu.java.client.Session#forceDisconnect(Connection)} * automatically updates the inner affected connections for that specific * Session</li> * <li>Calling {@link io.openvidu.java.client.Session#forceUnpublish(Publisher)} * also automatically updates the inner affected connections for that specific * Session</li> * <li>Calling {@link io.openvidu.java.client.OpenVidu#startRecording(String)} * and {@link io.openvidu.java.client.OpenVidu#stopRecording(String)} * automatically updates the recording status of the Session * ({@link io.openvidu.java.client.Session#isBeingRecorded()})</li> * </ul> * <br> * 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<Session> 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 access the updated * list of active sessions by calling * {@link io.openvidu.java.client.OpenVidu#getActiveSessions()} * * @return true if any Session status has changed with respect to the server, * false if not. This applies to any property or sub-property of any of * the sessions locally stored in OpenVidu Java Client * * @throws OpenViduHttpException * @throws OpenViduJavaClientException */ @SuppressWarnings("unchecked") public boolean fetch() throws OpenViduJavaClientException, OpenViduHttpException { HttpGet request = new HttpGet(OpenVidu.urlOpenViduServer + API_SESSIONS); HttpResponse response; try { response = OpenVidu.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)) { JSONObject jsonSessions = httpResponseToJson(response); JSONArray jsonArraySessions = (JSONArray) jsonSessions.get("content"); // Set to store fetched sessionIds and later remove closed sessions Set<String> fetchedSessionIds = new HashSet<>(); // Boolean to store if any Session has changed final 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(); boolean changed = !beforeJSON.equals(afterJSON); hasChanged[0] = hasChanged[0] || changed; log.info("Available session '{}' info fetched. Any change: {}", sessionId, changed); return s; }); OpenVidu.activeSessions.computeIfAbsent(sessionId, sId -> { log.info("New session '{}' fetched", sessionId); hasChanged[0] = true; 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()); hasChanged[0] = true; return false; } }).collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())); log.info("Active sessions info fetched: {}", OpenVidu.activeSessions.keySet()); return hasChanged[0]; } else { throw new OpenViduHttpException(statusCode); } } finally { EntityUtils.consumeQuietly(response.getEntity()); } } private JSONObject httpResponseToJson(HttpResponse response) throws OpenViduJavaClientException { JSONParser parser = new JSONParser(); JSONObject json; try { json = (JSONObject) parser.parse(EntityUtils.toString(response.getEntity())); } catch (org.apache.http.ParseException | ParseException | IOException e) { throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); } return json; } }