package com.redhat.lightblue.client;
import com.redhat.lightblue.client.LightblueClientConfiguration.Compression;
import com.redhat.lightblue.client.MongoExecution.ReadPreference;
import org.apache.commons.lang.text.StrSubstitutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Properties;
/**
* Provides factory methods for
* {@link com.redhat.lightblue.client.LightblueClientConfiguration} that is
* derived from {@code .properties} files or {@link java.util.Properties}
* objects.
*
* <p>
* These properties files are read for specific keys for configuration:
*
* <dl>
* <dt>dataServiceURI</dt>
* <dd>The URL for the lightblue data service.</dd>
* <dt>metadataServiceURI</dt>
* <dd>The URL for the lightblue metadata service.</dd>
* <dt>useCertAuth</dt>
* <dd>Whether or not to use certificate authentication to talk to the lightblue
* services.</dd>
* <dt>caFilePath</dt>
* <dd>SSL certificate for talking with lightblue services.</dd>
* <dt>certFilePath</dt>
* <dd>The file path to the client certificate. This follows the semantics of
* {@link java.lang.ClassLoader#getResource(String)}, which is to say it is a
* relative, / separated path from the root of the classpath, and should
* <em>not</em> start with a forward slash.</dd>
* <dt>certPassword</dt>
* <dd>The password for the client certificate.</dd>
* <dt>certAlias</dt>
* <dd>The alias for the client certificate. ???</dd>
* </dl>
*/
public final class PropertiesLightblueClientConfiguration {
public static final String DEFAULT_CONFIG_FILE = "lightblue-client.properties";
private static final String DATA_SERVICE_URI_KEY = "dataServiceURI";
private static final String METADATA_SERVICE_URI_KEY = "metadataServiceURI";
private static final String ACCEPT_SELF_SIGNED_CERT_KEY = "acceptSelfSignedCert";
private static final String USE_CERT_AUTH_KEY = "useCertAuth";
private static final String CA_FILE_PATH_KEY = "caFilePath";
private static final String CERT_FILE_PATH_KEY = "certFilePath";
private static final String CERT_PASSWORD_KEY = "certPassword";
private static final String COMPRESSION = "compression";
private static final String BASIC_AUTH_USERNAME_KEY = "basicAuthUsername";
private static final String BASIC_AUTH_PASSWORD_KEY = "basicAuthPassword";
private static final String MONGO_READ_PREFERENCE = "execution.mongo.readPreference";
private static final String MONGO_WRITE_CONCERN = "execution.mongo.writeConcern";
private static final String MONGO_MAX_QUERY_TIME_MS = "execution.mongo.maxQueryTimeMS";
private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesLightblueClientConfiguration.class);
/**
* Assumes a lightblue-client.properties file at the root of the classpath.
*
* <p>
* For client configuration property keys, see
* {@link com.redhat.lightblue.client.PropertiesLightblueClientConfiguration}.
*/
public static LightblueClientConfiguration fromDefault() {
return fromResource(DEFAULT_CONFIG_FILE);
}
/**
* Returns a resource found using the current thread's context class loader.
*
* <p>
* For client configuration property keys, see
* {@link com.redhat.lightblue.client.PropertiesLightblueClientConfiguration}.
*
* @param resourcePath Follows the semantics of
* {@link java.lang.ClassLoader#getResourceAsStream(String)}, which is to
* say it is a relative / separated path from the root of the class path and
* should <em>not</em> start with a forward slash (/).
*
* @see Thread#currentThread()
* @see Thread#getContextClassLoader()
*/
public static LightblueClientConfiguration fromResource(String resourcePath) {
return fromResource(resourcePath, Thread.currentThread().getContextClassLoader());
}
/**
* For client configuration property keys, see
* {@link com.redhat.lightblue.client.PropertiesLightblueClientConfiguration}.
*
* @param resourcePath Follows the semantics of
* {@link java.lang.ClassLoader#getResourceAsStream(String)}, which is to
* say it is a relative / separated path from the root of the class path and
* should <em>not</em> start with a forward slash (/).
* @param classLoader The class loader to use to find the resource.
*/
public static LightblueClientConfiguration fromResource(String resourcePath,
ClassLoader classLoader) {
InputStream propertiesStream = classLoader.getResourceAsStream(resourcePath);
if (propertiesStream == null) {
LOGGER.error("Could not find properties resource at " + resourcePath);
throw new LightblueClientConfigurationException("Could not find properties resource "
+ "at " + resourcePath);
}
return fromInputStream(propertiesStream);
}
/**
* For client configuration property keys, see
* {@link com.redhat.lightblue.client.PropertiesLightblueClientConfiguration}.
*
* @param pathToProperties A file system path, relative to the working
* directory of the java process. If the specified path has no leading
* directories, try to look for it in the classpath instead
*/
public static LightblueClientConfiguration fromPath(Path pathToProperties) {
try {
//If specified path has no leading directories, look for the correponding file on the classpath
if(pathToProperties.getFileName().toString().equals(pathToProperties.toString())) {
return fromResource(pathToProperties.toString());
} else {
InputStream inStream = Files.newInputStream(pathToProperties);
return fromInputStream(inStream);
}
} catch (IOException e) {
LOGGER.error(pathToProperties + " could not be found/read", e);
throw new LightblueClientConfigurationException("Could not read properties file from "
+ "path, " + pathToProperties, e);
}
}
/**
* For client configuration property keys, see
* {@link com.redhat.lightblue.client.PropertiesLightblueClientConfiguration}.
*/
public static LightblueClientConfiguration fromInputStream(InputStream propertiesStream) {
try {
Properties properties = new Properties();
properties.load(loadInputStream(propertiesStream));
return fromObject(properties);
} catch (IOException e) {
LOGGER.error(propertiesStream + " could not be read", e);
throw new LightblueClientConfigurationException("Could not read properties file from "
+ "input stream, " + propertiesStream, e);
}
}
/**
* Reads the {@link InputStream} and substitutes system properties.
*
* @return {@link Reader}
*/
private static Reader loadInputStream(InputStream propertiesStream) throws IOException {
StringBuilder buff = new StringBuilder();
try (InputStreamReader isr = new InputStreamReader(propertiesStream, Charset.defaultCharset());
BufferedReader reader = new BufferedReader(isr)) {
String line;
while ((line = reader.readLine()) != null) {
buff.append(line).append("\n");
}
}
return new StringReader(StrSubstitutor.replaceSystemProperties(buff.toString()));
}
/**
* For client configuration property keys, see
* {@link com.redhat.lightblue.client.PropertiesLightblueClientConfiguration}.
*/
public static LightblueClientConfiguration fromObject(Properties properties) {
LightblueClientConfiguration config = new LightblueClientConfiguration();
config.setCaFilePath(properties.getProperty(CA_FILE_PATH_KEY));
config.setCertFilePath(properties.getProperty(CERT_FILE_PATH_KEY));
config.setCertPassword(properties.getProperty(CERT_PASSWORD_KEY));
config.setDataServiceURI(properties.getProperty(DATA_SERVICE_URI_KEY));
config.setMetadataServiceURI(properties.getProperty(METADATA_SERVICE_URI_KEY));
config.setAcceptSelfSignedCert(Boolean.parseBoolean(properties.getProperty(ACCEPT_SELF_SIGNED_CERT_KEY)));
config.setUseCertAuth(Boolean.parseBoolean(properties.getProperty(USE_CERT_AUTH_KEY)));
config.setBasicAuthUsername(properties.getProperty(BASIC_AUTH_USERNAME_KEY));
config.setBasicAuthPassword(properties.getProperty(BASIC_AUTH_PASSWORD_KEY));
if (properties.containsKey(COMPRESSION)) {
config.setCompression(Compression.parseCompression(properties.getProperty(COMPRESSION)));
}
if (properties.containsKey(MONGO_READ_PREFERENCE)) {
config.setReadPreference(ReadPreference.valueOf(
properties.getProperty(MONGO_READ_PREFERENCE)));
}
if (properties.containsKey(MONGO_WRITE_CONCERN)) {
config.setWriteConcern(properties.getProperty(MONGO_WRITE_CONCERN));
}
if (properties.containsKey(MONGO_MAX_QUERY_TIME_MS)) {
config.setMaxQueryTimeMS(Integer.parseInt(properties.getProperty(MONGO_MAX_QUERY_TIME_MS)));
}
return config;
}
private PropertiesLightblueClientConfiguration() {
}
}