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

com.datastax.oss.driver.internal.core.config.typesafe.TypesafeDriverConfig Maven / Gradle / Ivy

The newest version!
/*
 * 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.datastax.oss.driver.internal.core.config.typesafe;

import static com.typesafe.config.ConfigValueType.OBJECT;

import com.datastax.oss.driver.api.core.config.DriverConfig;
import com.datastax.oss.driver.api.core.config.DriverExecutionProfile;
import com.datastax.oss.driver.api.core.config.DriverOption;
import com.datastax.oss.driver.internal.core.util.Loggers;
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigObject;
import com.typesafe.config.ConfigOrigin;
import com.typesafe.config.ConfigOriginFactory;
import com.typesafe.config.ConfigValue;
import com.typesafe.config.ConfigValueFactory;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.net.URL;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import net.jcip.annotations.ThreadSafe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@ThreadSafe
public class TypesafeDriverConfig implements DriverConfig {

  private static final Logger LOG = LoggerFactory.getLogger(TypesafeDriverConfig.class);
  private static final ConfigOrigin DEFAULT_OVERRIDES_ORIGIN =
      ConfigOriginFactory.newSimple("default was overridden programmatically");

  private final ImmutableMap profiles;
  // Only used to detect if reload saw any change
  private volatile Config lastLoadedConfig;

  private final Map defaultOverrides = new ConcurrentHashMap<>();

  private final TypesafeDriverExecutionProfile.Base defaultProfile;

  public TypesafeDriverConfig(Config config) {
    this.lastLoadedConfig = config;
    Map profileConfigs = extractProfiles(config);

    ImmutableMap.Builder builder =
        ImmutableMap.builder();
    for (Map.Entry entry : profileConfigs.entrySet()) {
      builder.put(
          entry.getKey(),
          new TypesafeDriverExecutionProfile.Base(entry.getKey(), entry.getValue()));
    }
    this.profiles = builder.build();
    this.defaultProfile = profiles.get(DriverExecutionProfile.DEFAULT_NAME);
  }

  /** @return whether the configuration changed */
  public boolean reload(Config config) {
    config = applyDefaultOverrides(config);
    if (config.equals(lastLoadedConfig)) {
      return false;
    } else {
      lastLoadedConfig = config;
      try {
        Map profileConfigs = extractProfiles(config);
        for (Map.Entry entry : profileConfigs.entrySet()) {
          String profileName = entry.getKey();
          TypesafeDriverExecutionProfile.Base profile = this.profiles.get(profileName);
          if (profile == null) {
            LOG.warn(
                "Unknown profile '{}' while reloading configuration. "
                    + "Adding profiles at runtime is not supported.",
                profileName);
          } else {
            profile.refresh(entry.getValue());
          }
        }
        return true;
      } catch (Throwable t) {
        Loggers.warnWithException(LOG, "Error reloading configuration, keeping previous one", t);
        return false;
      }
    }
  }

  /*
   * Processes the raw configuration to extract profiles. For example:
   *     {
   *         foo = 1, bar = 2
   *         profiles {
   *             custom1 { bar = 3 }
   *         }
   *     }
   * Would produce:
   *     "default" => { foo = 1, bar = 2 }
   *     "custom1" => { foo = 1, bar = 3 }
   */
  private Map extractProfiles(Config sourceConfig) {
    ImmutableMap.Builder result = ImmutableMap.builder();

    Config defaultProfileConfig = sourceConfig.withoutPath("profiles");
    result.put(DriverExecutionProfile.DEFAULT_NAME, defaultProfileConfig);

    // The rest of the method is a bit confusing because we navigate between Typesafe config's two
    // APIs, see https://github.com/typesafehub/config#understanding-config-and-configobject
    // In an attempt to clarify:
    //    xxxObject = `ConfigObject` API (config as a hierarchical structure)
    //    xxxConfig = `Config` API (config as a flat set of options with hierarchical paths)
    ConfigObject rootObject = sourceConfig.root();
    if (rootObject.containsKey("profiles") && rootObject.get("profiles").valueType() == OBJECT) {
      ConfigObject profilesObject = (ConfigObject) rootObject.get("profiles");
      for (String profileName : profilesObject.keySet()) {
        if (profileName.equals(DriverExecutionProfile.DEFAULT_NAME)) {
          throw new IllegalArgumentException(
              String.format(
                  "Can't have %s as a profile name because it's used internally. Pick another name.",
                  profileName));
        }
        ConfigValue profileObject = profilesObject.get(profileName);
        if (profileObject.valueType() == OBJECT) {
          Config profileConfig = ((ConfigObject) profileObject).toConfig();
          result.put(profileName, profileConfig.withFallback(defaultProfileConfig));
        }
      }
    }
    return result.build();
  }

  @Override
  public DriverExecutionProfile getDefaultProfile() {
    return defaultProfile;
  }

  @NonNull
  @Override
  public DriverExecutionProfile getProfile(@NonNull String profileName) {
    if (profileName.equals(DriverExecutionProfile.DEFAULT_NAME)) {
      return defaultProfile;
    }
    return Optional.ofNullable(profiles.get(profileName))
        .orElseThrow(
            () ->
                new IllegalArgumentException(
                    String.format("Unknown profile '%s'. Check your configuration.", profileName)));
  }

  @NonNull
  @Override
  public Map getProfiles() {
    return profiles;
  }

  /**
   * Replace the given options, only if the original values came from {@code
   * reference.conf}: if the option was set explicitly in {@code application.conf}, then the
   * override is ignored.
   *
   * 

The overrides are also taken into account in profiles, and survive reloads. If this method * is invoked multiple times, the last value for each option will be used. Note that it is * currently not possible to use {@code null} as a value. */ public void overrideDefaults(@NonNull Map overrides) { defaultOverrides.putAll(overrides); reload(lastLoadedConfig); } private Config applyDefaultOverrides(Config source) { Config result = source; for (Map.Entry entry : defaultOverrides.entrySet()) { String path = entry.getKey().getPath(); Object value = entry.getValue(); if (isDefault(source, path)) { LOG.debug("Replacing default value for {} by {}", path, value); result = result.withValue( path, ConfigValueFactory.fromAnyRef(value).withOrigin(DEFAULT_OVERRIDES_ORIGIN)); } else { LOG.debug( "Ignoring default override for {} because the user has overridden the value", path); } } return result; } // Whether the value in the given path comes from the reference.conf in the driver JAR. private static boolean isDefault(Config config, String path) { if (!config.hasPath(path)) { return false; } ConfigOrigin origin = config.getValue(path).origin(); if (origin.equals(DEFAULT_OVERRIDES_ORIGIN)) { // Same default was overridden twice, should use the last value return true; } URL url = origin.url(); return url != null && url.toString().endsWith("reference.conf"); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy