/*
* (C) Copyright 2014 Kurento (http://kurento.org/)
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 2.1 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl-2.1.html
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*/
package com.kurento.kmf.test.services;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Writer;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.io.CharStreams;
import com.kurento.kmf.common.Address;
import com.kurento.kmf.common.PropertiesManager;
import com.kurento.kmf.test.Shell;
import com.kurento.kmf.thrift.ThriftInterfaceConfiguration;
import com.kurento.kmf.thrift.pool.ClientPoolException;
import com.kurento.kmf.thrift.pool.ThriftClientPoolService;
import freemarker.template.Configuration;
import freemarker.template.Template;
/**
* Initializer/stopper class for Kurento Media Server (KMS).
*
* @author Boni Garcia (bgarcia@gsyc.es)
* @since 4.2.3
*/
public class KurentoMediaServerManager {
private static final String PROJECT_PATH_PROP = "project.path";
private static final String PROJECT_PATH_DEFAULT = ".";
private static final String KURENTO_WORKSPACE_PROP = "kurento.workspace";
private static final String KURENTO_WORKSPACE_DEFAULT = "/tmp";
private static final String KURENTO_GST_PLUGINS_PROP = "kms.gst.plugins";
private static final String KURENTO_GST_PLUGINS_DEFAULT = "";
private static final String KURENTO_SERVER_COMMAND_PROP = "kms.command";
private static final String KURENTO_SERVER_COMMAND_DEFAULT = "/usr/bin/kurento";
private static final String KURENTO_SERVER_DEBUG_PROP = "kms.debug";
private static final String KURENTO_SERVER_DEBUG_DEFAULT = "2,*media_server*:5,*Kurento*:5,KurentoMediaServerServiceHandler:7";
public static Logger log = LoggerFactory
.getLogger(KurentoMediaServerManager.class);
private static String workspace;
private Address thriftAddress;
private int httpPort;
private String testClassName;
private String testMethodName;
private String testDir;
private String serverCommand;
private String gstPlugins;
private String debugOptions;
private Address rabbitMqAddress;
public static KurentoMediaServerManager createWithThriftTransport(
Address thriftAddress, int httpPort) {
KurentoMediaServerManager manager = new KurentoMediaServerManager();
manager.thriftAddress = thriftAddress;
manager.httpPort = httpPort;
return manager;
}
public static KurentoMediaServerManager createWithRabbitMqTransport(
Address rabbitMqAddress, int httpPort) {
KurentoMediaServerManager manager = new KurentoMediaServerManager();
manager.rabbitMqAddress = rabbitMqAddress;
manager.httpPort = httpPort;
return manager;
}
private KurentoMediaServerManager() {
}
public void setTestClassName(String testClassName) {
this.testClassName = testClassName;
}
public void setTestMethodName(String testMethodName) {
this.testMethodName = testMethodName;
}
public void start() {
serverCommand = PropertiesManager.getProperty(
KURENTO_SERVER_COMMAND_PROP, KURENTO_SERVER_COMMAND_DEFAULT);
gstPlugins = PropertiesManager.getProperty(KURENTO_GST_PLUGINS_PROP,
KURENTO_GST_PLUGINS_DEFAULT);
try {
workspace = Files.createTempDirectory("kmf-system-test").toString();
} catch (IOException e) {
workspace = PropertiesManager.getProperty(KURENTO_WORKSPACE_PROP,
KURENTO_WORKSPACE_DEFAULT);
log.error(
"Exception loading temporal folder; instead folder {} will be used",
workspace, e);
}
debugOptions = PropertiesManager.getProperty(KURENTO_SERVER_DEBUG_PROP,
KURENTO_SERVER_DEBUG_DEFAULT);
testDir = PropertiesManager.getProperty(PROJECT_PATH_PROP,
PROJECT_PATH_DEFAULT) + "/target/surefire-reports/";
if (!workspace.endsWith("/")) {
workspace += "/";
}
log.debug("Local folder to store temporal files: {}", workspace);
KurentoServicesTestHelper.setTestDir(testDir);
if (rabbitMqAddress != null) {
log.info("Starting KMS with RabbitMQ: RabbitMQAddress:'{}'"
+ " serverCommand:'{}' gstPlugins:'{}' workspace: '{}'",
rabbitMqAddress, serverCommand, gstPlugins, workspace);
} else {
log.info("Starting KMS with Thrift: thriftAddress:'{}'"
+ " serverCommand:'{}' gstPlugins:'{}' workspace: '{}'",
thriftAddress, serverCommand, gstPlugins, workspace);
}
createKurentoConf();
// String logFolder = testDir + testClassName;
// createFolder(logFolder);
File logFile = new File(workspace, "kms.log");
KurentoServicesTestHelper.setServerLogFilePath(logFile);
log.debug("Log file: {}", logFile.getAbsolutePath());
Shell.runAndWait("sh", "-c",
workspace + "kurento.sh > " + logFile.getAbsolutePath()
+ " 2>&1");
waitForKurentoMediaServer();
}
private void waitForKurentoMediaServer() {
// Only wait if KMS uses Thrift
if (thriftAddress != null) {
long startWaiting = System.currentTimeMillis();
ThriftClientPoolService clientPool = new ThriftClientPoolService(
new ThriftInterfaceConfiguration(thriftAddress.getHost(),
thriftAddress.getPort()));
// Wait for a max of 20 seconds
long timeout = System.currentTimeMillis() + 20000;
while (true) {
try {
clientPool.acquireSync();
break;
} catch (ClientPoolException e) {
try {
Thread.sleep(100);
if (System.currentTimeMillis() > timeout) {
throw new RuntimeException(
"Timeout (20 sec) waiting for ThriftClientPoolService");
}
} catch (InterruptedException e1) {
}
}
}
long waitingTime = System.currentTimeMillis() - startWaiting;
log.info("KMS started in {} millis", waitingTime);
} else {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
log.error("InterruptedException {}", e.getMessage());
}
}
}
private void createFolder(String folder) {
File folderFile = new File(folder);
if (!folderFile.exists()) {
folderFile.mkdirs();
}
}
private void createKurentoConf() {
Configuration cfg = new Configuration();
// Data-model
Map<String, Object> data = new HashMap<String, Object>();
if (rabbitMqAddress != null) {
data.put("transport", "RabbitMQ");
data.put("serverAddress", rabbitMqAddress.getHost());
data.put("serverPort", String.valueOf(rabbitMqAddress.getPort()));
} else {
data.put("transport", "Thrift");
data.put("serverAddress", thriftAddress.getHost());
data.put("serverPort", String.valueOf(thriftAddress.getPort()));
}
data.put("gstPlugins", gstPlugins);
data.put("debugOptions", debugOptions);
data.put("serverCommand", serverCommand);
data.put("workspace", workspace);
data.put("httpEndpointPort", String.valueOf(httpPort));
cfg.setClassForTemplateLoading(KurentoMediaServerManager.class,
"/templates/");
createFileFromTemplate(cfg, data, "kurento.conf");
createFileFromTemplate(cfg, data, "pattern.sdp");
createFileFromTemplate(cfg, data, "kurento.sh");
Shell.runAndWait("chmod", "+x", workspace + "kurento.sh");
}
private void createFileFromTemplate(Configuration cfg,
Map<String, Object> data, String filename) {
try {
Template template = cfg.getTemplate(filename + ".ftl");
Writer writer = new FileWriter(new File(workspace + filename));
template.process(data, writer);
writer.flush();
writer.close();
} catch (Exception e) {
throw new RuntimeException(
"Exception while creating file from template", e);
}
}
public void stop() {
int numKmsProcesses = 0;
// Max timeout waiting kms ending: 5 seconds
long timeout = System.currentTimeMillis() + 5000;
do {
// If timeout, break the loop
if (System.currentTimeMillis() > timeout) {
break;
}
// Sending SIGTERM signal to KMS process
kmsSigTerm();
// Wait 100 msec to order kms termination
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
numKmsProcesses = countKmsProcesses();
} while (numKmsProcesses > 0);
if (numKmsProcesses > 0) {
// If at this point there is still kms process (after trying to
// kill it with SIGTERM during 5 seconds), we send the SIGKILL
// signal to the process
kmsSigKill();
}
}
private void kmsSigTerm() {
log.trace("Sending SIGTERM to KMS process");
Shell.runAndWait("sh", "-c", "kill `cat " + workspace + "kms-pid`");
}
private void kmsSigKill() {
log.trace("Sending SIGKILL to KMS process");
Shell.runAndWait("sh", "-c", "kill -9 `cat " + workspace + "kms-pid`");
}
public String getDebugOptions() {
return debugOptions;
}
public void setDebugOptions(String debugOptions) {
this.debugOptions = debugOptions;
}
public void setHttpPort(int httpPort) {
this.httpPort = httpPort;
}
public int countKmsProcesses() {
int result = 0;
try {
// This command counts number of process (given its PID, stored in
// kms-pid file)
String[] command = {
"sh",
"-c",
"ps --pid `cat " + workspace
+ "kms-pid` --no-headers | wc -l" };
Process countKms = Runtime.getRuntime().exec(command);
String stringFromStream = CharStreams
.toString(new InputStreamReader(countKms.getInputStream(),
"UTF-8"));
result = Integer.parseInt(stringFromStream.trim());
} catch (IOException e) {
log.error("Exception counting KMS processes", e);
}
return result;
}
public static String getWorkspace() {
return workspace;
}
}