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

org.apache.solr.common.util.EnvUtils 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 org.apache.solr.common.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.apache.solr.common.SolrException;

/**
 * Provides convenient access to System Properties for Solr. It also converts 'SOLR_FOO' env vars to
 * system properties 'solr.foo', which is done on first access of this class. All Solr code should
 * use this in lieu of JDK equivalents.
 */
public class EnvUtils {
  /** Maps ENV keys to sys prop keys for special/custom mappings */
  private static final Map CUSTOM_MAPPINGS = new HashMap<>();

  private static final Map camelCaseToDotsMap = new ConcurrentHashMap<>();

  static {
    try {
      Properties props = new Properties();
      try (InputStream stream =
          EnvUtils.class.getClassLoader().getResourceAsStream("EnvToSyspropMappings.properties")) {
        props.load(new InputStreamReader(Objects.requireNonNull(stream), StandardCharsets.UTF_8));
        for (String key : props.stringPropertyNames()) {
          CUSTOM_MAPPINGS.put(key, props.getProperty(key));
        }
        init(false, System.getenv());
      }
    } catch (IOException e) {
      throw new SolrException(
          SolrException.ErrorCode.INVALID_STATE, "Failed loading env.var->properties mapping", e);
    }
  }

  /** Get all Solr system properties as a sorted map */
  public static SortedMap getProperties() {
    return System.getProperties().entrySet().stream()
        .collect(
            Collectors.toMap(
                entry -> entry.getKey().toString(),
                entry -> entry.getValue().toString(),
                (e1, e2) -> e1,
                TreeMap::new));
  }

  /** Get a property as string */
  public static String getProperty(String key) {
    return getProperty(key, null);
  }

  /**
   * Get a property as string with a fallback value. All other getProperty* methods use this.
   *
   * @param key property key, which treats 'camelCase' the same as 'camel.case'
   * @param defaultValue fallback value if property is not found
   */
  public static String getProperty(String key, String defaultValue) {
    String value = getPropertyWithCamelCaseFallback(key);
    return value != null ? value : defaultValue;
  }

  /**
   * Get a property from given key or an alias key converted from CamelCase to dot separated.
   *
   * @return property value or value of dot-separated alias key or null if not found
   */
  private static String getPropertyWithCamelCaseFallback(String key) {
    String value = System.getProperty(key);
    if (value != null) {
      return value;
    } else {
      // Figure out if string is CamelCase and convert to dot separated
      String altKey = camelCaseToDotSeparated(key);
      return System.getProperty(altKey);
    }
  }

  private static String camelCaseToDotSeparated(String key) {
    return camelCaseToDotsMap.computeIfAbsent(
        key,
        (k) -> String.join(".", k.split("(?=[A-Z])")).replace("..", ".").toLowerCase(Locale.ROOT));
  }

  /** Get property as integer */
  public static Integer getPropertyAsInteger(String key) {
    return getPropertyAsInteger(key, null);
  }

  /** Get property as integer, or default value */
  public static Integer getPropertyAsInteger(String key, Integer defaultValue) {
    String value = getProperty(key);
    if (value == null) {
      return defaultValue;
    }
    return Integer.parseInt(value);
  }

  /** Get property as long */
  public static Long getPropertyAsLong(String key) {
    return getPropertyAsLong(key, null);
  }

  /** Get property as long, or default value */
  public static Long getPropertyAsLong(String key, Long defaultValue) {
    String value = getProperty(key);
    if (value == null) {
      return defaultValue;
    }
    return Long.parseLong(value);
  }

  /** Get property as boolean */
  public static Boolean getPropertyAsBool(String key) {
    return getPropertyAsBool(key, null);
  }

  /** Get property as boolean, or default value */
  public static Boolean getPropertyAsBool(String key, Boolean defaultValue) {
    String value = getProperty(key);
    if (value == null) {
      return defaultValue;
    }
    return StrUtils.parseBool(value);
  }

  /**
   * Get comma separated strings from sysprop as List
   *
   * @return list of strings, or null if not found
   */
  public static List getPropertyAsList(String key) {
    return getPropertyAsList(key, null);
  }

  /**
   * Get comma separated strings from sysprop as List, or default value
   *
   * @return list of strings, or provided default if not found
   */
  public static List getPropertyAsList(String key, List defaultValue) {
    return getProperty(key) != null ? stringValueToList(getProperty(key)) : defaultValue;
  }

  /** Set a system property. Shim to {@link System#setProperty(String, String)} */
  public static void setProperty(String key, String value) {
    System.setProperty(key, value);
    System.setProperty(camelCaseToDotSeparated(key), value);
  }

  /**
   * Re-reads environment variables and updates the internal map. Mainly for internal and test use.
   */
  static synchronized void init(boolean overwrite, Map env) {
    // Convert eligible environment variables to system properties
    for (String key : env.keySet()) {
      if (key.startsWith("SOLR_") || CUSTOM_MAPPINGS.containsKey(key)) {
        String sysPropKey = envNameToSyspropName(key);
        // Existing system properties take precedence
        if (!sysPropKey.isBlank() && (overwrite || getProperty(sysPropKey, null) == null)) {
          setProperty(sysPropKey, env.get(key));
        }
      }
    }
  }

  protected static String envNameToSyspropName(String envName) {
    return CUSTOM_MAPPINGS.containsKey(envName)
        ? CUSTOM_MAPPINGS.get(envName)
        : envName.toLowerCase(Locale.ROOT).replace("_", ".");
  }

  /**
   * Convert a string to a List<String>. If the string is a JSON array, it will be parsed as
   * such. String splitting uses "splitSmart" which supports backslash escaped characters.
   */
  @SuppressWarnings("unchecked")
  private static List stringValueToList(String string) {
    if (string.startsWith("[") && string.endsWith("]")) {
      // Convert a JSON string to a List using Noggit parser
      return (List) Utils.fromJSONString(string);
    } else {
      return StrUtils.splitSmart(string, ",", true).stream()
          .map(String::trim)
          .collect(Collectors.toList());
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy