/**
* Sahi - Web Automation and Test Tool
*
* Copyright 2006 V Narayan Raman
*
* 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 net.sf.sahi.config;
import com.google.common.io.Files;
import net.sf.sahi.util.FileUtils;
import net.sf.sahi.util.Utils;
import net.sf.sahi.workspace.WorkspaceBuilder;
import org.apache.log4j.Logger;
import java.io.*;
import java.util.ArrayList;
import java.util.Properties;
import java.util.jar.Manifest;
import java.util.regex.Pattern;
/**
* Configuration resolves all properties and paths required by Sahi.<br/>
* Invoking Configuration.init is mandatory before invoking Proxy.start
*/
public class Configuration {
public static final String PLAYBACK_LOG_ROOT = "playback";
private static final String HTDOCS_ROOT = "htdocs/";
private static Logger logger = Logger.getLogger(Configuration.class);
private static final String SAHI_USER_PROPERTIES = "config/userdata.properties";
private static final String LOG_PROPERITES = "config/log.properties";
private static final String TMP_DOWNLOAD_DIR = "temp/download";
static Properties userProperties; // = new Properties(properties);
static int enableKeepAlive = 0;
private static String userDataDir = null;
private static String basePath = null;
private static String[] exclusionList;
private static String keytoolPath;
private static boolean keytoolFound;
private static String overriddenControllerMode;
private static String[] downloadURLList;
private static String[] blockableSSLDomainList;
private static String domainFixInfo;
private static File tempDir;
private static String version;
public Properties getUserProperties() {
return userProperties;
}
/**
* Initializes Sahi's properties and relative paths.<br/>
* This is required before invoking Proxy.start()<br/>
* This assumes the current folder as Sahi's basePath. <br/>
* Same as init(".")<br/>
* A call to <code>init</code> or <code>initJava</code> is required before
* invoking Proxy.start()
*/
public static void init() {
init(".", new File("./userdata").getAbsolutePath());
}
public static void init(String basePath) {
init(basePath, basePath + "/userdata");
}
/**
* Reads the default configuration and an optional properties file
*
* A call to <code>init</code> or <code>initJava</code> is required before
* invoking Proxy.start()
*
* @param modifiedProperties String .properties file
*/
public static void initWithOptionalProperties(String modifiedProperties) {
// why?
Utils.BUFFER_SIZE = getBufferSize();
System.setProperty("java.util.logging.config.file", getLogPropertyFile());
System.setProperty("java.util.logging.config.file", LOG_PROPERITES);
InputStream propertiesIn = Configuration.class.getResourceAsStream("sahi.properties");
if (propertiesIn == null) {
throw new RuntimeException("sahi.properties not found");
}
try {
userProperties = loadProperties(propertiesIn);
} catch (IOException e) {
throw new RuntimeException("could not load properties");
}
if (modifiedProperties != null) {
try {
userProperties.load(new FileInputStream(modifiedProperties));
} catch (IOException e) {
// FIXME logging
}
}
}
public static void init(String basePath1, String workingDirectory) {
try {
basePath = basePath1;
buildWorkspace(workingDirectory);
userDataDir = workingDirectory;
InputStream sahiProperties;
sahiProperties = Configuration.class.getResourceAsStream("sahi.properties");
Properties properties = new Properties();
loadProperties(sahiProperties, properties);
String userPropsPath = Utils.concatPaths(userDataDir, SAHI_USER_PROPERTIES);
userProperties = new Properties();
loadProperties(userPropsPath, userProperties);
Properties combinedProperties = new Properties();
combinedProperties.putAll(properties);
combinedProperties.putAll(userProperties);
userProperties=combinedProperties;
System.setProperty("java.util.logging.config.file", LOG_PROPERITES);
createFolders(new File(getPlayBackLogsRoot()));
createFolders(new File(getCertsPath()));
createFolders(new File(tempDownloadDir()));
copyProfiles();
Utils.BUFFER_SIZE = getBufferSize();
System.setProperty("java.util.logging.config.file", getLogPropertyFile());
} catch (IOException e) {
e.printStackTrace();
}
}
private static void buildWorkspace(String workingDirectory) {
WorkspaceBuilder wb = new WorkspaceBuilder(workingDirectory);
wb.build();
}
private static Properties loadProperties(InputStream in) throws IOException {
Properties properties = new Properties();
properties.load(in);
in.close();
return properties;
}
private static String getLogPropertyFile() {
return Utils.concatPaths(userDataDir, "/config/log.properties");
}
private static int getBufferSize() {
try {
return Integer.parseInt(getUserProperty("io.buffer_size"));
} catch (Exception e) {
return 4096;
}
}
public static void loadProperties(String sahiProperties, Properties props) throws IOException {
FileInputStream inStream = new FileInputStream(sahiProperties);
props.load(inStream);
inStream.close();
}
public static void loadProperties(InputStream source, Properties propertiesToFill) throws IOException {
propertiesToFill.load(source);
source.close();
}
/**
* Initializes Sahi's properties and relative paths and additionally sets
* the Controller to Java mode.<br/>
* This is required before invoking Proxy.start()<br/>
* This assumes the current folder as Sahi's basePath. <br/>
* Same as initJava(".")<br/>
* A call to <code>init</code> or <code>initJava</code> is required before
* invoking Proxy.start()
*/
public static void initJava() {
initJava(".", "userdata");
}
/**
* Initializes Sahi's properties and relative paths and additionally sets
* the Controller to Java mode.<br/>
* A call to <code>init</code> or <code>initJava</code> is required before
* invoking Proxy.start()
*
* @param basePath String basePath to folder where sahi is located
* @param userDataDirectory String path to user data directory
*/
public static void initJava(String basePath, String userDataDirectory) {
init(basePath, userDataDirectory);
setControllerMode("java");
}
public static void copyProfiles() throws IOException {
final String templateDir = Utils.concatPaths(basePath, getUserProperty("ff.profiles.template"));
final String profilesDirPath = Utils.concatPaths(userDataDir, getUserProperty("ff.profiles.dir"));
File profileDir = new File(profilesDirPath);
profileDir.mkdirs();
String prefix = getUserProperty("ff.profiles.prefix");
String profile0 = Utils.concatPaths(profileDir.getCanonicalPath(), prefix + 0);
if (!new File(profile0).exists()) {
try {
FileUtils.copyDir(templateDir, profile0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int maxProfiles = Integer.parseInt(getUserProperty("ff.profiles.max_number", "10"));
for (int i = 1; i < maxProfiles; i++) {
String profileN = Utils.concatPaths(profileDir.getCanonicalPath(), prefix + i);
copyFile(profile0, profileN, "prefs.js");
copyFile(profile0, profileN, "cert8.db");
copyFile(profile0, profileN, "key3.db");
copyFile(profile0, profileN, "cert_override.txt");
}
}
public static void copyFile(final String origDir, final String destDir, final String fileName) {
try {
final File src = new File(origDir, fileName);
if (src.exists())
FileUtils.copyFile(src, new File(destDir, fileName));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void createFolders(final File file) {
if (!file.exists()) {
file.mkdirs();
}
}
/**
* Gets Port for the proxy.
* Using system environment variable, jdk property, user properties to find one.
* If none is found, 9999 is used.
**/
public static int getPort() {
try {
if (System.getenv("PORT") != null) {
return Integer.parseInt(System.getenv("PORT"));
}
} catch (Exception e) {}
try {
if (System.getProperty("proxy.port") != null) {
return Integer.parseInt(System.getProperty("proxy.port"));
}
} catch (Exception e) {}
try {
return Integer.parseInt(getUserProperty("proxy.port"));
} catch (Exception e) {
return 9999;
}
}
public static String getLogsRoot() {
String fileName = Utils.concatPaths(userDataDir, getUserProperty("logs.dir", "logs"));
File file = new File(fileName);
if (!file.exists()) {
file.mkdirs();
}
return fileName;
}
public static String[] getScriptRoots() {
String[] propertyArray = getPropertyArray("scripts.dir", userProperties, "scripts");
for (int i = 0; i < propertyArray.length; i++) {
propertyArray[i] = Utils.getAbsolutePath(Utils.concatPaths(userDataDir, propertyArray[i]))
+ System.getProperty("file.separator");
}
return propertyArray;
}
public static String[] getScriptExtensions() {
return getPropertyArray("script.extension", userProperties, "sah;sahi;inc");
}
private static String[] getPropertyArray(final String key, Properties props, String defaultValue) {
String property = props.getProperty(key, defaultValue);
String[] tokens = property.split(";");
for (int i = 0; i < tokens.length; i++) {
tokens[i] = tokens[i].trim();
}
return tokens;
}
public static String getPlayBackLogsRoot() {
String fileName = Utils.concatPaths(getLogsRoot(), PLAYBACK_LOG_ROOT);
File file = new File(fileName);
if (!file.exists()) {
file.mkdirs();
}
return fileName;
}
public static String getHtdocsRoot() {
return Utils.concatPaths(basePath, HTDOCS_ROOT) + "/";
}
public static String getPlaybackLogCSSFileName(final boolean addHtdocsRoot) {
final String path = "spr/css/playback_log_format.css";
return addHtdocsRoot ? Utils.concatPaths(getHtdocsRoot(), path) : path;
}
public static String getConsolidatedLogCSSFileName(final boolean addHtdocsRoot) {
final String path = "spr/css/consolidated_log_format.css";
return addHtdocsRoot ? Utils.concatPaths(getHtdocsRoot(), path) : path;
}
public static String getSahiJavascriptLib() {
return Utils.readFileAsString(Utils.concatPaths(getHtdocsRoot(),
"spr/lib.js"));
}
public static boolean isKeepAliveEnabled() {
return (enableKeepAlive > 0)
|| (enableKeepAlive <= 0 && "true".equalsIgnoreCase(getUserProperty("http.keep_alive")));
}
public static int getTimeBetweenTestsInSuite() {
try {
return Integer.parseInt(getUserProperty("suite.time_between_tests"));
} catch (Exception e) {
return 1000;
}
}
public static int getMaxInactiveTimeForScript() {
try {
return Integer.parseInt(getUserProperty("suite.max_inactive_time_for_script")) * 1000;
} catch (Exception e) {
return 20000;
}
}
public static void createScriptsDirIfNeeded() {
String[] scriptRoots = Configuration.getScriptRoots();
for (String scriptRoot : scriptRoots) {
File file = new File(scriptRoot);
file.mkdirs();
}
}
public static String getHotKey() {
String hotkey = getUserProperty("controller.hotkey");
if ("SHIFT".equals(hotkey) || "ALT".equals(hotkey) || "CTRL".equals(hotkey) || "META".equals(hotkey)) {
return hotkey;
}
return "ALT";
}
public static String appendLogsRoot(final String fileName) {
return Utils.concatPaths(getPlayBackLogsRoot(), fileName);
}
public static boolean isDevMode() {
return "true".equals(System.getProperty("sahi.mode.dev"));
}
public static boolean isStrictVisibilityCheckEnabled() {
return "true".equals(getUserProperty("element.visibility_check.strict"));
}
public static String xhrReadyStatesToWaitFor() {
return getUserProperty("xhr.wait_ready_states", "2");
}
public static String getCertsPath() {
return Utils.concatPaths(userDataDir, getUserProperty("certs.dir", "certs"));
}
public static String getRootKeyPath() {
return Configuration.getRootCaPath() + ".key";
}
public static String getRootCaName() {
return getUserProperty("ssl.ca.cn", "Sahi Root Ca");
}
public static String getRootCaPath() {
return Configuration.getCertsPath() + "/ca.crt";
}
public static String getConfigPath() {
return Utils.concatPaths(basePath, "config/");
}
public static String getToolsPath() {
return Utils.concatPaths(basePath, "tools/");
}
public static int getTimeBetweenSteps() {
try {
return Integer.parseInt(getUserProperty("script.time_between_steps"));
} catch (Exception e) {
return 100;
}
}
public static void setTimeBetweenSteps(int speed) {
userProperties.setProperty("script.time_between_steps", "" + speed);
}
public static int getTimeBetweenStepsOnError() {
try {
return Integer.parseInt(getUserProperty("script.time_between_steps_on_error"));
} catch (Exception e) {
return 1000;
}
}
public static int getMaxReAttemptsOnError() {
try {
return Integer.parseInt(getUserProperty("script.max_reattempts_on_error"));
} catch (Exception e) {
return 10;
}
}
public static int getMaxCyclesForPageLoad() {
try {
return Integer.parseInt(getUserProperty("script.max_cycles_for_page_load"));
} catch (Exception e) {
return 10;
}
}
public static String[] getExclusionList() {
if (exclusionList == null) {
File file = new File(Utils.concatPaths(userDataDir, "config/exclude_inject.txt"));
exclusionList = (file.exists()) ? getNonBlankLines(Utils.readCachedFile(file)) : new String[0];
}
return exclusionList;
}
public static String getDomainFixInfo() {
if (domainFixInfo == null) {
StringBuilder sb = new StringBuilder();
sb.append("{");
boolean start = true;
File file = new File(Utils.concatPaths(userDataDir, "config/domainfix.txt"));
String[] lines = (file.exists()) ? getNonBlankLines(Utils.readCachedFile(file)) : new String[0];
for (String line : lines) {
if (line.startsWith("#"))
continue;
String[] split = line.split("[\\s]+");
String key = split[0];
String value = key;
if (split.length == 1) {
value = split[1];
} else {
int ix = value.lastIndexOf(".", value.lastIndexOf(".") - 1);
if (ix != -1)
value = value.substring(ix + 1);
}
if (start) {
start = false;
} else {
sb.append(",");
}
key = key.replace(".", "[.]").replace("*", ".*");
sb.append("'" + key + "': '" + value + "'");
}
sb.append("}");
domainFixInfo = sb.toString();
}
return domainFixInfo;
}
public static void enableKeepAlive() {
enableKeepAlive++;
}
public static void disableKeepAlive() {
enableKeepAlive--;
}
public static boolean modifyActiveX() {
return "true".equals(getUserProperty("response.modify_activex"));
}
public static boolean spanVariablesAcrossSuite() {
return "true".equals(getUserProperty("suite.global_variables"));
}
public static Pattern getDownloadContentTypesRegExp() {
String[] downloadables = getNonBlankLines(Utils.readCachedFile(Utils.concatPaths(userDataDir,
"config/download_contenttypes.txt")));
if (downloadables.length != 0) {
try {
StringBuilder sb = new StringBuilder("(?:.*");
for (int i = 0; i < downloadables.length; i++) {
sb.append(downloadables[i]);
if (i != downloadables.length - 1) {
sb.append(".*)|(?:");
}
}
sb.append(".*)");
return Pattern.compile(sb.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
return Pattern.compile("");
}
public static String[] getDownloadURLList() {
if (downloadURLList == null) {
downloadURLList = getNonBlankLines(Utils.readCachedFileIfExists(Utils.concatPaths(userDataDir,
"config/download_urls.txt")));
}
return downloadURLList;
}
public static String[] getBlockableSSLDomainsList() {
if (blockableSSLDomainList == null) {
blockableSSLDomainList = getNonBlankLines(Utils.readCachedFileIfExists(Utils.concatPaths(userDataDir,
"config/block_ssl_domains.txt")));
}
return blockableSSLDomainList;
}
protected static String[] getNonBlankLines(byte[] b) {
return getNonBlankLines(new String(b));
}
protected static String[] getNonBlankLines(String s) {
s = s.trim().replaceAll("\\\r", "");
String[] tokens = s.split("\n");
ArrayList<String> l = new ArrayList<>();
for (String token1 : tokens) {
String token = token1.trim();
if (!token.equals("")) {
l.add(token);
}
}
return l.toArray(new String[l.size()]);
}
public static String tempDownloadDir() {
return Utils.concatPaths(userDataDir, TMP_DOWNLOAD_DIR);
}
public static void setProxyProperties() {
Properties systemProperties = System.getProperties();
if (isHttpProxyEnabled()) {
systemProperties.setProperty("http.proxyHost", getHttpProxyHost());
systemProperties.setProperty("http.proxyPort", "" + getHttpProxyPort());
systemProperties.setProperty("http.nonProxyHosts", "" + getHttpNonProxyHosts());
}
if (isHttpsProxyEnabled()) {
systemProperties.setProperty("https.proxyHost", getHttpsProxyHost());
systemProperties.setProperty("https.proxyPort", "" + getHttpsProxyPort());
systemProperties.setProperty("http.nonProxyHosts", "" + getHttpsNonProxyHosts());
systemProperties.setProperty("https.nonProxyHosts", "" + getHttpsNonProxyHosts());// ?
}
}
public static boolean isHttpProxyEnabled() {
return "true".equals(getUserProperty("ext.http.proxy.enable"));
}
public static boolean isHttpsProxyEnabled() {
return "true".equals(getUserProperty("ext.https.proxy.enable"));
}
public static String getHttpProxyHost() {
return getUserProperty("ext.http.proxy.host");
}
public static String getHttpProxyPort() {
return getUserProperty("ext.http.proxy.port");
}
public static String getHttpNonProxyHosts() {
return getUserProperty("ext.http.both.proxy.bypass_hosts");
}
public static String getHttpProxyAuthName() {
return getUserProperty("ext.http.proxy.auth.name");
}
public static String getHttpProxyAuthPassword() {
return getUserProperty("ext.http.proxy.auth.password");
}
public static String getHttpsProxyPort() {
return getUserProperty("ext.https.proxy.port");
}
public static String getHttpsProxyHost() {
return getUserProperty("ext.https.proxy.host");
}
static String getHttpsNonProxyHosts() {
return getUserProperty("ext.http.both.proxy.bypass_hosts");
}
public static String getHttpsProxyAuthName() {
return getUserProperty("ext.https.proxy.auth.name");
}
public static String getHttpsProxyAuthPassword() {
return getUserProperty("ext.https.proxy.auth.password");
}
public static boolean isHttpProxyAuthEnabled() {
return "true".equals(getUserProperty("ext.http.proxy.auth.enable"));
}
public static boolean isHttpsProxyAuthEnabled() {
return "true".equals(getUserProperty("ext.https.proxy.auth.enable"));
}
public static boolean isModifiedTrafficLoggingOn() {
return "true".equals(getUserProperty("debug.traffic.log.modified"));
}
public static boolean isUnmodifiedTrafficLoggingOn() {
return "true".equals(getUserProperty("debug.traffic.log.unmodified"));
}
public static void setModifiedTrafficLogging(boolean flag) {
userProperties.setProperty("debug.traffic.log.modified", "" + flag);
}
public static void setUnmodifiedTrafficLogging(boolean flag) {
userProperties.setProperty("debug.traffic.log.unmodified", "" + flag);
}
public static boolean downloadIfContentDispositionIsAttachment() {
return "true".equals(getUserProperty("download.download_if_contentdisposition_is_attachment"));
}
public static String getMimeTypesMappingFile() {
return Utils.concatPaths(basePath, "config/mime-types.mapping");
}
public static String getAbsolutePath(String relPath) {
return Utils.concatPaths(basePath, relPath);
}
public static String getAbsoluteUserPath(String relPath) {
return Utils.concatPaths(userDataDir, relPath);
}
public static int sampleLength() {
try {
return Integer.parseInt(getUserProperty("response.sample_length"));
} catch (Exception e) {
return 500;
}
}
public static String getSSLClientCertPath() {
final String path = getUserProperty("ssl.client.cert.path");
if (path == null)
return null;
return getAbsoluteUserPath(path);
}
private static String getUserProperty(String key) {
return userProperties.getProperty(key);
}
private static String getUserProperty(String key, String defaultValue) {
return userProperties.getProperty(key, defaultValue);
}
public static String getSSLClientCertPassword() {
return getUserProperty("ssl.client.cert.password");
}
public static String getSSLClientKeyStoreType() {
return getUserProperty("ssl.client.keystore.type", "PKCS12");
}
public static String getControllerMode() {
if (overriddenControllerMode == null) {
return getUserProperty("controller.mode", "sahi");
}
return overriddenControllerMode;
}
/**
* Sets the Controller mode. <br/>
* Currently valid values are "sahi" and "java".<br/>
* Set this to "java" to use the java Controller instead of Sahi's default
* Controller.
*
* @param mode "java" or "sahi"
*/
public static void setControllerMode(String mode) {
overriddenControllerMode = mode;
}
public static String getSSLAlgorithm() {
return getUserProperty("ssl.algorithm", "SunX509").trim();
}
public static String getInjectTop() {
return Configuration.getAbsolutePath("config/inject_top.txt");
}
public static String getInjectBottom() {
return Configuration.getAbsolutePath("config/inject_bottom.txt");
}
public static String getJiraPropertyPath() {
return Utils.concatPaths(userDataDir, "config/jira.properties");
}
public static String getUserDataDir() {
return Utils.getAbsolutePath(userDataDir);
}
public static String getChangeLogFilePath() {
return Utils.concatPaths(getConfigPath(), "../docs/changelog.txt");
}
public static File getOSPropertiesFile() throws Exception {
String filename = "os.properties";
if (tempDir == null) tempDir = Files.createTempDir();
return FileUtils.copyToTempFile(filename, Configuration.class, tempDir);
}
public static String getVersion() {
if (version != null) {
return version;
}
String v = null;
try {
InputStream in = Configuration.class.getResourceAsStream("/META-INF/MANIFEST.MF");
Manifest m = new Manifest(in);
v = m.getMainAttributes().getValue("Implementation-Version");
final String SNAPSHOT_SUFFIX = "-SNAPSHOT";
if (v != null && v.endsWith((SNAPSHOT_SUFFIX))) {
v = v.substring(0, v.length() - SNAPSHOT_SUFFIX.length());
v += "-" + m.getMainAttributes().getValue("Implementation-Build");
}
} catch (IOException ex) {
ex.printStackTrace();
}
version = v == null ? "DEV-" + Long.toString(System.currentTimeMillis(), 36): v;
return version;
}
public static int getStabilityIndex() {
try {
int i = Integer.parseInt(getUserProperty("script.stability_index"));
if (i < 1)
i = 1;
return i;
} catch (Exception e) {
return 5;
}
}
public static boolean getEscapeUnicode() {
return "true".equals(getUserProperty("script.escape_unicode"));
}
public static boolean addJSModifierFilter() {
return !"false".equals(getUserProperty("filters.addJSModifierFilter"));
}
public static boolean addHTMLModifierFilter() {
return !"false".equals(getUserProperty("filters.addHTMLModifierFilter"));
}
public static boolean addCharacterFilter() {
return !"false".equals(getUserProperty("filters.addCharacterFilter"));
}
public static String getCommonDomain() {
return getUserProperty("sahi.common_domain", "sahi.example.com").trim();
}
public static String getBrowserTypesPath() {
return getAbsoluteUserPath("config/browser_types.xml");
}
public static boolean getChromeExplicitCheckboxRadioToggle() {
return "true".equals(getUserProperty("simulation.chrome.explicit_checkbox_radio_toggle"));
}
public static String getIgnorableIdsPattern() {
return getUserProperty("recorder.ignorable_ids.pattern", "^z_").trim();
}
public static boolean forceTreatAsXHTML() {
return "true".equals(getUserProperty("sahi.inject.force_treat_as_xhtml"));
}
public static int getMaxTimeForPIDGatherFromDashboard() {
try {
return Integer.parseInt(getUserProperty("dashboard.max_time_for_pid_gather", "5000"));
} catch (Exception e) {
return 5000;
}
}
public static int getMaxTimeForPIDGather() {
try {
return Integer.parseInt(getUserProperty("script.max_time_for_pid_gather", "60000"));
} catch (Exception e) {
return 60000;
}
}
// Pro start
}