All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.api.ads.common.lib.conf.ConfigurationHelper Maven / Gradle / Ivy

// Copyright 2012 Google Inc. All Rights Reserved.
//
// 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 com.google.api.ads.common.lib.conf;

import com.google.api.ads.common.lib.utils.logging.AdsServiceLoggers;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import java.io.File;
import java.net.URL;
import java.security.AccessControlException;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import javax.annotation.Nullable;
import org.apache.commons.configuration.AbstractConfiguration;
import org.apache.commons.configuration.CombinedConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.MapConfiguration;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.tree.OverrideCombiner;

/**
 * Helper class that loads {@link Configuration} from various sources.
 */
public class ConfigurationHelper {

  /**
   * Loads configuration from a specified path. If not absolute, will look in
   * the user home directory, the current classpath and the system classpath.
   * Absolute classpath references will not work.
   *
   * @param path the path to try first as a resource, then as a file
   * @throws ConfigurationLoadException if the configuration could not be
   *         loaded.
   * @returns properties loaded from the specified path or null.
   */
  public Configuration fromFile(String path) throws ConfigurationLoadException {
    PropertiesConfiguration propertiesConfiguration =
        setupConfiguration(new PropertiesConfiguration());
    propertiesConfiguration.setFileName(path);
    try {
      propertiesConfiguration.load();
    } catch (ConfigurationException e) {
      if (Throwables.getRootCause(e) instanceof AccessControlException){
        AdsServiceLoggers.ADS_API_LIB_LOG.debug("Properties could not be loaded.", e);
      } else {
        throw new ConfigurationLoadException(
            "Encountered a problem reading the provided configuration file \"" + path + "\"!", e);
      }
    }
    return propertiesConfiguration;
  }

  /**
   * Loads configuration from a specified path.
   *
   * @param path the path to try first as a resource, then as a file
   * @throws ConfigurationLoadException if the configuration could not be
   *         loaded.
   * @returns properties loaded from the specified path or null.
   */
  public Configuration fromFile(File path) throws ConfigurationLoadException {
    PropertiesConfiguration configuration = setupConfiguration(new PropertiesConfiguration());
    configuration.setFile(path);
    try {
      configuration.load();
      return configuration;
    } catch (ConfigurationException e) {
      throw new ConfigurationLoadException(
          "Encountered a problem reading the provided configuration file \"" + path + "\"!", e);
    }
  }

  /**
   * Loads configuration from a specified path.
   *
   * @param path the path to try first as a resource, then as a file
   * @throws ConfigurationLoadException if the configuration could not be
   *         loaded.
   * @returns properties loaded from the specified path or null.
   */
  public Configuration fromFile(URL path) throws ConfigurationLoadException {
    PropertiesConfiguration configuration = setupConfiguration(new PropertiesConfiguration());
    configuration.setURL(path);
    try {
      configuration.load();
      return configuration;
    } catch (ConfigurationException e) {
      throw new ConfigurationLoadException(
          "Encountered a problem reading the provided configuration file \"" + path + "\"!", e);
    }
  }

  /**
   * Loads configuration from system defined arguments, i.e. -Dapi.x.y.z=abc.
   */
  public Configuration fromSystem() {
    MapConfiguration mapConfig =
        setupConfiguration(new MapConfiguration((Properties) System.getProperties().clone()));
    // Disables trimming so system properties that include whitespace (such as line.separator) will
    // be preserved.
    mapConfig.setTrimmingDisabled(true);
    return mapConfig;
  }

  /**
   * Creates the combined configuration. System properties will overwrite path
   * properties, which overwrite URL properties. The configuration is created
   * by:
   * 
  1. Loading properties {@link #fromSystem() from the system}.
  2. *
  3. Loading properties from each path with lower indexed paths overwritting * higher indexed paths using {@link #fromFile(String)}.
  4. *
  5. Loading properties from each URL with lower indexed urls overwritting * higher indexed URLs using {@link #fromFile(URL)}.
* @throws ConfigurationLoadException if any required configuration could not * be loaded */ public CombinedConfiguration createCombinedConfiguration( @Nullable List> paths, @Nullable List> urls) throws ConfigurationLoadException { CombinedConfiguration combinedConfiguration = setupConfiguration(new CombinedConfiguration(new OverrideCombiner())); // System configuration will override all other configurations. addConfiguration(combinedConfiguration, fromSystem()); // Classpath and file path configurations will override URL configurations. if (paths != null) { for (ConfigurationInfo path : paths) { if (path != null && path.getLocation() != null) { try { addConfiguration(combinedConfiguration, fromFile(path.getLocation())); } catch (ConfigurationLoadException e) { if (!path.isOptional) { throw e; } else { // Intentionally exclude the exception details from this log message because: // a) The path is optional, so it's not unusual for the resource to be missing. // b) Logging the exception details was needlessly alarming users. See github issue: // https://github.com/googleads/googleads-java-lib/issues/90 AdsServiceLoggers.ADS_API_LIB_LOG.debug( "Could not load optional configuration: " + path); } } } } } if (urls != null) { for (ConfigurationInfo url : urls) { if (url != null && url.getLocation() != null) { try { addConfiguration(combinedConfiguration, fromFile(url.getLocation())); } catch (ConfigurationLoadException e) { if (!url.isOptional) { throw e; } else { AdsServiceLoggers.ADS_API_LIB_LOG.debug( "Did not load optional configuration" + url.getLocation() + ":", e); } } } } } return combinedConfiguration; } /** * Adds a configuration to the combined configuration, overwriting any * existing properties. */ @VisibleForTesting void addConfiguration(CombinedConfiguration combinedConfiguration, final Configuration configuration) { if (configuration instanceof AbstractConfiguration) { combinedConfiguration.addConfiguration((AbstractConfiguration) configuration); } else { combinedConfiguration.addConfiguration(setupConfiguration(new AbstractConfiguration() { @Override public boolean isEmpty() { return configuration.isEmpty(); } @Override public Object getProperty(String key) { return configuration.getProperty(key); } @Override @SuppressWarnings("rawtypes") // No rawtype in class. public Iterator getKeys() { return configuration.getKeys(); } @Override public boolean containsKey(String key) { return configuration.containsKey(key); } @Override protected void addPropertyDirect(String key, Object value) { configuration.addProperty(key, value); } })); } } /** * Creates a list of configuration infos from the locations and if they are * optional. A {@code null} locations will return {@code null} * * @param the type of location. Only {@code String} and {@code URL} are * supported. */ public static List> newList(@Nullable List locations, final boolean isOptional) { if (locations == null) { return null; } return Lists.transform(locations, input -> new ConfigurationInfo(input, isOptional)); } /** * Creates a list of configuration infos from the single location and if they are * optional. * * @param the type of location. Only {@code String} and {@code URL} are * supported. */ public static List> newList(boolean isOptional, T location) { if (location == null) { throw new IllegalArgumentException("location cannot be null"); } return newList(Collections.singletonList(location), isOptional); } /** * Sets attributes of the configuration to common values. Pass any Configuration objects * created by this helper to this method to ensure consistency. * * @param configuration the new configuration to set up * @return the same configuration that was passed, updated with common attribute values */ private C setupConfiguration(C configuration) { configuration.setListDelimiter(','); configuration.setDelimiterParsingDisabled(false); return configuration; } /** * Information about the configuration. * @param the location type */ public static class ConfigurationInfo { private final T location; private final boolean isOptional; /** * Constructor. * * @param location the location (String for file path or {@code URL}) of the * properties file * @param isOptional {@code true} if no exception should be thrown if it * can't be loaded. * @throws IllegalArgumentException if location is anything but {@code String} * or {@code URL}. */ public ConfigurationInfo(T location, boolean isOptional) { if (!(location instanceof String || location instanceof URL)) { throw new IllegalArgumentException("Type " + location.getClass() + " not supported as a configuration location."); } this.location = location; this.isOptional = isOptional; } public T getLocation() { return location; } public boolean isOptional() { return isOptional; } @Override public String toString() { return MoreObjects.toStringHelper(getClass()) .add("location", location) .add("isOptional", isOptional) .toString(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy