/** * Copyright 2011 LiveRamp * * 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.liveramp.hank.config.yaml; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.Serializable; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import org.yaml.snakeyaml.Yaml; import com.liveramp.hank.config.InvalidConfigurationException; public abstract class YamlConfigurator implements Serializable { protected Map<String, Object> config; private String contentSource; public YamlConfigurator() { } public YamlConfigurator(String configurationFilePath) throws FileNotFoundException, InvalidConfigurationException { loadFromFile(configurationFilePath); validate(); } protected abstract void validate() throws InvalidConfigurationException; public void loadFromFile(String path) throws InvalidConfigurationException, FileNotFoundException { try { contentSource = path; config = (Map<String, Object>)new Yaml().load(new BufferedInputStream(new FileInputStream(path))); } catch (Exception e) { throw new RuntimeException("Invalid configuration in file " + path, e); } validate(); } public void loadFromYaml(String yaml) throws InvalidConfigurationException { try { contentSource = yaml; config = (Map<String, Object>)new Yaml().load(yaml); } catch (Exception e) { throw new RuntimeException("Invalid configuration: " + yaml, e); } validate(); } public void loadFromObjectMap(Map<String, Object> yaml) throws InvalidConfigurationException { config = yaml; validate(); } public String toYaml() { return new Yaml().dump(config); } @Override public String toString() { return toYaml(); } protected void checkNonEmptyConfiguration() throws InvalidConfigurationException { if (config == null) { throw new InvalidConfigurationException("Configuration is empty '" + contentSource + "'"); } } protected Object getRequiredOption(String... optionPath) throws InvalidConfigurationException { Map<String, Object> currentSection = config; String path = "_root"; int i = 0; for (; i < optionPath.length - 1; ++i) { if (currentSection.get(optionPath[i]) == null) { throw new InvalidConfigurationException("Section '" + optionPath[i] + "' is required in configuration section '" + path + "' of configuration '" + contentSource + "'"); } path = path + ":" + optionPath[i]; currentSection = (Map<String, Object>)currentSection.get(optionPath[i]); } if (!currentSection.containsKey(optionPath[i])) { throw new InvalidConfigurationException("Option '" + optionPath[i] + "' is required in configuration section '" + path + "' of configuration '" + contentSource + "'"); } return currentSection.get(optionPath[i]); } protected Map<String, Object> getRequiredSection(String... optionPath) throws InvalidConfigurationException { Object option = getRequiredOption(optionPath); if (!(option instanceof Map)) { throw new InvalidConfigurationException("Option '" + Arrays.toString(optionPath) + "' must be of type Map in configuration '" + contentSource + "'"); } return (Map<String, Object>)option; } protected Map<String, Object> getSection(String... optionPath) { try { return getRequiredSection(optionPath); } catch (InvalidConfigurationException e) { throw new RuntimeException(e); } } protected String getRequiredString(String... optionPath) throws InvalidConfigurationException { Object option = getRequiredOption(optionPath); if (option != null && !(option instanceof String)) { throw new InvalidConfigurationException("Option '" + Arrays.toString(optionPath) + "' must be of type String in configuration '" + contentSource + "'"); } return (String)option; } protected String getString(String... optionPath) { try { return getRequiredString(optionPath); } catch (InvalidConfigurationException e) { throw new RuntimeException(e); } } protected String getOptionalString(String... optionPath) { try { return getRequiredString(optionPath); } catch (InvalidConfigurationException e) { return null; } } protected Integer getRequiredInteger(String... optionPath) throws InvalidConfigurationException { Object option = getRequiredOption(optionPath); if (option != null && !(option instanceof Integer)) { throw new InvalidConfigurationException("Option '" + Arrays.toString(optionPath) + "' must be of type Integer in configuration '" + contentSource + "'"); } return (Integer)option; } protected Integer getInteger(String... optionPath) { try { return getRequiredInteger(optionPath); } catch (InvalidConfigurationException e) { throw new RuntimeException(e); } } protected Integer getOptionalInteger(String... optionPath) { try { return getRequiredInteger(optionPath); } catch (InvalidConfigurationException e) { return null; } } protected Long getRequiredLong(String... optionPath) throws InvalidConfigurationException { Object option = getRequiredOption(optionPath); // Translate integers to longs if (option instanceof Integer) { option = Long.valueOf((Integer)option); } if (option != null && !(option instanceof Long)) { throw new InvalidConfigurationException("Option '" + Arrays.toString(optionPath) + "' must be of type Long in configuration '" + contentSource + "'"); } return (Long)option; } protected Long getLong(String... optionPath) { try { return getRequiredLong(optionPath); } catch (InvalidConfigurationException e) { throw new RuntimeException(e); } } protected Long getOptionalLong(String... optionPath) { try { return getRequiredLong(optionPath); } catch (InvalidConfigurationException e) { return null; } } protected List<String> getRequiredStringList(String... optionPath) throws InvalidConfigurationException { Object option = getRequiredOption(optionPath); if (option != null && !(option instanceof List)) { throw new InvalidConfigurationException("Option '" + Arrays.toString(optionPath) + "' must be of type List of strings in configuration '" + contentSource + "'"); } try { return (List<String>)option; } catch (ClassCastException e) { throw new InvalidConfigurationException("Option '" + Arrays.toString(optionPath) + "' must be of type List of strings in configuration '" + contentSource + "'"); } } protected List<String> getOptionalStringList(String... optionPath) { try { return getRequiredStringList(optionPath); } catch (InvalidConfigurationException e) { return Collections.emptyList(); } } protected List<String> getStringList(String... optionPath) { try { return getRequiredStringList(optionPath); } catch (InvalidConfigurationException e) { throw new RuntimeException(e); } } }