/** * Copyright 2014 VU University Medical Center. * Licensed under the Apache License version 2.0 (see http://www.apache.org/licenses/LICENSE-2.0.html). */ package nl.vumc.biomedbridges.galaxy.configuration; import com.github.jmchilton.blend4j.galaxy.GalaxyInstance; import com.github.jmchilton.blend4j.galaxy.GalaxyInstanceFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import nl.vumc.biomedbridges.core.Constants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Configuration class which gives access to the Galaxy instance URL and API key to be used. These properties are by * default read from the .blend.properties file in the user's home directory (System.getProperty("user.home")). It is * also possible to specify a custom properties file. * <p/> * See the Config.getBlendPropertiesFile method in the blend4j library for more details: * https://github.com/jmchilton/blend4j/blob/master/src/main/java/com/github/jmchilton/blend4j/Config.java * * @author <a href="mailto:f.debruijn@vumc.nl">Freek de Bruijn</a> * @author <a href="mailto:y.hoogstrate@erasmusmc.nl">Youri Hoogstrate</a> */ public class GalaxyConfiguration { /** * The system environment variable that can be set to specify the path where the blend4j properties file is. */ public static final String BLEND_PROPERTIES_DIRECTORY = "BLEND_PROPERTIES_DIRECTORY"; /** * The file name of the blend4j properties file. */ public static final String BLEND_PROPERTIES_FILE_NAME = ".blend.properties"; /** * The property key for the Galaxy instance in the configuration string or .blend.properties file. */ public static final String GALAXY_INSTANCE_PROPERTY_KEY = "test.galaxy.instance"; /** * The property key for the API key in the configuration string or .blend.properties file. */ public static final String API_KEY_PROPERTY_KEY = "test.galaxy.key"; /** * The property key for the API key in the configuration string or .blend.properties file. */ public static final String HISTORY_NAME_PROPERTY_KEY = "galaxy.history.name"; /** * The separator between key and value of a configuration property. */ public static final String KEY_VALUE_SEPARATOR = "="; /** * The separator between configuration properties. */ public static final String PROPERTY_SEPARATOR = "|"; /** * The logger for this class. */ private static final Logger logger = LoggerFactory.getLogger(GalaxyConfiguration.class); /** * The blend(4j) properties. These are loaded when the first property is retrieved or when an explicit properties * file path is set. */ private Properties properties; /** * The path of the properties file. */ private String propertiesFilePath; /** * The Galaxy server URL. */ private String galaxyInstanceUrl; /** * The Galaxy API key. */ private String apiKey; /** * The name of the history to run the workflow in. */ private String historyName; /** * Whether debugging information should be printed. */ private boolean debug; /** * Construct a Galaxy configuration object, using the default properties file. */ public GalaxyConfiguration() { this(null); } /** * Construct a Galaxy configuration object. * * @param propertiesFilePath the path of the properties file or null to postpone reading the properties. */ public GalaxyConfiguration(final String propertiesFilePath) { if (propertiesFilePath != null) { this.propertiesFilePath = propertiesFilePath; loadProperties(); this.galaxyInstanceUrl = getGalaxyInstanceUrl(); this.apiKey = getGalaxyApiKey(); this.historyName = getGalaxyHistoryName(); } else if (System.getenv(BLEND_PROPERTIES_DIRECTORY) != null) this.propertiesFilePath = System.getenv(BLEND_PROPERTIES_DIRECTORY) + File.separator + BLEND_PROPERTIES_FILE_NAME; else this.propertiesFilePath = System.getProperty("user.home") + File.separator + BLEND_PROPERTIES_FILE_NAME; } /** * Build a configuration string from a Galaxy instance URL, an API key, and a history name. * * @param galaxyInstanceUrl the Galaxy instance URL. * @param galaxyApiKey the Galaxy API key or null to retrieve the key from the properties file. * @param galaxyHistoryName the Galaxy history name. * @return the configuration string. */ public String buildConfiguration(final String galaxyInstanceUrl, final String galaxyApiKey, final String galaxyHistoryName) { this.galaxyInstanceUrl = galaxyInstanceUrl; this.apiKey = (galaxyApiKey != null) ? galaxyApiKey : getGalaxyApiKey(); this.historyName = galaxyHistoryName; return GALAXY_INSTANCE_PROPERTY_KEY + KEY_VALUE_SEPARATOR + galaxyInstanceUrl + PROPERTY_SEPARATOR + API_KEY_PROPERTY_KEY + KEY_VALUE_SEPARATOR + galaxyApiKey + PROPERTY_SEPARATOR + HISTORY_NAME_PROPERTY_KEY + KEY_VALUE_SEPARATOR + galaxyHistoryName; } /** * Build a configuration string from a Galaxy instance URL, the default API key and the default history name. * * @param galaxyInstanceUrl the Galaxy instance URL. * @return the configuration string. */ public String buildConfiguration(final String galaxyInstanceUrl) { this.galaxyInstanceUrl = galaxyInstanceUrl; return buildConfiguration(galaxyInstanceUrl, getGalaxyApiKey(), getGalaxyHistoryName()); } /** * Set whether debugging information should be printed. * * @param debug whether debugging information should be printed. * @return this Galaxy configuration (to make initialization easy). */ public GalaxyConfiguration setDebug(final boolean debug) { this.debug = debug; return this; } /** * Create and configure a Galaxy instance object. * * todo: create a getGalaxyInstance method that handles the configurationData == null case? And cache the object? * * @param configurationData the configuration string or null to use the settings from the properties file. * @return a Galaxy instance object or null. */ public GalaxyInstance determineGalaxyInstance(final String configurationData) { String message = null; if (configurationData != null) { final String instancePrefix = GALAXY_INSTANCE_PROPERTY_KEY + KEY_VALUE_SEPARATOR; final String apiKeyPrefix = API_KEY_PROPERTY_KEY + KEY_VALUE_SEPARATOR; final String historyNamePrefix = HISTORY_NAME_PROPERTY_KEY + KEY_VALUE_SEPARATOR; if (configurationData.contains(PROPERTY_SEPARATOR) && configurationData.contains(instancePrefix) && configurationData.contains(apiKeyPrefix)) message = processConfigurationProperties(configurationData, instancePrefix, apiKeyPrefix, historyNamePrefix); else message = String.format("Expected properties were not found in configuration data %s.", configurationData); if (message != null) message += String.format(" Please specify: %s[Galaxy server URL]%s%s[API key]", instancePrefix, PROPERTY_SEPARATOR, apiKeyPrefix); } final boolean validConfiguration = checkConfiguration(configurationData, message); return validConfiguration ? GalaxyInstanceFactory.get(galaxyInstanceUrl, apiKey, debug) : null; } /** * Check the configuration and log any errors. * * @param configurationData the configuration string or null to use the settings from the properties file. * @param initialMessage the message thus far (from previous checking). * @return whether the configuration is valid. */ private boolean checkConfiguration(final String configurationData, final String initialMessage) { String message = initialMessage; final boolean instanceNull = galaxyInstanceUrl == null; final boolean keyNull = apiKey == null; if (configurationData == null && (instanceNull || keyNull)) { message = instanceNull ? "The Galaxy server URL was not found in the settings. " : ""; message += keyNull ? "The API key was not found in the settings." : ""; } if (message != null) logger.error(message.trim()); return message == null; } /** * Process all configuration properties. * * @param configurationData the configuration data. * @param instancePrefix the Galaxy instance property prefix. * @param apiKeyPrefix the api key property prefix. * @param historyNamePrefix the history name property prefix. * @return the logging message or null if there is nothing to log. */ private String processConfigurationProperties(final String configurationData, final String instancePrefix, final String apiKeyPrefix, final String historyNamePrefix) { String message = null; boolean instanceFound = false; boolean apiKeyFound = false; for (final String propertyDefinition : configurationData.split("\\|")) if (propertyDefinition.startsWith(instancePrefix)) { galaxyInstanceUrl = propertyDefinition.substring(propertyDefinition.indexOf('=') + 1); instanceFound = true; logger.trace("Read property Galaxy instance URL: {}.", galaxyInstanceUrl); } else if (propertyDefinition.startsWith(apiKeyPrefix)) { apiKey = propertyDefinition.substring(propertyDefinition.indexOf('=') + 1); apiKeyFound = true; logger.trace("Read property Galaxy API key: {}.", apiKey); } else if (propertyDefinition.startsWith(historyNamePrefix)) { historyName = propertyDefinition.substring(propertyDefinition.indexOf('=') + 1); logger.trace("Read property Galaxy history name: {}.", historyName); } if (!instanceFound || !apiKeyFound) message = String.format("Not all expected properties (Galaxy instance and API key) were found in" + " configuration data %s.", configurationData); return message; } /** * Set the path of the properties file and load the properties. * * @param propertiesFilePath the path of the properties file. * @return null or an error message. */ public String setPropertiesFilePath(final String propertiesFilePath) { this.propertiesFilePath = propertiesFilePath; return loadProperties(); } /** * Get the Galaxy instance URL. * * @return the Galaxy instance URL. */ public String getGalaxyInstanceUrl() { return galaxyInstanceUrl != null ? galaxyInstanceUrl : getProperty(GALAXY_INSTANCE_PROPERTY_KEY); } /** * Get the Galaxy API key. * * @return the Galaxy API key. */ public String getGalaxyApiKey() { final String galaxyApiKey; if (apiKey != null) galaxyApiKey = apiKey; else { final String serverSpecificKey = getProperty(API_KEY_PROPERTY_KEY + "." + getGalaxyInstanceUrl()); if (serverSpecificKey != null) galaxyApiKey = serverSpecificKey; else { galaxyInstanceUrl = getProperty(GALAXY_INSTANCE_PROPERTY_KEY); galaxyApiKey = getProperty(API_KEY_PROPERTY_KEY); } } return galaxyApiKey; } /** * Get the Galaxy history name. * * @return the Galaxy history name. */ public String getGalaxyHistoryName() { return historyName != null ? historyName : getProperty(HISTORY_NAME_PROPERTY_KEY); } /** * Get a blend(4j) property. Initialize and load the properties when the first property is retrieved. * * @param key the property key. * @return the property value. */ private String getProperty(final String key) { if (properties == null) loadProperties(); String value = null; if (properties.containsKey(key)) value = properties.getProperty(key); return value; } /** * Load and check the properties. * * @return null or an error message. */ private String loadProperties() { String message = null; properties = new Properties(); final File propertiesFile = new File(propertiesFilePath); if (!propertiesFile.exists()) message = String.format("The properties file %s is not found.", propertiesFile.getAbsolutePath()); try (final InputStream inputStream = new FileInputStream(propertiesFile)) { properties.load(inputStream); } catch (final IOException e) { logger.error("Error loading properties from file {}.", propertiesFile.getAbsolutePath(), e); message = String.format("Error loading properties from file %s.", propertiesFile.getAbsolutePath()); } checkConfiguration(); return message; } /** * Check whether the properties return a non-null value and log errors if this is not the case. */ private void checkConfiguration() { if (getGalaxyInstanceUrl() == null || getGalaxyApiKey() == null) { if (!new File(propertiesFilePath).exists()) { logger.error("The configuration file '{}' was not found.", propertiesFilePath); logger.error("Please make sure a configuration file is available with the following properties:"); } else { logger.error("The configuration file '{}' was not read successfully.", propertiesFilePath); logger.error("Please ensure the following properties are available:"); } logger.error("{}={} [or another Galaxy server]", GALAXY_INSTANCE_PROPERTY_KEY, Constants.CENTRAL_GALAXY_URL); logger.error(API_KEY_PROPERTY_KEY + "=[32hex-characters]"); logger.error(""); logger.error("Optional property:"); logger.error(HISTORY_NAME_PROPERTY_KEY + "=Some History Name"); } } }