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

com.facebook.config.AbstractExpandedConfJSONProvider Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2012 Facebook, 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.facebook.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;

/**
 * Reads and expands a JSON config object through a series of includes.
 * Expects JSON config objects to consist of two top level keys: 'conf' and 'includes':
 *
 * conf - key-value pairs to be consolidated in the returned JSONObject [req]
 * includes - list of JSON configs (of the same format) from which to pull and
 *    include key-value pairs [opt]
 *
 * JSON config file format:
 * {
 *  conf : {
 *    key1 : value1,
 *    key2 : value2
 *    ...
 *  },
 *  includes : [
 *    object1,
 *    object2
 *   ]
 * }
 *
 * Keys in the existing or closer config objects will take precedence over the same
 * key defined in more distantly included objects.
 */
public abstract class AbstractExpandedConfJSONProvider implements JSONProvider {
  private static final Logger LOG = LoggerFactory.getLogger(AbstractExpandedConfJSONProvider.class);
  private static final String CONF_KEY = "conf";
  private static final String INCLUDES_KEY = "includes";

  private final String root;

  public AbstractExpandedConfJSONProvider(String root) {
    this.root = root;
  }

  private JSONObject getExpandedJSONConfig() throws JSONException {
    Set traversedFiles = new HashSet<>();
    Queue toTraverse = new LinkedList<>();

    // Seed the graph traversal with the root node
    traversedFiles.add(root);
    toTraverse.add(root);

    // Policy: parent configs will override children (included) configs
    JSONObject expanded = new JSONObject();
    while (!toTraverse.isEmpty()) {
      String current = toTraverse.remove();
      JSONObject json = load(current);
      JSONObject conf = json.getJSONObject(CONF_KEY);
      Iterator iter = conf.keys();
      while (iter.hasNext()) {
        String key = iter.next();
        // Current config will get to insert keys before its include files
        if (!expanded.has(key)) {
          expanded.put(key, conf.get(key));
        }
      }
      // Check if the file itself has any included files
      if (json.has(INCLUDES_KEY)) {
        JSONArray includes = json.getJSONArray(INCLUDES_KEY);
        for (int idx = 0; idx < includes.length(); idx++) {
          String include = resolve(current, includes.getString(idx));
          if (traversedFiles.contains(include)) {
            LOG.warn("Config file was included twice: " + include);
          } else {
            toTraverse.add(include);
            traversedFiles.add(include);
          }
        }
      }
    }

    return expanded;
  }

  @Override
  public JSONObject get() throws JSONException {
    return getExpandedJSONConfig();
  }

  /**
   * Determines the canonical resource identifier for the given config.
   * This hook allows subclasses to resolve relative paths used in includes.
   *
   * @param parent the resource which listed this config in its includes
   *               (or null if it is the root)
   * @param config a config resource identifier
   * @return the canonical resource identifier
   * @see com.facebook.config.ExpandedConfFileJSONProvider#resolve(String, String)
   */
  abstract protected String resolve(String parent, String config);

  /**
   * Returns the configuration JSON identified by config.
   * config is guaranteed to be the result of some call to
   * {@link #resolve(String, String)}.
   *
   * @param config a canonical resource identifier
   * @return a JSON representation of the resource's contents
   * @see com.facebook.config.ExpandedConfFileJSONProvider#load(String)
   */
  abstract protected JSONObject load(String config) throws JSONException;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy