Smack/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java

558 lines
22 KiB
Java

/**
*
* Copyright 2015-2018 Florian Schmaus
*
* 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 org.igniterealtime.smack.inttest;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.SSLContext;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.debugger.ConsoleDebugger;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.debugger.EnhancedDebugger;
import eu.geekplace.javapinning.java7.Java7Pinning;
import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
// TODO: Rename to SinttestConfiguration.
public final class Configuration {
private static final Logger LOGGER = Logger.getLogger(Configuration.class.getName());
public enum AccountRegistration {
disabled,
inBandRegistration,
serviceAdministration,
}
public enum Debugger {
none,
console,
enhanced,
}
public final DomainBareJid service;
public final String serviceTlsPin;
public final SSLContext tlsContext;
public final SecurityMode securityMode;
public final int replyTimeout;
public final AccountRegistration accountRegistration;
public final String adminAccountUsername;
public final String adminAccountPassword;
public final String adminAccountHostname;
public final int adminAccountPort;
public final String accountOneUsername;
public final String accountOnePassword;
public final String accountOneHostname;
public final int accountOnePort;
public final String accountTwoUsername;
public final String accountTwoPassword;
public final String accountTwoHostname;
public final int accountTwoPort;
public final String accountThreeUsername;
public final String accountThreePassword;
public final String accountThreeHostname;
public final int accountThreePort;
public final Debugger debugger;
public final Set<String> enabledTests;
public final Set<String> disabledTests;
public final Set<String> testPackages;
public final ConnectionConfigurationBuilderApplier configurationApplier;
private Configuration(DomainBareJid service, String serviceTlsPin, SecurityMode securityMode,
int replyTimeout, Debugger debugger,
String accountOneUsername, String accountOnePassword, String accountOneHostname, int accountOnePort,
String accountTwoUsername, String accountTwoPassword, String accountTwoHostname, int accountTwoPort,
String accountThreeUsername, String accountThreePassword, String accountThreeHostname, int accountThreePort,
Set<String> enabledTests, Set<String> disabledTests, Set<String> testPackages,
String adminAccountUsername, String adminAccountPassword, String adminAccountHostname, int adminAccountPort)
throws KeyManagementException, NoSuchAlgorithmException {
this.service = Objects.requireNonNull(service,
"'service' must be set. Either via 'properties' files or via system property 'sinttest.service'.");
this.serviceTlsPin = serviceTlsPin;
if (serviceTlsPin != null) {
tlsContext = Java7Pinning.forPin(serviceTlsPin);
} else {
tlsContext = null;
}
this.securityMode = securityMode;
if (replyTimeout > 0) {
this.replyTimeout = replyTimeout;
} else {
this.replyTimeout = 60000;
}
this.debugger = debugger;
if (StringUtils.isNotEmpty(adminAccountUsername, adminAccountPassword)) {
accountRegistration = AccountRegistration.serviceAdministration;
}
else if (StringUtils.isNotEmpty(accountOneUsername, accountOnePassword, accountTwoUsername, accountTwoPassword,
accountThreeUsername, accountThreePassword)) {
accountRegistration = AccountRegistration.disabled;
}
else {
accountRegistration = AccountRegistration.inBandRegistration;
}
this.adminAccountUsername = adminAccountUsername;
this.adminAccountPassword = adminAccountPassword;
this.adminAccountHostname = adminAccountHostname;
this.adminAccountPort = adminAccountPort;
boolean accountOnePasswordSet = StringUtils.isNotEmpty(accountOnePassword);
if (accountOnePasswordSet != StringUtils.isNotEmpty(accountTwoPassword) ||
accountOnePasswordSet != StringUtils.isNotEmpty(accountThreePassword)) {
// Ensure the invariant that either all main accounts have a password set, or none.
throw new IllegalArgumentException();
}
this.accountOneUsername = accountOneUsername;
this.accountOnePassword = accountOnePassword;
this.accountOneHostname = accountOneHostname;
this.accountOnePort = accountOnePort;
this.accountTwoUsername = accountTwoUsername;
this.accountTwoPassword = accountTwoPassword;
this.accountTwoHostname = accountTwoHostname;
this.accountTwoPort = accountTwoPort;
this.accountThreeUsername = accountThreeUsername;
this.accountThreePassword = accountThreePassword;
this.accountThreeHostname = accountThreeHostname;
this.accountThreePort = accountThreePort;
this.enabledTests = enabledTests;
this.disabledTests = disabledTests;
this.testPackages = testPackages;
this.configurationApplier = builder -> {
if (tlsContext != null) {
builder.setCustomSSLContext(tlsContext);
}
builder.setSecurityMode(securityMode);
builder.setXmppDomain(service);
switch (debugger) {
case enhanced:
builder.setDebuggerFactory(EnhancedDebugger.Factory.INSTANCE);
break;
case console:
builder.setDebuggerFactory(ConsoleDebugger.Factory.INSTANCE);
break;
case none:
// Nothing to do :).
break;
}
};
}
public boolean isAccountRegistrationPossible() {
return accountRegistration != AccountRegistration.disabled;
}
public static Builder builder() {
return new Builder();
}
public static final class Builder {
private DomainBareJid service;
private String serviceTlsPin;
private SecurityMode securityMode;
private int replyTimeout;
private String adminAccountUsername;
private String adminAccountPassword;
private String adminAccountHostname;
private int adminAccountPort;
private String accountOneUsername;
private String accountOnePassword;
private String accountOneHostname;
private int accountOnePort;
private String accountTwoUsername;
private String accountTwoPassword;
private String accountTwoHostname;
private int accountTwoPort;
private String accountThreeUsername;
private String accountThreePassword;
private String accountThreeHostname;
private int accountThreePort;
private Debugger debugger = Debugger.none;
private Set<String> enabledTests;
private Set<String> disabledTests;
private Set<String> testPackages;
private Builder() {
}
public Builder setService(String service) throws XmppStringprepException {
if (service == null) {
// Do nothing if user did not specify the XMPP service domain. When the builder
// builds a configuration using build() it will throw a meaningful exception.
return this;
}
return setService(JidCreate.domainBareFrom(service));
}
public Builder setService(DomainBareJid service) {
this.service = service;
return this;
}
public Builder addEnabledTest(Class<? extends AbstractSmackIntTest> enabledTest) {
if (enabledTests == null) {
enabledTests = new HashSet<>();
}
enabledTests.add(enabledTest.getName());
// Also add the package of the test as test package
return addTestPackage(enabledTest.getPackage().getName());
}
private void ensureTestPackagesIsSet(int length) {
if (testPackages == null) {
testPackages = new HashSet<>(length);
}
}
public Builder addTestPackage(String testPackage) {
ensureTestPackagesIsSet(4);
testPackages.add(testPackage);
return this;
}
public Builder setAdminAccountUsernameAndPassword(String adminAccountUsername, String adminAccountPassword) {
this.adminAccountUsername = StringUtils.requireNotNullNorEmpty(adminAccountUsername, "adminAccountUsername must not be null nor empty");
this.adminAccountPassword = StringUtils.requireNotNullNorEmpty(adminAccountPassword, "adminAccountPassword must no be null nor empty");
return this;
}
public Builder setAdminHostnameAndPort(String adminAccountHostname, int adminAccountPort) {
this.adminAccountHostname = adminAccountHostname;
this.adminAccountPort = adminAccountPort;
return this;
}
public Builder setUsernamesAndPassword(String accountOneUsername, String accountOnePassword,
String accountTwoUsername, String accountTwoPassword, String accountThreeUsername, String accountThreePassword) {
this.accountOneUsername = StringUtils.requireNotNullNorEmpty(accountOneUsername, "accountOneUsername must not be null nor empty");
this.accountOnePassword = StringUtils.requireNotNullNorEmpty(accountOnePassword, "accountOnePassword must not be null nor empty");
this.accountTwoUsername = StringUtils.requireNotNullNorEmpty(accountTwoUsername, "accountTwoUsername must not be null nor empty");
this.accountTwoPassword = StringUtils.requireNotNullNorEmpty(accountTwoPassword, "accountTwoPasswordmust not be null nor empty");
this.accountThreeUsername = StringUtils.requireNotNullNorEmpty(accountThreeUsername, "accountThreeUsername must not be null nor empty");
this.accountThreePassword = StringUtils.requireNotNullNorEmpty(accountThreePassword, "accountThreePassword must not be null nor empty");
return this;
}
public Builder setUserHostnamesAndPorts(String accountOneHostname, int accountOnePort,
String accountTwoHostname, int accountTwoPort,
String accountThreeHostname, int accountThreePort) {
this.accountOneHostname = accountOneHostname;
this.accountOnePort = accountOnePort;
this.accountTwoHostname = accountTwoHostname;
this.accountTwoPort = accountTwoPort;
this.accountThreeHostname = accountThreeHostname;
this.accountThreePort = accountThreePort;
return this;
}
public Builder setServiceTlsPin(String tlsPin) {
this.serviceTlsPin = tlsPin;
return this;
}
public Builder setSecurityMode(String securityModeString) {
if (securityModeString != null) {
securityMode = SecurityMode.valueOf(securityModeString);
}
else {
securityMode = SecurityMode.required;
}
return this;
}
public Builder setReplyTimeout(String timeout) {
if (timeout != null) {
replyTimeout = Integer.valueOf(timeout);
}
return this;
}
@SuppressWarnings("fallthrough")
public Builder setDebugger(String debuggerString) {
if (debuggerString == null) {
return this;
}
switch (debuggerString) {
case "false": // For backwards compatibility settings with previous boolean setting.
LOGGER.warning("Debug string \"" + debuggerString + "\" is deprecated, please use \"none\" instead");
case "none":
debugger = Debugger.none;
break;
case "true": // For backwards compatibility settings with previous boolean setting.
LOGGER.warning("Debug string \"" + debuggerString + "\" is deprecated, please use \"console\" instead");
case "console":
debugger = Debugger.console;
break;
case "enhanced":
debugger = Debugger.enhanced;
break;
default:
throw new IllegalArgumentException("Unrecognized debugger string: " + debuggerString);
}
return this;
}
public Builder setEnabledTests(String enabledTestsString) {
enabledTests = getTestSetFrom(enabledTestsString);
return this;
}
public Builder setDisabledTests(String disabledTestsString) {
disabledTests = getTestSetFrom(disabledTestsString);
return this;
}
public Builder addTestPackages(String testPackagesString) {
if (testPackagesString != null) {
String[] testPackagesArray = testPackagesString.split(",");
ensureTestPackagesIsSet(testPackagesArray.length);
for (String s : testPackagesArray) {
testPackages.add(s.trim());
}
}
return this;
}
public Builder addTestPackages(String[] testPackagesString) {
if (testPackagesString == null) {
return this;
}
ensureTestPackagesIsSet(testPackagesString.length);
for (String testPackage : testPackagesString) {
testPackages.add(testPackage);
}
return this;
}
public Configuration build() throws KeyManagementException, NoSuchAlgorithmException {
return new Configuration(service, serviceTlsPin, securityMode, replyTimeout, debugger,
accountOneUsername, accountOnePassword, accountOneHostname, accountOnePort,
accountTwoUsername, accountTwoPassword, accountTwoHostname, accountTwoPort,
accountThreeUsername, accountThreePassword, accountThreeHostname, accountThreePort,
enabledTests, disabledTests, testPackages,
adminAccountUsername, adminAccountPassword, adminAccountHostname, adminAccountPort);
}
}
private static final String SINTTEST = "sinttest.";
public static Configuration newConfiguration(String[] testPackages)
throws IOException, KeyManagementException, NoSuchAlgorithmException {
Properties properties = new Properties();
File propertiesFile = findPropertiesFile();
if (propertiesFile != null) {
try (FileInputStream in = new FileInputStream(propertiesFile)) {
properties.load(in);
}
}
// Properties set via the system override the file properties
Properties systemProperties = System.getProperties();
for (Entry<Object, Object> entry : systemProperties.entrySet()) {
String key = (String) entry.getKey();
if (!key.startsWith(SINTTEST)) {
continue;
}
key = key.substring(SINTTEST.length());
String value = (String) entry.getValue();
properties.put(key, value);
}
Builder builder = builder();
builder.setService(properties.getProperty("service"));
builder.setServiceTlsPin(properties.getProperty("serviceTlsPin"));
builder.setSecurityMode(properties.getProperty("securityMode"));
builder.setReplyTimeout(properties.getProperty("replyTimeout", "60000"));
String adminAccountUsername = properties.getProperty("adminAccountUsername");
String adminAccountPassword = properties.getProperty("adminAccountPassword");
if (StringUtils.isNotEmpty(adminAccountUsername, adminAccountPassword)) {
builder.setAdminAccountUsernameAndPassword(adminAccountUsername, adminAccountPassword);
}
String adminAccountHostname = properties.getProperty("adminAccountHostname");
int adminAccountPort = getIntProperty(properties, "adminAccountPort", 0);
if (StringUtils.isNotEmpty(adminAccountHostname)) {
builder.setAdminHostnameAndPort(adminAccountHostname, adminAccountPort);
}
String accountOneUsername = properties.getProperty("accountOneUsername");
String accountOnePassword = properties.getProperty("accountOnePassword");
String accountTwoUsername = properties.getProperty("accountTwoUsername");
String accountTwoPassword = properties.getProperty("accountTwoPassword");
String accountThreeUsername = properties.getProperty("accountThreeUsername");
String accountThreePassword = properties.getProperty("accountThreePassword");
if (accountOneUsername != null || accountOnePassword != null || accountTwoUsername != null
|| accountTwoPassword != null || accountThreeUsername != null || accountThreePassword != null) {
builder.setUsernamesAndPassword(accountOneUsername, accountOnePassword, accountTwoUsername,
accountTwoPassword, accountThreeUsername, accountThreePassword);
}
String accountOneHostname = properties.getProperty("accountOneHostname");
int accountOnePort = getIntProperty(properties, "accountOnePort", 0);
String accountTwoHostname = properties.getProperty("accountTwoHostname");
int accountTwoPort = getIntProperty(properties, "accountTwoPort", 0);
String accountThreeHostname = properties.getProperty("accountThreeHostname");
int accountThreePort = getIntProperty(properties, "accountThreePort", 0);
if (accountOneHostname != null || accountTwoHostname != null || accountThreeHostname != null) {
builder.setUserHostnamesAndPorts(
accountOneHostname, accountOnePort,
accountTwoHostname, accountTwoPort,
accountThreeHostname, accountThreePort);
}
String debugString = properties.getProperty("debug");
if (debugString != null) {
LOGGER.warning("Usage of depreacted 'debug' option detected, please use 'debugger' instead");
builder.setDebugger(debugString);
}
builder.setDebugger(properties.getProperty("debugger"));
builder.setEnabledTests(properties.getProperty("enabledTests"));
builder.setDisabledTests(properties.getProperty("disabledTests"));
builder.addTestPackages(properties.getProperty("testPackages"));
builder.addTestPackages(testPackages);
return builder.build();
}
private static File findPropertiesFile() {
List<String> possibleLocations = new LinkedList<>();
possibleLocations.add("properties");
String userHome = System.getProperty("user.home");
if (userHome != null) {
possibleLocations.add(userHome + "/.config/smack-integration-test/properties");
}
for (String possibleLocation : possibleLocations) {
File res = new File(possibleLocation);
if (res.isFile())
return res;
}
return null;
}
private static Set<String> getTestSetFrom(String string) {
if (string == null) {
return null;
}
String[] stringArray = string.split(",");
Set<String> res = new HashSet<>(stringArray.length);
for (String s : stringArray) {
res.add(getFullTestStringFrom(s));
}
return res;
}
private static String getFullTestStringFrom(String string) {
string = string.trim();
if (string.startsWith("smackx.") || string.startsWith("smack.")) {
string = "org.jivesoftware." + string;
}
return string;
}
private static int getIntProperty(Properties properties, String propertyName, int defaultValue) {
String s = properties.getProperty(propertyName, Integer.toString(defaultValue));
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
LOGGER.log(Level.WARNING, "Could not parse value of property " + propertyName +
". Using default value " + defaultValue + " instead.");
return defaultValue;
}
}
}