Java tutorial
/******************************************************************************* * Copyright (C) 2016 Black Duck Software, Inc. * http://www.blackducksoftware.com/ * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 com.blackducksoftware.tools.commonframework.core.config; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import org.apache.http.client.utils.URIBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.blackducksoftware.tools.commonframework.core.config.server.ServerBean; import com.blackducksoftware.tools.commonframework.core.config.server.ServerConfigurationParser; import com.blackducksoftware.tools.commonframework.core.config.user.CommonUser; import com.blackducksoftware.tools.commonframework.standard.protex.report.template.TemplateReader; /** * Abstract ConfigManager that either: <li>Loads basic properties used for most * projects. or <li>Accepts a user object with the necessary populated fields * * @author Ari Kamen * */ public abstract class ConfigurationManager extends ConfigConstants implements IConfigurationManager { /** The log. */ private final Logger log = LoggerFactory.getLogger(this.getClass().getName()); /** * For parsing, processing and retaining server beans and lists */ private ServerConfigurationParser serverConfigParser; private ProxyBean proxyBean; private SSOBean ssoBean; private String serverListLocation; // List of all the possible connections private List<ServerBean> serverList = new ArrayList<ServerBean>(); // Map of connections, used in the event where multipl connections are // specified in one config file. private final Map<APPLICATION, ServerBean> serverMap = new HashMap<APPLICATION, ServerBean>(); private EmailBean emailConfiguration; private NotificationRulesConfig notificationRulesConfig; // Derived from either the user or the API wrapper /** The server url. */ private String serverURL; private CommonUser user; /** The sdk time out. */ private Long sdkTimeOut; /** * CXF child element override */ private Long childElementCount; // Template mappings private final Map<String, String> mappings = new HashMap<String, String>(); /** The props. */ private ConfigurationProperties props = new ConfigurationProperties(); // IF the config is read from a file, the following two props // will be set, and the file may be modified, encrypting // passwords. See ConfigurationFile class header comment // for more details. // The location of this configuration file private String configFileLocation; private ConfigurationFile configFile; /** * Selects a component of a URL. */ public static enum URL_INFORMATION { /** The host. */ HOST, /** The port. */ PORT, /** The protocol. */ PROTOCOL }; /** * Default constructor. Used by utilities that set all options via the * command line. */ protected ConfigurationManager() { } /** * Extensible to provide custom properties. Use {@link} * {@link #getProperty(String)} method. Deprecated: Application Type no * longer needed * * @param configFileLocation * the config file location * @param applicationType * the application type {@link ConfigConstants.APPLICATION} * */ @Deprecated protected ConfigurationManager(final String configFileLocation, final APPLICATION applicationName) { this.configFileLocation = configFileLocation; loadPropertiesFromFile(configFileLocation); initApplication(); } /** * Extensible to provide custom properties. Use {@link} * {@link #getProperty(String)} method. * * @param configFileLocation */ protected ConfigurationManager(final String configFileLocation) { this.configFileLocation = configFileLocation; loadPropertiesFromFile(configFileLocation); initApplication(); } protected ConfigurationManager(final File configFile) { this.configFileLocation = configFile.getAbsolutePath(); loadPropertiesFromFile(configFile); initApplication(); } /** * Instantiates a new configuration manager. * * This constructor should be used only by tests, since it introduces some * risk of incorrect handling of special characters and escaped characters. * * @param props * the props * @param applicationType * the application type {@link ConfigConstants.APPLICATION} */ protected ConfigurationManager(final Properties props, final APPLICATION applicationName) { this.props = new ConfigurationProperties(); // start clean just in case this.props.addAll(props); initApplication(); } /** * Initializer for stream instead of specific file Used when file's * classpath is not obvious (web context). * * Deprecated: Use ConfigrationManger(File file) instead. The underlying * properties library (Apache Commons Configuration) cannot load from an * InputStream, so this constructor introduces extra processing of property * values as they are read, and therefore a slight risk of inconsistent * handling of escaped characters. * * @param is * the is * @param applicationType * the application type {@link ConfigConstants.APPLICATION} */ @Deprecated protected ConfigurationManager(final InputStream is, final APPLICATION applicationName) { loadPropertiesFromStream(is); initApplication(); } /** * CommonUser based instantiation Used by those applications that do not * have a property file. * * @param user * @param applicationName */ protected ConfigurationManager(final CommonUser user, final APPLICATION applicationName) { this.user = user; initApplication(); } /** * Load properties from file. * * @param configFileLocation * the config file location * @throws IOException * @throws FileNotFoundException */ private void loadPropertiesFromFile(final String configFileLocation) { configFile = new ConfigurationFile(configFileLocation); initProperties(); } private void loadPropertiesFromFile(final File configFile) { this.configFile = new ConfigurationFile(configFile); initProperties(); } private void initProperties() { props = this.configFile.getProperties(); configFile.saveWithEncryptedPasswords(); } /** * Load properties from stream. * * @param is * the is */ private void loadPropertiesFromStream(final InputStream is) { final Properties tempProps = new Properties(); try { tempProps.load(is); is.close(); } catch (final IOException e) { throw new IllegalArgumentException("Unable to load properties from stream!"); } props.addAll(tempProps); } /** * Initializes all the properties depending on APPLICATION name */ private void initApplication() { initCommonProperties(); final boolean processedList = processServerListConfiguration(); // For all possible conection types check to see if we can find // something for (final APPLICATION appType : APPLICATION.values()) { final ServerBean bean = processServerBeanConfiguration(appType, processedList); if (bean != null) { serverMap.put(appType, bean); } } if (serverMap.keySet().size() == 0) { throw new RuntimeException("No Suitable connections found"); // loadAppSpecificProperties(suppliedAppNamePropertyName); } } /** * Creates a server bean (or multiple) and populates the server bean map. If * the user specified a known application type (cc, protex, hub, etc) then * we add it to the map. If no suitable connections found, throw a fatal. * * @param appType * @param processedList */ private ServerBean processServerBeanConfiguration(final APPLICATION appType, final boolean processedList) { ServerBean sb = null; // If there is a processed list, then attempt to search by application // type if (processedList) { final List<ServerBean> filteredList = getServerListByApplication(appType); if (filteredList.size() == 0) { log.warn("Server list processed, but no configuration for application type: " + appType); } else { sb = filteredList.get(0); return sb; } } if (user != null) { // Check if user object exists sb = new ServerBean(user.getServer(), user.getUserName(), user.getPassword(), appType); } else { // If not, then populate the serverbean sb = createAppSpecificServerBean(appType); } if (sb == null) { log.debug("No configuration information for: " + appType); } else { log.debug("Server configuration available for: " + appType); } return sb; } /** * Optional properties that are end point agnostic. * * @param suppliedAppNamePropertyName */ protected void initCommonProperties() { // Potential serverList setServerListLocation(getOptionalProperty(SERVER_LIST_LOCATION)); // Optional email emailConfiguration = new EmailBean(); emailConfiguration.setSmtpAddress(getOptionalProperty(EMAIL_SMTP_ADDRESS)); emailConfiguration.setSmtpTo(getOptionalProperty(EMAIL_SMTP_TO_FIELD)); emailConfiguration.setSmtpFrom(getOptionalProperty(EMAIL_SMTP_FROM_FIELD)); emailConfiguration.setUseAuth(getOptionalProperty(EMAIL_SMTP_USE_AUTH, false, Boolean.class)); notificationRulesConfig = new NotificationRulesConfig( getOptionalProperty(EMAIL_TRIGGER_RULES, "", String.class)); // The messy work of interpreting the possibly-plain-text, // possibly-encrypted, possibly-base64-encoded password // is delegated to ConfigurationPassword final ConfigurationPassword configurationPassword = ConfigurationPassword .createFromProperty(props.getProperties(), EMAIL_AUTH_PASSWORD_PREFIX); emailConfiguration.setAuthPassword(configurationPassword.getPlainText()); emailConfiguration.setAuthUserName(getOptionalProperty(EMAIL_SMTP_AUTH_LOGIN, "", String.class)); emailConfiguration.setEmailProtocol(getOptionalProperty(EMAIL_PROTOCOL, "smtp", String.class)); emailConfiguration.setSmtpPort(getOptionalProperty(EMAIL_SMTP_PORT, 25, Integer.class)); // This could go into a separate ProxyObject proxyBean = new ProxyBean(); proxyBean.setProxyServer(getOptionalProperty(PROXY_SERVER)); proxyBean.setProxyPort(getOptionalProperty(PROXY_PORT)); proxyBean.setProxyServerHttps(getOptionalProperty(PROXY_HTTPS_SERVER)); proxyBean.setProxyPortHttps(getOptionalProperty(PROXY_HTTPS_PORT)); // Optional SSO related Information ssoBean = new SSOBean(); ssoBean.setAuthenticationMethods(getOptionalProperty(SSOBean.SSO_AUTH_METHODS)); ssoBean.setKeyStorePassword(getOptionalProperty(SSOBean.SSO_KEY_STORE_PASSWORD)); ssoBean.setKeyStorePath(getOptionalProperty(SSOBean.SSO_KEY_STORE_PATH)); ssoBean.setKeyStoreType(getOptionalProperty(SSOBean.SSO_KEY_STORE_TYPE)); ssoBean.setTrustStorePath(getOptionalProperty(SSOBean.SSO_TRUST_STORE_PATH)); ssoBean.setTrustStorePassword(getOptionalProperty(SSOBean.SSO_TRUST_STORE_PASSWORD)); ssoBean.setTrustStoreType(getOptionalProperty(SSOBean.SSO_TRUST_STORE_TYPE)); // Optional child element overrwrite final Integer childCount = getOptionalProperty(SDK_CHILD_COUNT, 50000, Integer.class); log.debug("Setting CXF timeout: " + childCount); setChildElementCount(new Long(childCount)); } /** * Based on specified application name we will extract the necessary * properties */ private ServerBean createAppSpecificServerBean(final APPLICATION suppliedApplication) { String propertyPrefix; String userName; String server; // We want to perform a small translation here. Most users 'cc' as a // prefix, so we map it here. if (suppliedApplication == APPLICATION.CODECENTER) { propertyPrefix = "cc"; } else { propertyPrefix = suppliedApplication.toString().toLowerCase(); } try { userName = getProperty(propertyPrefix + "." + GENERIC_USER_NAME_PROPERTY_SUFFIX); server = getProperty(propertyPrefix + "." + GENERIC_SERVER_NAME_PROPERTY_SUFFIX); } catch (final Exception e) { log.info("No connection information available for: " + suppliedApplication); return null; } // The messy work of interpreting the possibly-plain-text, // possibly-encrypted, possibly-base64-encoded password // is delegated to ConfigurationPassword final ConfigurationPassword configurationPassword = ConfigurationPassword .createFromProperty(props.getProperties(), propertyPrefix); final ServerBean serverBean = new ServerBean(server, userName, configurationPassword.getPlainText(), suppliedApplication); log.debug("Configured custom server bean: " + serverBean); return serverBean; } /** * Returns a list of ServerBeans based on Application Name only * * @param appName * @return * @throws Exception */ @Override public List<ServerBean> getServerListByApplication(final APPLICATION appName) throws IllegalArgumentException { if (serverConfigParser == null || serverList.isEmpty()) { throw new IllegalArgumentException( "Cannot retrieve server beans without proper server list processing!"); } return serverConfigParser.getServerListByApplication(appName); } /** * If the user provides a server list, process it here. * * @return the list */ protected boolean processServerListConfiguration() { final String serverListLocationStr = getServerListLocation(); if (serverListLocationStr == null) { log.warn("Server List location property exists, but empty"); return false; } try { File serverListLocation = null; if (configFileLocation == null) { log.warn("Loading server list using class loader"); serverListLocation = new File( this.getClass().getClassLoader().getResource(serverListLocationStr).getFile()); } else { log.info("Loading server configuration relative to configuration file: " + configFileLocation); final File configFileLocationPath = new File(configFileLocation); serverListLocation = new File( configFileLocationPath.getParent() + File.separator + serverListLocationStr); } if (serverListLocation.exists()) { serverConfigParser = new ServerConfigurationParser(serverListLocation.toString()); try { serverList = serverConfigParser.processServerConfiguration(); return true; } catch (final Exception e) { log.error("Unable to parse server file", e); throw new IllegalArgumentException("Unable to parse server file!" + e.getMessage()); } } else { throw new IllegalArgumentException("Server list location does not exist:" + serverListLocationStr); } } catch (final Exception e) { throw new IllegalArgumentException("Unable to load server list file", e); } } /** * Returns the value for the property resource loaded by the * ConfigurationManager. * * @param propertyKey * the property key * @return the property */ @Override public String getProperty(final String propertyKey) { String value = null; if (props.containsKey(propertyKey)) { value = props.getProperty(propertyKey); value = value.trim(); if (value == null || value.length() == 0) { throw new IllegalArgumentException("Value DNE for key: " + propertyKey); } value = value.trim(); } else { throw new IllegalArgumentException("Property key DNE: " + propertyKey); } return value; } /** * Check to make sure this UNsupported (obsolete) property is not set. If it * is set, throw an IllegalArgumentException. * * @param propertyKey * the property key * @return the property */ public void checkForUnsupportedProperty(final String propertyKey, final String newConfigMessage) { if (props.containsKey(propertyKey)) { throw new IllegalArgumentException( "Property key: " + propertyKey + " is no longer supported. " + newConfigMessage + ". Please refer to the documentation for the new configuration instructions."); } } /** * Retrieves optional properties and sets them to the config. * * @param propertyKey * the property key * @return the optional property */ @Override public String getOptionalProperty(final String propertyKey) { String value = null; if (props.containsKey(propertyKey)) { value = props.getProperty(propertyKey); value = value.trim(); } if (value == null) { log.debug("[Optional] property: " + propertyKey + ", is missing or blank"); } return value; } /** * Retrieves optional property, if property is missing returns default * value. * * @param <T> * @param key * @param defaultValue * @return * @return */ @SuppressWarnings("unchecked") @Override public <T> T getOptionalProperty(final String propertyKey, final T defaultValue, final Class<T> theClass) { final T classType = theClass.cast(defaultValue); String value = null; T returnValue = null; if (props.containsKey(propertyKey)) { String propStr = props.getProperty(propertyKey); if (propStr != null) { propStr = propStr.trim(); } value = propStr; } if (value == null) { value = defaultValue.toString(); } if (classType instanceof Boolean) { returnValue = (T) new Boolean(value); } else if (classType instanceof Integer) { returnValue = (T) new Integer(value); } else { returnValue = (T) new String(value); } return returnValue; } /** * Consumes user specified mappings from the property file. User must have * "template.mapping.x=column,value" specified for this to be populated. <br> * Used by {@link TemplateReader} * * @return the mappings * @throws IllegalArgumentException * the illegal argument exception */ @Override public Map<String, String> getMappings() throws IllegalArgumentException { @SuppressWarnings("unchecked") final Set<Object> keys = props.keySet(); for (final Object o : keys) { final String key = (String) o; if (key.startsWith(PROPERTY_TEMPLATE_MAPPING)) { final String mappingPair = props.getProperty(key); if (mappingPair != null) { final String[] keyValuePair = mappingPair.split(","); if (keyValuePair.length == 2) { final String columnKey = keyValuePair[0].trim(); final String mappingMethod = keyValuePair[1].trim(); // Check to make sure that if the Column exists, the // method is the same // Otherwise throw Exception alerting the user. // TODO: Figure out a way to allow user to map same // column names to different methods. if (mappings.containsKey(columnKey)) { final String existingMethod = mappings.get(columnKey); if (!mappingMethod.equals(existingMethod)) { throw new IllegalArgumentException( columnKey + " is mapped more than once to non-unique methods."); } } mappings.put(columnKey, mappingMethod); log.debug("Added value mapping for key" + mappingPair); } } } } return mappings; } /** * Gets the sdk time out. * * @return the sdk time out */ @Override public Long getSdkTimeOut() { return sdkTimeOut; } /** * Sets the sdk time out. * * @param sdkTimeOut * the new sdk time out */ @Override public void setSdkTimeOut(final Long sdkTimeOut) { this.sdkTimeOut = sdkTimeOut; } /** * Provides information from the server URL. * * @param urlInfo * the url info * @return the string */ @Override public String findURLInformation(final URL_INFORMATION urlInfo) { String returnString = null; try { final URIBuilder builder = new URIBuilder(serverURL); if (urlInfo == URL_INFORMATION.HOST) { returnString = builder.getHost(); } else if (urlInfo == URL_INFORMATION.PORT) { returnString = Integer.toString(builder.getPort()); } else if (urlInfo == URL_INFORMATION.PROTOCOL) { returnString = builder.getScheme(); } } catch (final Exception e) { log.warn("Unable to determine host name for: " + serverURL, e); } return returnString; } /** * Fully resolved URL, used to instantiate SDK Proxy Objects. * * @return the server url */ @Override public String getServerURL() { return serverURL; } /** * Sets the internal server URL so that it can determine host and port * information. * * @param serverURL * the new server url */ @Override public void setServerURL(final String serverURL) { this.serverURL = serverURL; } /** * Gets the props. * * @return the props */ @Override public Properties getProps() { return props.getProperties(); } /** * Gets the server list location. * * @return the server list location */ private String getServerListLocation() { return serverListLocation; } /** * Sets the server list location. * * @param serverListLocation * the new server list location */ private void setServerListLocation(final String serverListLocation) { this.serverListLocation = serverListLocation; } @Override public ServerBean getServerBean(final APPLICATION app) { return serverMap.get(app); } /** * Deprecated, use serverSerbean(bean, APPLICATION) * * @param serverBean */ @Override public List<ServerBean> getServerList() { return serverList; } /** * Allows the user to add server beans Useful in the case of a non-standard * configuration. * * @param bean */ @Override public void addServerBean(final ServerBean bean) { if (serverList != null) { serverList.add(bean); log.debug("Added bean : " + bean.toString()); log.debug("Server list size: " + serverList.size()); } } // /// EMAIL //// /** * Returns the email configuration object. This contains the required email * protocol information - SMTP address - SMTP TO - SMTP From * * @return */ @Override public EmailBean getEmailConfiguration() { return emailConfiguration; } public NotificationRulesConfig getNotificationRulesConfiguration() { return notificationRulesConfig; } public Long getChildElementCount() { return childElementCount; } public void setChildElementCount(final Long childElementCount) { this.childElementCount = childElementCount; } // SSO public SSOBean getSsoBean() { return ssoBean; } // Proxy public ProxyBean getProxyBean() { return proxyBean; } /** * Set the proxy bean explicitly, use only if Proxy settings are not being * derived by way of inputstream. * * @param pb */ public void setProxyBean(final ProxyBean pb) { this.proxyBean = pb; } }