Merge branch 'master' of github.com:OpenVidu/openvidu

pull/431/head
OscarSotoSanchez 2020-04-09 16:35:06 +02:00
commit 0a36927836
8 changed files with 248 additions and 124 deletions

View File

@ -243,7 +243,7 @@ public class OpenViduServer implements JsonRpcConfigurer {
for (Error error : config.getConfigErrors()) { for (Error error : config.getConfigErrors()) {
msg += " * "; msg += " * ";
if(error.getProperty() != null) { if (error.getProperty() != null) {
msg += "Property " + config.getPropertyName(error.getProperty()); msg += "Property " + config.getPropertyName(error.getProperty());
if (error.getValue() == null || error.getValue().equals("")) { if (error.getValue() == null || error.getValue().equals("")) {
msg += " is not set. "; msg += " is not set. ";
@ -268,15 +268,15 @@ public class OpenViduServer implements JsonRpcConfigurer {
} else { } else {
String msg = "\n\n\n" + " Configuration properties\n" + " ----------------------\n" + "\n"; String msg = "\n\n\n" + " Configuration properties\n" + " ------------------------\n" + "\n";
Map<String, String> configProps = config.getConfigProps(); Map<String, String> configProps = config.getConfigProps();
List<String> configPropNames = new ArrayList<>(config.getUserProperties()); List<String> configPropNames = new ArrayList<>(config.getUserProperties());
Collections.sort(configPropNames); Collections.sort(configPropNames);
for(String property : configPropNames) { for (String property : configPropNames) {
String value = configProps.get(property); String value = configProps.get(property);
msg += " * "+config.getPropertyName(property)+"="+(value == null? "": value)+"\n"; msg += " * " + config.getPropertyName(property) + "=" + (value == null ? "" : value) + "\n";
} }
msg += "\n\n"; msg += "\n\n";
@ -290,10 +290,10 @@ public class OpenViduServer implements JsonRpcConfigurer {
String dashboardUrl = httpUrl + "dashboard/"; String dashboardUrl = httpUrl + "dashboard/";
// @formatter:off // @formatter:off
String msg = "\n\n----------------------------------------------------\n" + "\n" String msg = "\n\n----------------------------------------------------\n" + "\n" + " OpenVidu is ready!\n"
+ " OpenVidu Platform is ready!\n" + " ---------------------------\n" + "\n" + " ---------------------------\n" + "\n" + " * OpenVidu Server: " + httpUrl + "\n" + "\n"
+ " * OpenVidu Server: " + httpUrl + "\n" + "\n" + " * OpenVidu Dashboard: " + dashboardUrl + "\n" + " * OpenVidu Dashboard: " + dashboardUrl + "\n" + "\n"
+ "\n" + "----------------------------------------------------\n"; + "----------------------------------------------------\n";
// @formatter:on // @formatter:on
log.info(msg); log.info(msg);

View File

@ -81,6 +81,11 @@ public class OpenviduConfig {
public String getMessage() { public String getMessage() {
return message; return message;
} }
@Override
public String toString() {
return "Error [property=" + property + ", value=" + value + ", message=" + message + "]";
}
} }
private static final Logger log = LoggerFactory.getLogger(OpenviduConfig.class); private static final Logger log = LoggerFactory.getLogger(OpenviduConfig.class);
@ -95,6 +100,8 @@ public class OpenviduConfig {
private List<String> userConfigProps; private List<String> userConfigProps;
private Map<String, ?> propertiesSource;
@Autowired @Autowired
protected Environment env; protected Environment env;
@ -333,6 +340,17 @@ public class OpenviduConfig {
// Properties management methods // Properties management methods
public OpenviduConfig deriveWithAdditionalPropertiesSource(Map<String, ?> propertiesSource) {
OpenviduConfig config = newOpenviduConfig();
config.propertiesSource = propertiesSource;
config.env = env;
return config;
}
protected OpenviduConfig newOpenviduConfig() {
return new OpenviduConfig();
}
public List<Error> getConfigErrors() { public List<Error> getConfigErrors() {
return configErrors; return configErrors;
} }
@ -345,9 +363,25 @@ public class OpenviduConfig {
return userConfigProps; return userConfigProps;
} }
public String getConfigValue(String property) { private String getValue(String property) {
String value = env.getProperty(property); String value = null;
if (propertiesSource != null) {
Object valueObj = propertiesSource.get(property);
if (valueObj != null) {
if(valueObj instanceof Iterable) {
value = JsonUtils.toJson(valueObj);
} else {
value = valueObj.toString();
}
}
}
if (value == null) {
value = env.getProperty(property);
}
this.configProps.put(property, value); this.configProps.put(property, value);
@ -367,27 +401,27 @@ public class OpenviduConfig {
String value = null; String value = null;
if (property != null) { if (property != null) {
value = getConfigValue(property); value = getValue(property);
} }
this.configErrors.add(new Error(property, value, msg)); this.configErrors.add(new Error(property, value, msg));
} }
@PostConstruct @PostConstruct
protected void checkConfigurationProperties() { public void checkConfiguration() {
dotenv = new Dotenv(); dotenv = new Dotenv();
try { try {
dotenv.read(); dotenv.read();
} catch (IOException e) { } catch (IOException e) {
log.warn("Exception reading .env file. "+ e.getClass()+":"+e.getMessage()); log.warn("Exception reading .env file. " + e.getClass() + ":" + e.getMessage());
} catch (DotenvFormatException e) { } catch (DotenvFormatException e) {
log.warn("Format error in .env file. "+ e.getClass()+":"+e.getMessage()); log.warn("Format error in .env file. " + e.getClass() + ":" + e.getMessage());
} }
try { try {
this.checkConfigurationParameters(); this.checkConfigurationProperties();
} catch (Exception e) { } catch (Exception e) {
log.error("Exception checking configuration", e); log.error("Exception checking configuration", e);
addError(null, "Exception checking configuration." + e.getClass().getName() + ":" + e.getMessage()); addError(null, "Exception checking configuration." + e.getClass().getName() + ":" + e.getMessage());
@ -405,15 +439,15 @@ public class OpenviduConfig {
// Properties // Properties
protected void checkConfigurationParameters() { protected void checkConfigurationProperties() {
serverPort = getConfigValue("server.port"); serverPort = getValue("server.port");
coturnRedisDbname = getConfigValue("coturn.redis.dbname"); coturnRedisDbname = getValue("coturn.redis.dbname");
coturnRedisPassword = getConfigValue("coturn.redis.password"); coturnRedisPassword = getValue("coturn.redis.password");
coturnRedisConnectTimeout = getConfigValue("coturn.redis.connect-timeout"); coturnRedisConnectTimeout = getValue("coturn.redis.connect-timeout");
openviduSecret = asNonEmptyString("openvidu.secret"); openviduSecret = asNonEmptyString("openvidu.secret");
@ -437,6 +471,9 @@ public class OpenviduConfig {
openviduStreamsVideoMaxSendBandwidth = asNonNegativeInteger("openvidu.streams.video.max-send-bandwidth"); openviduStreamsVideoMaxSendBandwidth = asNonNegativeInteger("openvidu.streams.video.max-send-bandwidth");
openviduStreamsVideoMinSendBandwidth = asNonNegativeInteger("openvidu.streams.video.min-send-bandwidth"); openviduStreamsVideoMinSendBandwidth = asNonNegativeInteger("openvidu.streams.video.min-send-bandwidth");
openviduSessionsGarbageInterval = asNonNegativeInteger("openvidu.sessions.garbage.interval");
openviduSessionsGarbageThreshold = asNonNegativeInteger("openvidu.sessions.garbage.threshold");
kmsUrisList = checkKmsUris(); kmsUrisList = checkKmsUris();
checkCoturnIp(); checkCoturnIp();
@ -447,9 +484,6 @@ public class OpenviduConfig {
checkCertificateType(); checkCertificateType();
openviduSessionsGarbageInterval = asNonNegativeInteger("openvidu.sessions.garbage.interval");
openviduSessionsGarbageThreshold = asNonNegativeInteger("openvidu.sessions.garbage.threshold");
} }
private void checkCertificateType() { private void checkCertificateType() {
@ -465,7 +499,7 @@ public class OpenviduConfig {
} }
private void checkCoturnIp() { private void checkCoturnIp() {
coturnIp = getConfigValue("coturn.ip"); coturnIp = getValue("coturn.ip");
if (coturnIp == null || this.coturnIp.isEmpty()) { if (coturnIp == null || this.coturnIp.isEmpty()) {
try { try {
@ -501,13 +535,13 @@ public class OpenviduConfig {
private void checkOpenviduPublicurl() { private void checkOpenviduPublicurl() {
final String property = "openvidu.domain.or.public.ip"; final String property = "openvidu.domain.or.public.ip";
String domain = getConfigValue(property); String domain = getValue(property);
if (domain != null && !domain.isEmpty()) { if (domain != null && !domain.isEmpty()) {
this.openviduPublicUrl = "https://" + domain; this.openviduPublicUrl = "https://" + domain;
} else { } else {
final String urlProperty = "openvidu.publicurl"; final String urlProperty = "openvidu.publicurl";
String publicurl = getConfigValue(urlProperty); String publicurl = getValue(urlProperty);
if (publicurl == null || publicurl.isEmpty()) { if (publicurl == null || publicurl.isEmpty()) {
addError(property, "Cannot be empty"); addError(property, "Cannot be empty");
} else { } else {
@ -573,7 +607,7 @@ public class OpenviduConfig {
String property = "kms.uris"; String property = "kms.uris";
return asKmsUris(property, getConfigValue(property)); return asKmsUris(property, getValue(property));
} }
@ -646,7 +680,7 @@ public class OpenviduConfig {
// ------------------------------------------------------- // -------------------------------------------------------
protected String asOptionalURL(String property) { protected String asOptionalURL(String property) {
String optionalUrl = getConfigValue(property); String optionalUrl = getValue(property);
try { try {
if (!optionalUrl.isEmpty()) { if (!optionalUrl.isEmpty()) {
checkUrl(optionalUrl); checkUrl(optionalUrl);
@ -659,7 +693,7 @@ public class OpenviduConfig {
} }
protected String asNonEmptyString(String property) { protected String asNonEmptyString(String property) {
String stringValue = getConfigValue(property); String stringValue = getValue(property);
if (stringValue != null && !stringValue.isEmpty()) { if (stringValue != null && !stringValue.isEmpty()) {
return stringValue; return stringValue;
} else { } else {
@ -669,11 +703,11 @@ public class OpenviduConfig {
} }
protected String asOptionalString(String property) { protected String asOptionalString(String property) {
return getConfigValue(property); return getValue(property);
} }
protected boolean asBoolean(String property) { protected boolean asBoolean(String property) {
String value = getConfigValue(property); String value = getValue(property);
if (value == null) { if (value == null) {
addError(property, "Cannot be empty"); addError(property, "Cannot be empty");
return false; return false;
@ -689,7 +723,7 @@ public class OpenviduConfig {
protected Integer asNonNegativeInteger(String property) { protected Integer asNonNegativeInteger(String property) {
try { try {
Integer integerValue = Integer.parseInt(getConfigValue(property)); Integer integerValue = Integer.parseInt(getValue(property));
if (integerValue < 0) { if (integerValue < 0) {
addError(property, "Is not a non negative integer"); addError(property, "Is not a non negative integer");
@ -705,7 +739,7 @@ public class OpenviduConfig {
* This method checks all types of Internet addresses (IPv4, IPv6 and Domains) * This method checks all types of Internet addresses (IPv4, IPv6 and Domains)
*/ */
protected String asOptionalInetAddress(String property) { protected String asOptionalInetAddress(String property) {
String inetAddress = getConfigValue(property); String inetAddress = getValue(property);
if (inetAddress != null && !inetAddress.isEmpty()) { if (inetAddress != null && !inetAddress.isEmpty()) {
try { try {
Inet6Address.getByName(inetAddress).getHostAddress(); Inet6Address.getByName(inetAddress).getHostAddress();
@ -733,7 +767,7 @@ public class OpenviduConfig {
protected List<String> asJsonStringsArray(String property) { protected List<String> asJsonStringsArray(String property) {
try { try {
Gson gson = new Gson(); Gson gson = new Gson();
JsonArray jsonArray = gson.fromJson(getConfigValue(property), JsonArray.class); JsonArray jsonArray = gson.fromJson(getValue(property), JsonArray.class);
List<String> list = JsonUtils.toStringList(jsonArray); List<String> list = JsonUtils.toStringList(jsonArray);
if (list.size() == 1 && list.get(0).isEmpty()) { if (list.size() == 1 && list.get(0).isEmpty()) {
list = new ArrayList<>(); list = new ArrayList<>();
@ -746,7 +780,7 @@ public class OpenviduConfig {
} }
protected <E extends Enum<E>> E asEnumValue(String property, Class<E> enumType) { protected <E extends Enum<E>> E asEnumValue(String property, Class<E> enumType) {
String value = this.getConfigValue(property); String value = this.getValue(property);
try { try {
return Enum.valueOf(enumType, value); return Enum.valueOf(enumType, value);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {

View File

@ -28,6 +28,7 @@ import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -447,16 +448,27 @@ public abstract class SessionManager {
long sessionExistsSince = currentMillis - sessionNotActive.getStartTime(); long sessionExistsSince = currentMillis - sessionNotActive.getStartTime();
if (sessionExistsSince > (openviduConfig.getSessionGarbageThreshold() * 1000)) { if (sessionExistsSince > (openviduConfig.getSessionGarbageThreshold() * 1000)) {
try { try {
sessionNotActive.closingLock.writeLock().lock(); if (sessionNotActive.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
if (sessions.containsKey(sessionId)) { try {
// The session passed to active during lock wait if (sessions.containsKey(sessionId)) {
continue; // The session passed to active during lock wait
continue;
}
iter.remove();
cleanCollections(sessionId);
log.info("Non active session {} cleaned up by garbage collector", sessionId);
} finally {
sessionNotActive.closingLock.writeLock().unlock();
}
} else {
log.error(
"Timeout waiting for Session closing lock to be available for garbage collector to clean session {}",
sessionId);
} }
iter.remove(); } catch (InterruptedException e) {
cleanCollections(sessionId); log.error(
log.info("Non active session {} cleaned up by garbage collector", sessionId); "InterruptedException while waiting for Session closing lock to be available for garbage collector to clean session {}",
} finally { sessionId);
sessionNotActive.closingLock.writeLock().unlock();
} }
} }
} }
@ -515,13 +527,20 @@ public abstract class SessionManager {
// to the session. That is: if the session was in the automatic recording stop // to the session. That is: if the session was in the automatic recording stop
// timeout with INDIVIDUAL recording (no docker participant connected) // timeout with INDIVIDUAL recording (no docker participant connected)
try { try {
session.closingLock.writeLock().lock(); if (session.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
if (session.isClosed()) { try {
return; if (session.isClosed()) {
return;
}
this.closeSessionAndEmptyCollections(session, reason, true);
} finally {
session.closingLock.writeLock().unlock();
}
} else {
log.error("Timeout waiting for Session {} closing lock to be available", sessionId);
} }
this.closeSessionAndEmptyCollections(session, reason, true); } catch (InterruptedException e) {
} finally { log.error("InterruptedException while waiting for Session {} closing lock to be available", sessionId);
session.closingLock.writeLock().unlock();
} }
} }
} }

View File

@ -331,12 +331,18 @@ public class KurentoParticipant extends Participant {
} finally { } finally {
pub.closingLock.writeLock().unlock(); pub.closingLock.writeLock().unlock();
} }
} else {
log.error(
"Timeout waiting for PublisherEndpoint closing lock of participant {} to be available for participant {} to call cancelReceveivingMedia",
senderName, this.getParticipantPublicId());
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
subscribers.remove(senderName);
log.error( log.error(
"Timeout wating for PublisherEndpoint closing lock of participant {} to be available for participant {} to call cancelReceveivingMedia", "InterruptedException while waiting for PublisherEndpoint closing lock of participant {} to be available for participant {} to call cancelReceveivingMedia",
senderName, this.getParticipantPublicId()); senderName, this.getParticipantPublicId());
} finally {
// Always clean map
subscribers.remove(senderName);
} }
} }
} }

View File

@ -86,7 +86,7 @@ public class KurentoSessionManager extends SessionManager {
@Override @Override
/* Protected by Session.closingLock.readLock */ /* Protected by Session.closingLock.readLock */
public synchronized void joinRoom(Participant participant, String sessionId, Integer transactionId) { public void joinRoom(Participant participant, String sessionId, Integer transactionId) {
Set<Participant> existingParticipants = null; Set<Participant> existingParticipants = null;
boolean lockAcquired = false; boolean lockAcquired = false;
try { try {
@ -162,8 +162,7 @@ public class KurentoSessionManager extends SessionManager {
} }
@Override @Override
public synchronized boolean leaveRoom(Participant participant, Integer transactionId, EndReason reason, public boolean leaveRoom(Participant participant, Integer transactionId, EndReason reason, boolean closeWebSocket) {
boolean closeWebSocket) {
log.info("Request [LEAVE_ROOM] for participant {} of session {} with reason {}", log.info("Request [LEAVE_ROOM] for participant {} of session {} with reason {}",
participant.getParticipantPublicId(), participant.getSessionId(), participant.getParticipantPublicId(), participant.getSessionId(),
reason != null ? reason.name() : "NULL"); reason != null ? reason.name() : "NULL");
@ -235,17 +234,27 @@ public class KurentoSessionManager extends SessionManager {
recordingManager.initAutomaticRecordingStopThread(session); recordingManager.initAutomaticRecordingStopThread(session);
} else { } else {
try { try {
session.closingLock.writeLock().lock(); if (session.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
if (session.isClosed()) { try {
return false; if (session.isClosed()) {
return false;
}
log.info("No more participants in session '{}', removing it and closing it", sessionId);
this.closeSessionAndEmptyCollections(session, reason, true);
sessionClosedByLastParticipant = true;
} finally {
session.closingLock.writeLock().unlock();
}
} else {
log.error(
"Timeout waiting for Session {} closing lock to be available for closing as last participant left",
sessionId);
} }
log.info("No more participants in session '{}', removing it and closing it", sessionId); } catch (InterruptedException e) {
this.closeSessionAndEmptyCollections(session, reason, true); log.error(
sessionClosedByLastParticipant = true; "InterruptedException while waiting for Session {} closing lock to be available for closing as last participant left",
} finally { sessionId);
session.closingLock.writeLock().unlock();
} }
} }
} else if (remainingParticipants.size() == 1 && openviduConfig.isRecordingModuleEnabled() } else if (remainingParticipants.size() == 1 && openviduConfig.isRecordingModuleEnabled()
&& MediaMode.ROUTED.equals(session.getSessionProperties().mediaMode()) && MediaMode.ROUTED.equals(session.getSessionProperties().mediaMode())

View File

@ -480,34 +480,46 @@ public class RecordingManager {
recordingId, this.openviduConfig.getOpenviduRecordingAutostopTimeout()); recordingId, this.openviduConfig.getOpenviduRecordingAutostopTimeout());
if (this.automaticRecordingStopThreads.remove(session.getSessionId()) != null) { if (this.automaticRecordingStopThreads.remove(session.getSessionId()) != null) {
boolean alreadyUnlocked = false; boolean alreadyUnlocked = false;
try { try {
session.closingLock.writeLock().lock(); if (session.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
if (session.isClosed()) { try {
return; if (session.isClosed()) {
} return;
}
if (session.getParticipants().size() == 0 || session.onlyRecorderParticipant()) { if (session.getParticipants().size() == 0 || session.onlyRecorderParticipant()) {
// Close session if there are no participants connected (RECORDER does not // Close session if there are no participants connected (RECORDER does not
// count) and publishing // count) and publishing
log.info("Closing session {} after automatic stop of recording {}", session.getSessionId(), log.info("Closing session {} after automatic stop of recording {}",
recordingId); session.getSessionId(), recordingId);
sessionManager.closeSessionAndEmptyCollections(session, EndReason.automaticStop, true); sessionManager.closeSessionAndEmptyCollections(session, EndReason.automaticStop,
true);
} else {
// There are users connected, but no one is publishing
// We don't need the lock if session is not closing
session.closingLock.writeLock().unlock();
alreadyUnlocked = true;
log.info(
"Automatic stopping recording {}. There are users connected to session {}, but no one is publishing",
recordingId, session.getSessionId());
this.stopRecording(session, recordingId, EndReason.automaticStop);
}
} finally {
if (!alreadyUnlocked) {
session.closingLock.writeLock().unlock();
}
}
} else { } else {
// There are users connected, but no one is publishing log.error(
session.closingLock.writeLock().unlock(); // We don't need the lock if session's not closing "Timeout waiting for Session {} closing lock to be available for automatic recording stop thred",
alreadyUnlocked = true; session.getSessionId());
log.info(
"Automatic stopping recording {}. There are users connected to session {}, but no one is publishing",
recordingId, session.getSessionId());
this.stopRecording(session, recordingId, EndReason.automaticStop);
}
} finally {
if (!alreadyUnlocked) {
session.closingLock.writeLock().unlock();
} }
} catch (InterruptedException e) {
log.error(
"InterruptedException while waiting for Session {} closing lock to be available for automatic recording stop thred",
session.getSessionId());
} }
} else { } else {
// This code shouldn't be reachable // This code shouldn't be reachable
log.warn("Recording {} was already automatically stopped by a previous thread", recordingId); log.warn("Recording {} was already automatically stopped by a previous thread", recordingId);
@ -523,23 +535,34 @@ public class RecordingManager {
if (future != null) { if (future != null) {
boolean cancelled = future.cancel(false); boolean cancelled = future.cancel(false);
try { try {
session.closingLock.writeLock().lock(); if (session.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
if (session.isClosed()) { try {
return false; if (session.isClosed()) {
} return false;
if (session.getParticipants().size() == 0 || session.onlyRecorderParticipant()) { }
// Close session if there are no participants connected (except for RECORDER). if (session.getParticipants().size() == 0 || session.onlyRecorderParticipant()) {
// This code will only be executed if recording is manually stopped during the // Close session if there are no participants connected (except for RECORDER).
// automatic stop timeout, so the session must be also closed // This code will only be executed if recording is manually stopped during the
log.info( // automatic stop timeout, so the session must be also closed
"Ongoing recording of session {} was explicetly stopped within timeout for automatic recording stop. Closing session", log.info(
"Ongoing recording of session {} was explicetly stopped within timeout for automatic recording stop. Closing session",
session.getSessionId());
sessionManager.closeSessionAndEmptyCollections(session, reason, false);
}
} finally {
session.closingLock.writeLock().unlock();
}
} else {
log.error(
"Timeout waiting for Session {} closing lock to be available for aborting automatic recording stop thred",
session.getSessionId()); session.getSessionId());
sessionManager.closeSessionAndEmptyCollections(session, reason, false);
} }
return cancelled; } catch (InterruptedException e) {
} finally { log.error(
session.closingLock.writeLock().unlock(); "InterruptedException while waiting for Session {} closing lock to be available for aborting automatic recording stop thred",
session.getSessionId());
} }
return cancelled;
} else { } else {
return true; return true;
} }

View File

@ -21,6 +21,7 @@ import java.net.MalformedURLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
@ -234,15 +235,28 @@ public class SessionRestController {
Session sessionNotActive = this.sessionManager.getSessionNotActive(sessionId); Session sessionNotActive = this.sessionManager.getSessionNotActive(sessionId);
if (sessionNotActive != null) { if (sessionNotActive != null) {
try { try {
sessionNotActive.closingLock.writeLock().lock(); if (sessionNotActive.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
if (sessionNotActive.isClosed()) { try {
return new ResponseEntity<>(HttpStatus.NOT_FOUND); if (sessionNotActive.isClosed()) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
this.sessionManager.closeSessionAndEmptyCollections(sessionNotActive,
EndReason.sessionClosedByServer, true);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} finally {
sessionNotActive.closingLock.writeLock().unlock();
}
} else {
String errorMsg = "Timeout waiting for Session " + sessionId
+ " closing lock to be available for closing from DELETE /api/sessions";
log.error(errorMsg);
return this.generateErrorResponse(errorMsg, "/api/sessions", HttpStatus.BAD_REQUEST);
} }
this.sessionManager.closeSessionAndEmptyCollections(sessionNotActive, EndReason.sessionClosedByServer, } catch (InterruptedException e) {
true); String errorMsg = "InterruptedException while waiting for Session " + sessionId
return new ResponseEntity<>(HttpStatus.NO_CONTENT); + " closing lock to be available for closing from DELETE /api/sessions";
} finally { log.error(errorMsg);
sessionNotActive.closingLock.writeLock().unlock(); return this.generateErrorResponse(errorMsg, "/api/sessions", HttpStatus.BAD_REQUEST);
} }
} else { } else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND); return new ResponseEntity<>(HttpStatus.NOT_FOUND);

View File

@ -22,6 +22,7 @@ import static org.openqa.selenium.OutputType.BASE64;
import java.awt.Color; import java.awt.Color;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
@ -3259,6 +3260,8 @@ public class OpenViduTestAppE2eTest {
recording.getResolution(), realResolution); recording.getResolution(), realResolution);
log.info("Recording map color: {}", colorMap.toString()); log.info("Recording map color: {}", colorMap.toString());
log.info("Recording frame below");
System.out.println(bufferedImageToBase64PngString(image));
isFine = this.checkVideoAverageRgbGreen(colorMap); isFine = this.checkVideoAverageRgbGreen(colorMap);
} catch (IOException | JCodecException e) { } catch (IOException | JCodecException e) {
log.warn("Error getting frame from video recording: {}", e.getMessage()); log.warn("Error getting frame from video recording: {}", e.getMessage());
@ -3267,6 +3270,22 @@ public class OpenViduTestAppE2eTest {
return isFine; return isFine;
} }
private String bufferedImageToBase64PngString(BufferedImage image) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
String imageString = null;
try {
ImageIO.write(image, "png", bos);
byte[] imageBytes = bos.toByteArray();
imageString = "data:image/png;base64," + Base64.getEncoder().encodeToString(imageBytes);
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return imageString;
}
private void checkIndividualRecording(String recPath, Recording recording, int numberOfVideoFiles, private void checkIndividualRecording(String recPath, Recording recording, int numberOfVideoFiles,
String audioDecoder, String videoDecoder, boolean checkAudio) throws IOException { String audioDecoder, String videoDecoder, boolean checkAudio) throws IOException {