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

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

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

import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
import com.datastax.oss.driver.api.core.config.DriverConfig;
import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
import com.datastax.oss.driver.api.core.config.DriverExecutionProfile;
import com.datastax.oss.driver.api.core.context.DriverContext;
import com.datastax.oss.driver.api.core.session.SessionBuilder;
import com.datastax.oss.driver.internal.core.config.ConfigChangeEvent;
import com.datastax.oss.driver.internal.core.context.EventBus;
import com.datastax.oss.driver.internal.core.context.InternalDriverContext;
import com.datastax.oss.driver.internal.core.util.Loggers;
import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures;
import com.datastax.oss.driver.internal.core.util.concurrent.RunOrSchedule;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigParseOptions;
import edu.umd.cs.findbugs.annotations.NonNull;
import com.datastax.oss.driver.shaded.netty.util.concurrent.EventExecutor;
import com.datastax.oss.driver.shaded.netty.util.concurrent.ScheduledFuture;
import java.io.File;
import java.net.URL;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import net.jcip.annotations.ThreadSafe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The default loader; it is based on Typesafe Config and optionally reloads at a configurable
 * interval.
 */
@ThreadSafe
public class DefaultDriverConfigLoader implements DriverConfigLoader {

  private static final Logger LOG = LoggerFactory.getLogger(DefaultDriverConfigLoader.class);

  public static final String DEFAULT_ROOT_PATH = "datastax-java-driver";

  public static final Supplier DEFAULT_CONFIG_SUPPLIER =
      () -> {
        ConfigFactory.invalidateCaches();
        // The thread's context class loader will be used for application classpath resources,
        // while the driver class loader will be used for reference classpath resources.
        return ConfigFactory.defaultOverrides()
            .withFallback(ConfigFactory.defaultApplication())
            .withFallback(ConfigFactory.defaultReference(CqlSession.class.getClassLoader()))
            .resolve()
            .getConfig(DEFAULT_ROOT_PATH);
      };

  @NonNull
  public static DefaultDriverConfigLoader fromClasspath(
      @NonNull String resourceBaseName, @NonNull ClassLoader appClassLoader) {
    return new DefaultDriverConfigLoader(
        () -> {
          ConfigFactory.invalidateCaches();
          Config config =
              ConfigFactory.defaultOverrides()
                  .withFallback(
                      ConfigFactory.parseResourcesAnySyntax(
                          resourceBaseName,
                          ConfigParseOptions.defaults().setClassLoader(appClassLoader)))
                  .withFallback(ConfigFactory.defaultReference(CqlSession.class.getClassLoader()))
                  .resolve();
          return config.getConfig(DEFAULT_ROOT_PATH);
        });
  }

  @NonNull
  public static DriverConfigLoader fromFile(@NonNull File file) {
    return new DefaultDriverConfigLoader(
        () -> {
          ConfigFactory.invalidateCaches();
          Config config =
              ConfigFactory.defaultOverrides()
                  .withFallback(ConfigFactory.parseFileAnySyntax(file))
                  .withFallback(ConfigFactory.defaultReference(CqlSession.class.getClassLoader()))
                  .resolve();
          return config.getConfig(DEFAULT_ROOT_PATH);
        });
  }

  @NonNull
  public static DriverConfigLoader fromUrl(@NonNull URL url) {
    return new DefaultDriverConfigLoader(
        () -> {
          ConfigFactory.invalidateCaches();
          Config config =
              ConfigFactory.defaultOverrides()
                  .withFallback(ConfigFactory.parseURL(url))
                  .withFallback(ConfigFactory.defaultReference(CqlSession.class.getClassLoader()))
                  .resolve();
          return config.getConfig(DEFAULT_ROOT_PATH);
        });
  }

  @NonNull
  public static DefaultDriverConfigLoader fromString(@NonNull String contents) {
    return new DefaultDriverConfigLoader(
        () -> {
          ConfigFactory.invalidateCaches();
          Config config =
              ConfigFactory.defaultOverrides()
                  .withFallback(ConfigFactory.parseString(contents))
                  .withFallback(ConfigFactory.defaultReference(CqlSession.class.getClassLoader()))
                  .resolve();
          return config.getConfig(DEFAULT_ROOT_PATH);
        },
        false);
  }

  private final Supplier configSupplier;
  private final TypesafeDriverConfig driverConfig;
  private final boolean supportsReloading;

  private volatile SingleThreaded singleThreaded;

  /**
   * Builds a new instance with the default Typesafe config loading rules (documented in {@link
   * SessionBuilder#withConfigLoader(DriverConfigLoader)}) and the core driver options. This
   * constructor enables config reloading (that is, {@link #supportsReloading} will return true).
   *
   * 

Application-specific classpath resources will be located using the {@linkplain * Thread#getContextClassLoader() the current thread's context class loader}. This might not be * suitable for OSGi deployments, which should use {@link #DefaultDriverConfigLoader(ClassLoader)} * instead. */ public DefaultDriverConfigLoader() { this(DEFAULT_CONFIG_SUPPLIER); } /** * Builds a new instance with the default Typesafe config loading rules (documented in {@link * SessionBuilder#withConfigLoader(DriverConfigLoader)}) and the core driver options, except that * application-specific classpath resources will be located using the provided {@link ClassLoader} * instead of {@linkplain Thread#getContextClassLoader() the current thread's context class * loader}. This constructor enables config reloading (that is, {@link #supportsReloading} will * return true). */ public DefaultDriverConfigLoader(@NonNull ClassLoader appClassLoader) { this( () -> { ConfigFactory.invalidateCaches(); return ConfigFactory.defaultOverrides() .withFallback(ConfigFactory.defaultApplication(appClassLoader)) .withFallback(ConfigFactory.defaultReference(CqlSession.class.getClassLoader())) .resolve() .getConfig(DEFAULT_ROOT_PATH); }); } /** * Builds an instance with custom arguments, if you want to load the configuration from somewhere * else. This constructor enables config reloading (that is, {@link #supportsReloading} will * return true). * * @param configSupplier A supplier for the Typesafe {@link Config}; it will be invoked once when * this object is instantiated, and at each reload attempt, if reloading is enabled. */ public DefaultDriverConfigLoader(@NonNull Supplier configSupplier) { this(configSupplier, true); } /** * Builds an instance with custom arguments, if you want to load the configuration from somewhere * else and/or modify config reload behavior. * * @param configSupplier A supplier for the Typesafe {@link Config}; it will be invoked once when * this object is instantiated, and at each reload attempt, if reloading is enabled. * @param supportsReloading Whether config reloading should be enabled or not. */ public DefaultDriverConfigLoader( @NonNull Supplier configSupplier, boolean supportsReloading) { this.configSupplier = configSupplier; this.driverConfig = new TypesafeDriverConfig(configSupplier.get()); this.supportsReloading = supportsReloading; } @NonNull @Override public DriverConfig getInitialConfig() { return driverConfig; } @Override public void onDriverInit(@NonNull DriverContext driverContext) { this.singleThreaded = new SingleThreaded((InternalDriverContext) driverContext); } @NonNull @Override public final CompletionStage reload() { if (supportsReloading) { CompletableFuture result = new CompletableFuture<>(); RunOrSchedule.on(singleThreaded.adminExecutor, () -> singleThreaded.reload(result)); return result; } else { return CompletableFutures.failedFuture( new UnsupportedOperationException( "This instance of DefaultDriverConfigLoader does not support reloading")); } } @Override public final boolean supportsReloading() { return supportsReloading; } /** For internal use only, this leaks a Typesafe config type. */ @NonNull public Supplier getConfigSupplier() { return configSupplier; } @Override public void close() { SingleThreaded singleThreaded = this.singleThreaded; if (singleThreaded != null && !singleThreaded.adminExecutor.terminationFuture().isDone()) { try { RunOrSchedule.on(singleThreaded.adminExecutor, singleThreaded::close); } catch (RejectedExecutionException e) { // Checking the future is racy, there is still a tiny window that could get us here. // We can safely ignore this error because, if the execution is rejected, the periodic // reload task, if any, has been already cancelled. } } } /** * Constructs a builder that may be used to provide additional configuration beyond those defined * in your configuration files programmatically. For example: * *

{@code
   * CqlSession session = CqlSession.builder()
   *   .withConfigLoader(DefaultDriverConfigLoader.builder()
   *     .withDuration(DefaultDriverOption.REQUEST_TIMEOUT, Duration.ofMillis(500))
   *     .build())
   *   .build();
   * }
* *

In the general case, use of this is not recommended, but it may be useful in situations * where configuration must be defined at runtime or is derived from some other configuration * source. * * @deprecated this feature is now available in the public API. Use {@link * DriverConfigLoader#programmaticBuilder()} instead. */ @Deprecated @NonNull public static DefaultDriverConfigLoaderBuilder builder() { return new DefaultDriverConfigLoaderBuilder(); } private class SingleThreaded { private final String logPrefix; private final EventExecutor adminExecutor; private final EventBus eventBus; private final DriverExecutionProfile config; private Duration reloadInterval; private ScheduledFuture periodicTaskHandle; private boolean closeWasCalled; private SingleThreaded(InternalDriverContext context) { this.logPrefix = context.getSessionName(); this.adminExecutor = context.getNettyOptions().adminEventExecutorGroup().next(); this.eventBus = context.getEventBus(); this.config = context.getConfig().getDefaultProfile(); this.reloadInterval = context .getConfig() .getDefaultProfile() .getDuration(DefaultDriverOption.CONFIG_RELOAD_INTERVAL); RunOrSchedule.on(adminExecutor, this::schedulePeriodicReload); } private void schedulePeriodicReload() { assert adminExecutor.inEventLoop(); // Cancel any previously running task if (periodicTaskHandle != null) { periodicTaskHandle.cancel(false); } if (reloadInterval.isZero()) { LOG.debug("[{}] Reload interval is 0, disabling periodic reloading", logPrefix); } else { LOG.debug("[{}] Scheduling periodic reloading with interval {}", logPrefix, reloadInterval); periodicTaskHandle = adminExecutor.scheduleAtFixedRate( this::reloadInBackground, reloadInterval.toNanos(), reloadInterval.toNanos(), TimeUnit.NANOSECONDS); } } /** * @param reloadedFuture a future to complete when the reload is complete (might be null if the * caller is not interested in being notified) */ private void reload(CompletableFuture reloadedFuture) { assert adminExecutor.inEventLoop(); if (closeWasCalled) { if (reloadedFuture != null) { reloadedFuture.completeExceptionally(new IllegalStateException("session is closing")); } return; } try { boolean changed = driverConfig.reload(configSupplier.get()); if (changed) { LOG.info("[{}] Detected a configuration change", logPrefix); eventBus.fire(ConfigChangeEvent.INSTANCE); Duration newReloadInterval = config.getDuration(DefaultDriverOption.CONFIG_RELOAD_INTERVAL); if (!newReloadInterval.equals(reloadInterval)) { reloadInterval = newReloadInterval; schedulePeriodicReload(); } } else { LOG.debug("[{}] Reloaded configuration but it hasn't changed", logPrefix); } if (reloadedFuture != null) { reloadedFuture.complete(changed); } } catch (Error | RuntimeException e) { if (reloadedFuture != null) { reloadedFuture.completeExceptionally(e); } else { Loggers.warnWithException( LOG, "[{}] Unexpected exception during scheduled reload", logPrefix, e); } } } private void reloadInBackground() { reload(null); } private void close() { assert adminExecutor.inEventLoop(); if (closeWasCalled) { return; } closeWasCalled = true; if (periodicTaskHandle != null) { periodicTaskHandle.cancel(false); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy