mirror of https://github.com/OpenVidu/openvidu.git
openvidu-server: OpenviduConfig refactoring
parent
de2153fe0f
commit
3ed4ae0856
|
@ -18,12 +18,9 @@
|
|||
package io.openvidu.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.kurento.jsonrpc.internal.server.config.JsonRpcConfiguration;
|
||||
import org.kurento.jsonrpc.server.JsonRpcConfigurer;
|
||||
import org.kurento.jsonrpc.server.JsonRpcHandlerRegistry;
|
||||
|
@ -38,8 +35,6 @@ import org.springframework.context.annotation.Bean;
|
|||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.event.EventListener;
|
||||
|
||||
import io.openvidu.client.OpenViduException;
|
||||
import io.openvidu.client.OpenViduException.Code;
|
||||
import io.openvidu.server.cdr.CDRLogger;
|
||||
import io.openvidu.server.cdr.CDRLoggerFile;
|
||||
import io.openvidu.server.cdr.CallDetailRecord;
|
||||
|
@ -81,26 +76,45 @@ public class OpenViduServer implements JsonRpcConfigurer {
|
|||
|
||||
private static final Logger log = LoggerFactory.getLogger(OpenViduServer.class);
|
||||
|
||||
public static final String WS_PATH = "/openvidu";
|
||||
public static String publicurlType;
|
||||
public static String wsUrl;
|
||||
public static String httpUrl;
|
||||
|
||||
@Autowired
|
||||
OpenviduConfig openviduConfig;
|
||||
|
||||
public static final String KMSS_URIS_PROPERTY = "kms.uris";
|
||||
|
||||
public static String wsUrl;
|
||||
|
||||
public static String httpUrl;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public KmsManager kmsManager() {
|
||||
if (openviduConfig.getKmsUris().isEmpty()) {
|
||||
throw new IllegalArgumentException(KMSS_URIS_PROPERTY + " should contain at least one kms url");
|
||||
throw new IllegalArgumentException("'kms.uris' should contain at least one KMS url");
|
||||
}
|
||||
String firstKmsWsUri = openviduConfig.getKmsUris().get(0);
|
||||
log.info("OpenVidu Server using one KMS: {}", firstKmsWsUri);
|
||||
return new FixedOneKmsManager();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public CallDetailRecord cdr() {
|
||||
List<CDRLogger> loggers = new ArrayList<>();
|
||||
if (openviduConfig.isCdrEnabled()) {
|
||||
log.info("OpenVidu CDR is enabled");
|
||||
loggers.add(new CDRLoggerFile());
|
||||
}
|
||||
if (openviduConfig.isWebhookEnabled()) {
|
||||
loggers.add(new CDRLoggerWebhook(openviduConfig));
|
||||
}
|
||||
return new CallDetailRecord(loggers);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public CoturnCredentialsService coturnCredentialsService() {
|
||||
return new CoturnCredentialsServiceFactory().getCoturnCredentialsService(openviduConfig.getSpringProfile());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public LoadManager loadManager() {
|
||||
|
@ -131,20 +145,6 @@ public class OpenViduServer implements JsonRpcConfigurer {
|
|||
return new KurentoSessionEventsHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public CallDetailRecord cdr() {
|
||||
List<CDRLogger> loggers = new ArrayList<>();
|
||||
if (openviduConfig.isCdrEnabled()) {
|
||||
log.info("OpenVidu CDR is enabled");
|
||||
loggers.add(new CDRLoggerFile());
|
||||
}
|
||||
if (openviduConfig.isWebhookEnabled()) {
|
||||
loggers.add(new CDRLoggerWebhook(openviduConfig));
|
||||
}
|
||||
return new CallDetailRecord(loggers);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public KurentoParticipantEndpointConfig kurentoEndpointConfig() {
|
||||
|
@ -169,12 +169,6 @@ public class OpenViduServer implements JsonRpcConfigurer {
|
|||
return new DummyRecordingDownloader();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public CoturnCredentialsService coturnCredentialsService() {
|
||||
return new CoturnCredentialsServiceFactory().getCoturnCredentialsService(openviduConfig.getSpringProfile());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public GeoLocationByIp geoLocationByIp() {
|
||||
|
@ -190,10 +184,10 @@ public class OpenViduServer implements JsonRpcConfigurer {
|
|||
@Override
|
||||
public void registerJsonRpcHandlers(JsonRpcHandlerRegistry registry) {
|
||||
registry.addHandler(rpcHandler().withPingWatchdog(true).withInterceptors(new HttpHandshakeInterceptor()),
|
||||
"/openvidu");
|
||||
WS_PATH);
|
||||
}
|
||||
|
||||
private static String getContainerIp() throws IOException, InterruptedException {
|
||||
public static String getContainerIp() throws IOException, InterruptedException {
|
||||
return CommandExecutor.execCommand("/bin/sh", "-c", "hostname -i | awk '{print $1}'");
|
||||
}
|
||||
|
||||
|
@ -203,86 +197,14 @@ public class OpenViduServer implements JsonRpcConfigurer {
|
|||
SpringApplication.run(OpenViduServer.class, args);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() throws MalformedURLException, InterruptedException {
|
||||
String publicUrl = this.openviduConfig.getOpenViduPublicUrl();
|
||||
String type = publicUrl;
|
||||
|
||||
switch (publicUrl) {
|
||||
case "docker":
|
||||
try {
|
||||
String containerIp = getContainerIp();
|
||||
OpenViduServer.wsUrl = "wss://" + containerIp + ":" + openviduConfig.getServerPort();
|
||||
} catch (Exception e) {
|
||||
log.error("Docker container IP was configured, but there was an error obtaining IP: "
|
||||
+ e.getClass().getName() + " " + e.getMessage());
|
||||
log.error("Fallback to local URL");
|
||||
OpenViduServer.wsUrl = null;
|
||||
}
|
||||
break;
|
||||
|
||||
case "local":
|
||||
break;
|
||||
|
||||
case "":
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
type = "custom";
|
||||
|
||||
if (publicUrl.startsWith("https://")) {
|
||||
OpenViduServer.wsUrl = publicUrl.replace("https://", "wss://");
|
||||
} else if (publicUrl.startsWith("http://")) {
|
||||
OpenViduServer.wsUrl = publicUrl.replace("http://", "wss://");
|
||||
}
|
||||
|
||||
if (!OpenViduServer.wsUrl.startsWith("wss://")) {
|
||||
OpenViduServer.wsUrl = "wss://" + OpenViduServer.wsUrl;
|
||||
}
|
||||
}
|
||||
|
||||
if (OpenViduServer.wsUrl == null) {
|
||||
type = "local";
|
||||
OpenViduServer.wsUrl = "wss://localhost:" + openviduConfig.getServerPort();
|
||||
}
|
||||
|
||||
if (OpenViduServer.wsUrl.endsWith("/")) {
|
||||
OpenViduServer.wsUrl = OpenViduServer.wsUrl.substring(0, OpenViduServer.wsUrl.length() - 1);
|
||||
}
|
||||
|
||||
if (this.openviduConfig.isRecordingModuleEnabled()) {
|
||||
try {
|
||||
this.recordingManager().initializeRecordingManager();
|
||||
} catch (OpenViduException e) {
|
||||
String finalErrorMessage = "";
|
||||
if (e.getCodeValue() == Code.DOCKER_NOT_FOUND.getValue()) {
|
||||
finalErrorMessage = "Error connecting to Docker daemon. Enabling OpenVidu recording module requires Docker";
|
||||
} else if (e.getCodeValue() == Code.RECORDING_PATH_NOT_VALID.getValue()) {
|
||||
finalErrorMessage = "Error initializing recording path \""
|
||||
+ this.openviduConfig.getOpenViduRecordingPath()
|
||||
+ "\" set with system property \"openvidu.recording.path\"";
|
||||
} else if (e.getCodeValue() == Code.RECORDING_FILE_EMPTY_ERROR.getValue()) {
|
||||
finalErrorMessage = "Error initializing recording custom layouts path \""
|
||||
+ this.openviduConfig.getOpenviduRecordingCustomLayout()
|
||||
+ "\" set with system property \"openvidu.recording.custom-layout\"";
|
||||
}
|
||||
log.error(finalErrorMessage + ". Shutting down OpenVidu Server");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
String finalUrl = OpenViduServer.wsUrl.replaceFirst("wss://", "https://").replaceFirst("ws://", "http://");
|
||||
openviduConfig.setFinalUrl(finalUrl);
|
||||
httpUrl = openviduConfig.getFinalUrl();
|
||||
log.info("OpenVidu Server using " + type + " URL: [" + OpenViduServer.wsUrl + "]");
|
||||
}
|
||||
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
public void whenReady() {
|
||||
log.info("OpenVidu Server listening for client websocket connections on"
|
||||
+ (OpenViduServer.publicurlType.isEmpty() ? "" : (" " + OpenViduServer.publicurlType)) + " url "
|
||||
+ OpenViduServer.wsUrl + WS_PATH);
|
||||
final String NEW_LINE = System.lineSeparator();
|
||||
String str = NEW_LINE + NEW_LINE + " ACCESS IP " + NEW_LINE + "-------------------------"
|
||||
+ NEW_LINE + httpUrl + NEW_LINE + "-------------------------" + NEW_LINE;
|
||||
String str = NEW_LINE + NEW_LINE + " OPENVIDU SERVER IP " + NEW_LINE + "--------------------------"
|
||||
+ NEW_LINE + httpUrl + NEW_LINE + "--------------------------" + NEW_LINE;
|
||||
log.info(str);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,12 +19,18 @@ package io.openvidu.server.config;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
|
@ -46,18 +52,42 @@ import com.google.gson.JsonElement;
|
|||
import com.google.gson.JsonParser;
|
||||
|
||||
import io.openvidu.java.client.OpenViduRole;
|
||||
import io.openvidu.server.OpenViduServer;
|
||||
import io.openvidu.server.cdr.CDREventName;
|
||||
import io.openvidu.server.recording.RecordingNotification;
|
||||
|
||||
@Component
|
||||
public class OpenviduConfig {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(OpenviduConfig.class);
|
||||
|
||||
public static final List<String> OPENVIDU_STRING_PROPERTIES = Arrays.asList(new String[] { "openvidu.secret",
|
||||
"openvidu.publicurl", "openvidu.recording.path", "openvidu.recording.notification",
|
||||
"openvidu.recording.custom-layout", "openvidu.webhook.endpoint" });
|
||||
|
||||
public static final List<String> OPENVIDU_INTEGER_PROPERTIES = Arrays
|
||||
.asList(new String[] { "openvidu.recording.autostop-timeout", "openvidu.streams.video.max-recv-bandwidth",
|
||||
"openvidu.streams.video.min-recv-bandwidth", "openvidu.streams.video.max-send-bandwidth",
|
||||
"openvidu.streams.video.min-send-bandwidth" });
|
||||
|
||||
public static final List<String> OPENVIDU_BOOLEAN_PROPERTIES = Arrays.asList(new String[] { "openvidu.cdr",
|
||||
"openvidu.recording", "openvidu.recording.public-access", "openvidu.webhook", });
|
||||
|
||||
public static final List<String> OPENVIDU_ARRAY_PROPERTIES = Arrays
|
||||
.asList(new String[] { "kms.uris", "openvidu.webhook.headers", "openvidu.webhook.events", });
|
||||
|
||||
public static final List<String> OPENVIDU_PROPERTIES = Stream.of(OPENVIDU_STRING_PROPERTIES,
|
||||
OPENVIDU_INTEGER_PROPERTIES, OPENVIDU_BOOLEAN_PROPERTIES, OPENVIDU_ARRAY_PROPERTIES)
|
||||
.flatMap(Collection::stream).collect(Collectors.toList());
|
||||
|
||||
public static final List<String> OPENVIDU_VALID_PUBLICURL_VALUES = Arrays
|
||||
.asList(new String[] { "local", "docker", "" });
|
||||
|
||||
@Value("#{'${spring.config.additional-location:}'.length() > 0 ? '${spring.config.additional-location:}' : \"\"}")
|
||||
private String springConfigLocation;
|
||||
|
||||
@Autowired
|
||||
BuildProperties buildProperties;
|
||||
private BuildProperties buildProperties;
|
||||
|
||||
@Value("${kms.uris}")
|
||||
private String kmsUris;
|
||||
|
@ -87,7 +117,7 @@ public class OpenviduConfig {
|
|||
private boolean openviduRecordingPublicAccess;
|
||||
|
||||
@Value("${openvidu.recording.notification}")
|
||||
private String openviduRecordingNotification;
|
||||
private RecordingNotification openviduRecordingNotification;
|
||||
|
||||
@Value("${openvidu.recording.custom-layout}")
|
||||
private String openviduRecordingCustomLayout;
|
||||
|
@ -144,72 +174,9 @@ public class OpenviduConfig {
|
|||
private List<String> kmsUrisList;
|
||||
private List<Header> webhookHeadersList;
|
||||
private List<CDREventName> webhookEventsList;
|
||||
|
||||
private Properties externalizedProperties;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
|
||||
if (!this.springConfigLocation.isEmpty()) {
|
||||
// Properties file has been manually configured in certain path
|
||||
FileSystemResource resource = new FileSystemResource(this.springConfigLocation);
|
||||
try {
|
||||
this.externalizedProperties = PropertiesLoaderUtils.loadProperties(resource);
|
||||
log.info("Properties file found at \"{}\". Content: {}", this.springConfigLocation,
|
||||
externalizedProperties);
|
||||
} catch (IOException e) {
|
||||
log.error("Error in 'spring.config.additional-location' system property: {}", e.getMessage());
|
||||
log.error("Shutting down OpenVidu Server");
|
||||
System.exit(1);
|
||||
}
|
||||
// Check OpenVidu Server write permissions in properties path
|
||||
if (!Files.isWritable(Paths.get(this.springConfigLocation))) {
|
||||
log.warn(
|
||||
"The properties path '{}' set with property 'spring.config.additional-location' is not valid. Reason: OpenVidu Server needs write permissions. Try running command \"sudo chmod 777 {}\". If not, OpenVidu won't be able to overwrite preexisting properties on reboot",
|
||||
this.springConfigLocation, this.springConfigLocation);
|
||||
} else {
|
||||
log.info("OpenVidu Server has write permissions on properties path: {}", this.springConfigLocation);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.kmsUrisList = this.kmsUrisStringToList(this.kmsUris);
|
||||
} catch (Exception e) {
|
||||
log.error("Error in 'kms.uris' system property: " + e.getMessage());
|
||||
log.error("Shutting down OpenVidu Server");
|
||||
System.exit(1);
|
||||
}
|
||||
if (this.isWebhookEnabled()) {
|
||||
log.info("OpenVidu Webhook service enabled");
|
||||
try {
|
||||
if (this.openviduWebhookEndpoint == null || this.openviduWebhookEndpoint.isEmpty()) {
|
||||
log.error(
|
||||
"If OpenVidu Webhook service is enabled property 'openvidu.webhook.endpoint' must be defined");
|
||||
log.error("Shutting down OpenVidu Server");
|
||||
System.exit(1);
|
||||
}
|
||||
this.initiateOpenViduWebhookEndpoint(this.openviduWebhookEndpoint);
|
||||
} catch (Exception e) {
|
||||
log.error("Error in 'openvidu.webhook.endpoint' system property. " + e.getMessage());
|
||||
log.error("Shutting down OpenVidu Server");
|
||||
System.exit(1);
|
||||
}
|
||||
try {
|
||||
this.initiateOpenViduWebhookHeaders(this.openviduWebhookHeaders);
|
||||
} catch (Exception e) {
|
||||
log.error("Error in 'openvidu.webhook.headers' system property: " + e.getMessage());
|
||||
log.error("Shutting down OpenVidu Server");
|
||||
System.exit(1);
|
||||
}
|
||||
try {
|
||||
this.initiateOpenViduWebhookEvents(this.openviduWebhookEvents);
|
||||
} catch (Exception e) {
|
||||
log.error("Error in 'openvidu.webhook.events' system property: " + e.getMessage());
|
||||
log.error("Shutting down OpenVidu Server");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getKmsUris() {
|
||||
return this.kmsUrisList;
|
||||
}
|
||||
|
@ -311,7 +278,7 @@ public class OpenviduConfig {
|
|||
return this.coturnRedisDbname;
|
||||
}
|
||||
|
||||
public String getOpenViduRecordingNotification() {
|
||||
public RecordingNotification getOpenViduRecordingNotification() {
|
||||
return this.openviduRecordingNotification;
|
||||
}
|
||||
|
||||
|
@ -338,16 +305,16 @@ public class OpenviduConfig {
|
|||
public OpenViduRole[] getRolesFromRecordingNotification() {
|
||||
OpenViduRole[] roles;
|
||||
switch (this.openviduRecordingNotification) {
|
||||
case "none":
|
||||
case none:
|
||||
roles = new OpenViduRole[0];
|
||||
break;
|
||||
case "moderator":
|
||||
case moderator:
|
||||
roles = new OpenViduRole[] { OpenViduRole.MODERATOR };
|
||||
break;
|
||||
case "publisher_moderator":
|
||||
case publisher_moderator:
|
||||
roles = new OpenViduRole[] { OpenViduRole.PUBLISHER, OpenViduRole.MODERATOR };
|
||||
break;
|
||||
case "all":
|
||||
case all:
|
||||
roles = new OpenViduRole[] { OpenViduRole.SUBSCRIBER, OpenViduRole.PUBLISHER, OpenViduRole.MODERATOR };
|
||||
break;
|
||||
default:
|
||||
|
@ -380,6 +347,256 @@ public class OpenviduConfig {
|
|||
return this.externalizedProperties;
|
||||
}
|
||||
|
||||
public void checkWebsocketUri(String uri) throws Exception {
|
||||
try {
|
||||
if (!uri.startsWith("ws://") || uri.startsWith("wss://")) {
|
||||
throw new Exception("WebSocket protocol not found");
|
||||
}
|
||||
String parsedUri = uri.replaceAll("^ws://", "http://").replaceAll("^wss://", "https://");
|
||||
new URL(parsedUri).toURI();
|
||||
} catch (Exception e) {
|
||||
throw new Exception("URI '" + uri + "' has not a valid WebSocket endpoint format: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void checkConfigurationParameters(Map<String, ?> parameters, Collection<String> validKeys) throws Exception {
|
||||
|
||||
parameters = this.filterValidParameters(parameters, validKeys);
|
||||
|
||||
log.info("Checking configuration parameters: {}", parameters.keySet());
|
||||
|
||||
for (String parameter : parameters.keySet()) {
|
||||
switch (parameter) {
|
||||
case "openvidu.secret":
|
||||
String secret = checkString(parameters, parameter);
|
||||
if (secret.isEmpty()) {
|
||||
throw new Exception("Property 'openvidu.secret' cannot be empty");
|
||||
}
|
||||
break;
|
||||
case "openvidu.publicurl":
|
||||
String publicurl = checkString(parameters, parameter);
|
||||
if (!OPENVIDU_VALID_PUBLICURL_VALUES.contains(publicurl)) {
|
||||
// Must be a valid URL
|
||||
try {
|
||||
new URL(publicurl).toURI();
|
||||
} catch (MalformedURLException | URISyntaxException e) {
|
||||
throw new Exception(
|
||||
"Property 'openvidu.publicurl' has not a valid URL format: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "openvidu.cdr":
|
||||
checkBoolean(parameters, parameter);
|
||||
break;
|
||||
case "openvidu.recording":
|
||||
checkBoolean(parameters, parameter);
|
||||
break;
|
||||
case "openvidu.recording.public-access":
|
||||
checkBoolean(parameters, parameter);
|
||||
break;
|
||||
case "openvidu.recording.autostop-timeout":
|
||||
checkIntegerNonNegative(parameters, parameter);
|
||||
break;
|
||||
case "openvidu.recording.notification":
|
||||
String recordingNotif = checkString(parameters, parameter);
|
||||
try {
|
||||
RecordingNotification.valueOf(recordingNotif);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new Exception("Property 'openvidu.recording.notification' has not a valid value ('"
|
||||
+ recordingNotif + "'). Must be one of " + Arrays.asList(RecordingNotification.values()));
|
||||
}
|
||||
break;
|
||||
case "openvidu.webhook":
|
||||
checkBoolean(parameters, parameter);
|
||||
break;
|
||||
case "openvidu.webhook.endpoint":
|
||||
String webhookEndpoint = checkString(parameters, parameter);
|
||||
try {
|
||||
checkWebhookEndpoint(webhookEndpoint);
|
||||
} catch (Exception e) {
|
||||
throw new Exception("Property 'openvidu.webhook.endpoint' is not valid: " + e.getMessage());
|
||||
}
|
||||
break;
|
||||
case "openvidu.streams.video.max-recv-bandwidth":
|
||||
checkIntegerNonNegative(parameters, parameter);
|
||||
break;
|
||||
case "openvidu.streams.video.min-recv-bandwidth":
|
||||
checkIntegerNonNegative(parameters, parameter);
|
||||
break;
|
||||
case "openvidu.streams.video.max-send-bandwidth":
|
||||
checkIntegerNonNegative(parameters, parameter);
|
||||
break;
|
||||
case "openvidu.streams.video.min-send-bandwidth":
|
||||
checkIntegerNonNegative(parameters, parameter);
|
||||
break;
|
||||
case "kms.uris":
|
||||
String kmsUris;
|
||||
try {
|
||||
// First check if castable to a List
|
||||
List<?> list = checkArray(parameters, parameter);
|
||||
String elementString;
|
||||
for (Object element : list) {
|
||||
try {
|
||||
// Check every object is a String value
|
||||
elementString = (String) element;
|
||||
} catch (ClassCastException e) {
|
||||
throw new Exception("Property 'kms.uris' is an array, but contains a value (" + element
|
||||
+ ") that is not a string: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
kmsUris = list.toString();
|
||||
} catch (Exception e) {
|
||||
// If it is not a list, try casting to String
|
||||
kmsUris = checkString(parameters, parameter);
|
||||
}
|
||||
// Finally check all strings have a valid WebSocket URI format
|
||||
try {
|
||||
kmsUrisStringToList(kmsUris);
|
||||
} catch (Exception e) {
|
||||
throw new Exception(
|
||||
"Property 'kms.uris' is an array of strings, but contains some value that has not a valid WbeSocket URI format: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
break;
|
||||
case "openvidu.webhook.headers":
|
||||
String webhookHeaders;
|
||||
try {
|
||||
// First check if castable to a List
|
||||
List<?> list = checkArray(parameters, parameter);
|
||||
String elementString;
|
||||
for (Object element : list) {
|
||||
try {
|
||||
// Check every object is a String value
|
||||
elementString = (String) element;
|
||||
} catch (ClassCastException e) {
|
||||
throw new Exception(
|
||||
"Property 'openvidu.webhook.headers' is an array, but contains a value (" + element
|
||||
+ ") that is not a string: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
webhookHeaders = list.toString();
|
||||
} catch (Exception e) {
|
||||
// If it is not a list, try casting to String
|
||||
webhookHeaders = checkString(parameters, parameter);
|
||||
}
|
||||
try {
|
||||
checkWebhookHeaders(webhookHeaders);
|
||||
} catch (Exception e) {
|
||||
throw new Exception(
|
||||
"Property 'openvidu.webhook.headers' contains a value not valid: " + e.getMessage());
|
||||
}
|
||||
break;
|
||||
case "openvidu.webhook.events":
|
||||
String webhookEvents;
|
||||
try {
|
||||
// First check if castable to a List
|
||||
List<?> list = checkArray(parameters, parameter);
|
||||
String elementString;
|
||||
for (Object element : list) {
|
||||
try {
|
||||
// Check every object is a String value
|
||||
elementString = (String) element;
|
||||
} catch (ClassCastException e) {
|
||||
throw new Exception("Property 'openvidu.webhook.events' is an array, but contains a value ("
|
||||
+ element + ") that is not a string: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
webhookEvents = list.toString();
|
||||
} catch (Exception e) {
|
||||
// If it is not a list, try casting to String
|
||||
webhookEvents = checkString(parameters, parameter);
|
||||
}
|
||||
try {
|
||||
checkWebhookEvents(webhookEvents);
|
||||
} catch (Exception e) {
|
||||
throw new Exception(
|
||||
"Property 'openvidu.webhook.events' contains a value not valid: " + e.getMessage());
|
||||
}
|
||||
break;
|
||||
case "openvidu.recording.path":
|
||||
checkString(parameters, parameter);
|
||||
break;
|
||||
case "openvidu.recording.custom-layout":
|
||||
checkString(parameters, parameter);
|
||||
break;
|
||||
default:
|
||||
log.warn("Unknown configuration parameter '{}'", parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String checkString(Map<String, ?> parameters, String key) throws Exception {
|
||||
try {
|
||||
String stringValue = (String) parameters.get(key);
|
||||
return stringValue;
|
||||
} catch (ClassCastException e) {
|
||||
throw new Exception("Property '" + key + "' must be a string: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkBoolean(Map<String, ?> parameters, String key) throws Exception {
|
||||
try {
|
||||
boolean booleanValue = Boolean.parseBoolean((String) parameters.get(key));
|
||||
return booleanValue;
|
||||
} catch (ClassCastException e) {
|
||||
throw new Exception("Property '" + key + "' must be a boolean: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public Integer checkIntegerNonNegative(Map<String, ?> parameters, String key) throws Exception {
|
||||
try {
|
||||
Integer integerValue = Integer.parseInt((String) parameters.get(key));
|
||||
if (integerValue < 0) {
|
||||
throw new Exception("Property '" + key + "' is an integer but cannot be less than 0 (current value: "
|
||||
+ integerValue + ")");
|
||||
}
|
||||
return integerValue;
|
||||
} catch (ClassCastException e) {
|
||||
throw new Exception("Property '" + key + "' must be an integer: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public List<?> checkArray(Map<String, ?> parameters, String key) throws Exception {
|
||||
try {
|
||||
List<String> list = (List<String>) parameters.get(key);
|
||||
return list;
|
||||
} catch (ClassCastException e) {
|
||||
throw new Exception("Property '" + key + "' must be an array: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, ?> filterValidParameters(Map<String, ?> parameters, Collection<String> validKeys) {
|
||||
return parameters.entrySet().stream().filter(x -> validKeys.contains(x.getKey()))
|
||||
.collect(Collectors.toMap(x -> x.getKey(), x -> x.getValue()));
|
||||
}
|
||||
|
||||
public Properties retrieveExternalizedProperties() throws Exception {
|
||||
|
||||
if (this.springConfigLocation.isEmpty()) {
|
||||
throw new Exception(
|
||||
"Externalized properties file not found. Path must be set with configuration parameter 'spring.config.additional-location'");
|
||||
}
|
||||
|
||||
Properties externalizedProps = null;
|
||||
FileSystemResource resource = new FileSystemResource(this.springConfigLocation);
|
||||
try {
|
||||
externalizedProps = PropertiesLoaderUtils.loadProperties(resource);
|
||||
log.info("Properties file found at \"{}\". Content: {}", this.springConfigLocation, externalizedProps);
|
||||
} catch (IOException e) {
|
||||
throw new Exception("Error in 'spring.config.additional-location' system property. Cannot load properties: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
// Check OpenVidu Server write permissions in properties path
|
||||
if (!Files.isWritable(Paths.get(this.springConfigLocation))) {
|
||||
log.warn(
|
||||
"The properties path '{}' set with property 'spring.config.additional-location' is missconfigured. Reason: OpenVidu Server needs write permissions. Try running command \"sudo chmod 777 {}\". If not, OpenVidu won't be able to overwrite preexisting properties",
|
||||
this.springConfigLocation, this.springConfigLocation);
|
||||
} else {
|
||||
log.info("OpenVidu Server has write permissions on properties path {}", this.springConfigLocation);
|
||||
}
|
||||
return externalizedProps;
|
||||
}
|
||||
|
||||
public List<String> kmsUrisStringToList(String kmsUris) throws Exception {
|
||||
kmsUris = kmsUris.replaceAll("\\s", ""); // Remove all white spaces
|
||||
kmsUris = kmsUris.replaceAll("\\\\", ""); // Remove previous escapes
|
||||
|
@ -392,7 +609,6 @@ public class OpenviduConfig {
|
|||
|
||||
List<String> list = JsonUtils.toStringList(kmsUrisArray);
|
||||
if (list.size() == 1 && list.get(0).isEmpty()) {
|
||||
log.warn("Array kms.uris is empty");
|
||||
list = new ArrayList<>();
|
||||
} else {
|
||||
for (String uri : list) {
|
||||
|
@ -402,20 +618,20 @@ public class OpenviduConfig {
|
|||
return list;
|
||||
}
|
||||
|
||||
public void initiateOpenViduWebhookEndpoint(String endpoint) throws Exception {
|
||||
private void checkWebhookEndpoint(String endpoint) throws Exception {
|
||||
try {
|
||||
new URL(endpoint);
|
||||
new URL(endpoint).toURI();
|
||||
log.info("OpenVidu Webhook endpoint is {}", endpoint);
|
||||
} catch (MalformedURLException e) {
|
||||
} catch (MalformedURLException | URISyntaxException e) {
|
||||
throw new Exception("Webhook endpoint '" + endpoint + "' is not correct. Malformed URL: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void initiateOpenViduWebhookHeaders(String headers) throws Exception {
|
||||
private List<Header> checkWebhookHeaders(String headers) throws Exception {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonElement elem = parser.parse(headers);
|
||||
JsonArray headersJsonArray = elem.getAsJsonArray();
|
||||
this.webhookHeadersList = new ArrayList<>();
|
||||
List<Header> headerList = new ArrayList<>();
|
||||
|
||||
for (JsonElement jsonElement : headersJsonArray) {
|
||||
String headerString = jsonElement.getAsString();
|
||||
|
@ -434,37 +650,112 @@ public class OpenviduConfig {
|
|||
throw new Exception(
|
||||
"HTTP header '" + headerString + "' syntax is not correct. Header value cannot be empty");
|
||||
}
|
||||
this.webhookHeadersList.add(new BasicHeader(headerName, headerValue));
|
||||
headerList.add(new BasicHeader(headerName, headerValue));
|
||||
}
|
||||
log.info("OpenVidu Webhook headers: {}", this.getOpenViduWebhookHeaders().toString());
|
||||
return headerList;
|
||||
}
|
||||
|
||||
public void initiateOpenViduWebhookEvents(String events) throws Exception {
|
||||
private List<CDREventName> checkWebhookEvents(String events) throws Exception {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonElement elem = parser.parse(events);
|
||||
JsonArray eventsJsonArray = elem.getAsJsonArray();
|
||||
this.webhookEventsList = new ArrayList<>();
|
||||
List<CDREventName> eventList = new ArrayList<>();
|
||||
|
||||
for (JsonElement jsonElement : eventsJsonArray) {
|
||||
String eventString = jsonElement.getAsString();
|
||||
try {
|
||||
CDREventName.valueOf(eventString);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new Exception("Event name '" + eventString + "' does not exist");
|
||||
throw new Exception("Event '" + eventString + "' does not exist");
|
||||
}
|
||||
this.webhookEventsList.add(CDREventName.valueOf(eventString));
|
||||
eventList.add(CDREventName.valueOf(eventString));
|
||||
}
|
||||
log.info("OpenVidu Webhook events: {}", this.getOpenViduWebhookEvents().toString());
|
||||
return eventList;
|
||||
}
|
||||
|
||||
public void checkWebsocketUri(String uri) throws MalformedURLException {
|
||||
try {
|
||||
String parsedUri = uri.replaceAll("^ws://", "http://").replaceAll("^wss://", "https://");
|
||||
new URL(parsedUri);
|
||||
} catch (MalformedURLException e) {
|
||||
log.error("URI {} is not a valid WebSocket endpoint", uri);
|
||||
throw e;
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
|
||||
// Check configuration parameters
|
||||
Map<String, ?> props = null;
|
||||
if (!this.springConfigLocation.isEmpty()) {
|
||||
try {
|
||||
this.externalizedProperties = this.retrieveExternalizedProperties();
|
||||
props = (Map) this.externalizedProperties;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
log.error("Shutting down OpenVidu Server");
|
||||
System.exit(1);
|
||||
}
|
||||
} else {
|
||||
props = (Map) System.getProperties();
|
||||
}
|
||||
|
||||
try {
|
||||
this.checkConfigurationParameters(props, OPENVIDU_PROPERTIES);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
log.error("Shutting down OpenVidu Server");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
this.kmsUrisList = this.kmsUrisStringToList(this.kmsUris);
|
||||
if (this.isWebhookEnabled()) {
|
||||
this.webhookHeadersList = this.checkWebhookHeaders(this.openviduWebhookHeaders);
|
||||
this.webhookEventsList = this.checkWebhookEvents(this.openviduWebhookEvents);
|
||||
log.info("OpenVidu Webhook endpoint: {}", this.openviduWebhookEndpoint);
|
||||
log.info("OpenVidu Webhook headers: {}", this.getOpenViduWebhookHeaders().toString());
|
||||
log.info("OpenVidu Webhook events: {}", this.getOpenViduWebhookEvents().toString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Unexpected exception when setting final value of configuration parameters: {}", e.getMessage());
|
||||
}
|
||||
|
||||
// Generate final public url
|
||||
String publicUrl = this.getOpenViduPublicUrl();
|
||||
String type = "";
|
||||
switch (publicUrl) {
|
||||
case "docker":
|
||||
try {
|
||||
String containerIp = OpenViduServer.getContainerIp();
|
||||
OpenViduServer.wsUrl = "wss://" + containerIp + ":" + this.getServerPort();
|
||||
} catch (Exception e) {
|
||||
log.error("Docker container IP was configured, but there was an error obtaining IP: "
|
||||
+ e.getClass().getName() + " " + e.getMessage());
|
||||
log.error("Fallback to local URL");
|
||||
OpenViduServer.wsUrl = null;
|
||||
}
|
||||
break;
|
||||
case "local":
|
||||
break;
|
||||
case "":
|
||||
break;
|
||||
default:
|
||||
if (publicUrl.startsWith("https://")) {
|
||||
OpenViduServer.wsUrl = publicUrl.replace("https://", "wss://");
|
||||
} else if (publicUrl.startsWith("http://")) {
|
||||
OpenViduServer.wsUrl = publicUrl.replace("http://", "wss://");
|
||||
}
|
||||
|
||||
if (!OpenViduServer.wsUrl.startsWith("wss://")) {
|
||||
OpenViduServer.wsUrl = "wss://" + OpenViduServer.wsUrl;
|
||||
}
|
||||
}
|
||||
|
||||
if (OpenViduServer.wsUrl == null) {
|
||||
type = "local";
|
||||
OpenViduServer.wsUrl = "wss://localhost:" + this.getServerPort();
|
||||
}
|
||||
|
||||
if (OpenViduServer.wsUrl.endsWith("/")) {
|
||||
OpenViduServer.wsUrl = OpenViduServer.wsUrl.substring(0, OpenViduServer.wsUrl.length() - 1);
|
||||
}
|
||||
|
||||
String finalUrl = OpenViduServer.wsUrl.replaceFirst("wss://", "https://").replaceFirst("ws://", "http://");
|
||||
this.setFinalUrl(finalUrl);
|
||||
OpenViduServer.httpUrl = this.getFinalUrl();
|
||||
OpenViduServer.publicurlType = type;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package io.openvidu.server.recording;
|
||||
|
||||
/**
|
||||
* Defines which users should receive the Session recording notifications on the
|
||||
* client side (recordingStarted, recordingStopped)
|
||||
*
|
||||
* @author Pablo Fuente (pablofuenteperez@gmail.com)
|
||||
*/
|
||||
public enum RecordingNotification {
|
||||
|
||||
/*
|
||||
* No user of the session will receive recording events
|
||||
*/
|
||||
none,
|
||||
|
||||
/*
|
||||
* Only users with role MODERATOR will receive recording events
|
||||
*/
|
||||
moderator,
|
||||
|
||||
/*
|
||||
* Users with role MODERATOR or PUBLISHER will receive recording events
|
||||
*/
|
||||
publisher_moderator,
|
||||
|
||||
/*
|
||||
* All users of to the session will receive recording events
|
||||
*/
|
||||
all
|
||||
|
||||
}
|
|
@ -36,6 +36,8 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.kurento.client.ErrorEvent;
|
||||
import org.kurento.client.EventListener;
|
||||
|
@ -116,6 +118,30 @@ public class RecordingManager {
|
|||
.asList(new EndReason[] { EndReason.disconnect, EndReason.forceDisconnectByUser,
|
||||
EndReason.forceDisconnectByServer, EndReason.networkDisconnect });
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (this.openviduConfig.isRecordingModuleEnabled()) {
|
||||
try {
|
||||
this.initializeRecordingManager();
|
||||
} catch (OpenViduException e) {
|
||||
String finalErrorMessage = "";
|
||||
if (e.getCodeValue() == Code.DOCKER_NOT_FOUND.getValue()) {
|
||||
finalErrorMessage = "Error connecting to Docker daemon. Enabling OpenVidu recording module requires Docker";
|
||||
} else if (e.getCodeValue() == Code.RECORDING_PATH_NOT_VALID.getValue()) {
|
||||
finalErrorMessage = "Error initializing recording path \""
|
||||
+ this.openviduConfig.getOpenViduRecordingPath()
|
||||
+ "\" set with system property \"openvidu.recording.path\"";
|
||||
} else if (e.getCodeValue() == Code.RECORDING_FILE_EMPTY_ERROR.getValue()) {
|
||||
finalErrorMessage = "Error initializing recording custom layouts path \""
|
||||
+ this.openviduConfig.getOpenviduRecordingCustomLayout()
|
||||
+ "\" set with system property \"openvidu.recording.custom-layout\"";
|
||||
}
|
||||
log.error(finalErrorMessage + ". Shutting down OpenVidu Server");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void initializeRecordingManager() throws OpenViduException {
|
||||
|
||||
RecordingManager.IMAGE_TAG = openviduConfig.getOpenViduRecordingVersion();
|
||||
|
|
|
@ -48,7 +48,7 @@ public class ConfigRestController {
|
|||
private static final Logger log = LoggerFactory.getLogger(ConfigRestController.class);
|
||||
|
||||
@Autowired
|
||||
protected OpenviduConfig openviduConfig;
|
||||
private OpenviduConfig openviduConfig;
|
||||
|
||||
@RequestMapping(value = "/openvidu-version", method = RequestMethod.GET)
|
||||
public String getOpenViduServerVersion() {
|
||||
|
@ -108,7 +108,7 @@ public class ConfigRestController {
|
|||
json.addProperty("openviduRecordingVersion", openviduConfig.getOpenViduRecordingVersion());
|
||||
json.addProperty("openviduRecordingPath", openviduConfig.getOpenViduRecordingPath());
|
||||
json.addProperty("openviduRecordingPublicAccess", openviduConfig.getOpenViduRecordingPublicAccess());
|
||||
json.addProperty("openviduRecordingNotification", openviduConfig.getOpenViduRecordingNotification());
|
||||
json.addProperty("openviduRecordingNotification", openviduConfig.getOpenViduRecordingNotification().name());
|
||||
json.addProperty("openviduRecordingCustomLayout", openviduConfig.getOpenviduRecordingCustomLayout());
|
||||
json.addProperty("openviduRecordingAutostopTimeout", openviduConfig.getOpenviduRecordingAutostopTimeout());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue