001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.conf; 019 020import java.util.Map; 021import java.util.concurrent.ConcurrentHashMap; 022import java.util.function.Predicate; 023import org.apache.hadoop.conf.Configuration; 024import org.apache.yetus.audience.InterfaceAudience; 025import org.slf4j.Logger; 026import org.slf4j.LoggerFactory; 027 028/** 029 * Utility class for basic validation of configuration values. 030 */ 031@InterfaceAudience.Private 032public final class ConfigKey { 033 private static final Logger LOG = LoggerFactory.getLogger(ConfigKey.class); 034 035 private ConfigKey() { 036 } 037 038 @FunctionalInterface 039 private interface Validator { 040 void validate(Configuration conf); 041 } 042 043 // Map of configuration keys to validators 044 private static final Map<String, Validator> validators = new ConcurrentHashMap<>(); 045 046 /** 047 * Registers the configuration key that expects an integer. 048 * @param key configuration key 049 * @param predicates additional predicates to validate the value 050 * @return the key 051 */ 052 @SafeVarargs 053 public static String INT(String key, Predicate<Integer>... predicates) { 054 return register(key, 055 conf -> validateNumeric(key, "an integer", () -> conf.getInt(key, 0), predicates)); 056 } 057 058 /** 059 * Registers the configuration key that expects a long. 060 * @param key configuration key 061 * @param predicates additional predicates to validate the value 062 * @return the key 063 */ 064 @SafeVarargs 065 public static String LONG(String key, Predicate<Long>... predicates) { 066 return register(key, 067 conf -> validateNumeric(key, "a long", () -> conf.getLong(key, 0), predicates)); 068 } 069 070 /** 071 * Registers the configuration key that expects a float. 072 * @param key configuration key 073 * @param predicates additional predicates to validate the value 074 * @return the key 075 */ 076 @SafeVarargs 077 public static String FLOAT(String key, Predicate<Float>... predicates) { 078 return register(key, 079 conf -> validateNumeric(key, "a float", () -> conf.getFloat(key, 0), predicates)); 080 } 081 082 /** 083 * Registers the configuration key that expects a double. 084 * @param key configuration key 085 * @param predicates additional predicates to validate the value 086 * @return the key 087 */ 088 @SafeVarargs 089 public static String DOUBLE(String key, Predicate<Double>... predicates) { 090 return register(key, 091 conf -> validateNumeric(key, "a double", () -> conf.getDouble(key, 0), predicates)); 092 } 093 094 /** 095 * Registers the configuration key that expects a class. 096 * @param key configuration key 097 * @param expected the expected class or interface the value should implement 098 * @return the key 099 */ 100 public static <T> String CLASS(String key, Class<T> expected) { 101 return register(key, conf -> { 102 String value = conf.get(key); 103 try { 104 if (!expected.isAssignableFrom(Class.forName(value))) { 105 throw new IllegalArgumentException( 106 String.format("%s ('%s') is not compatible to %s.", value, key, expected.getName())); 107 } 108 } catch (ClassNotFoundException e) { 109 throw new IllegalArgumentException(String.format("'%s' must be a class.", key), e); 110 } 111 }); 112 } 113 114 /** 115 * Registers the configuration key that expects a boolean value. We actually don't register a 116 * validator, because {@link Configuration#getBoolean} doesn't throw an exception even if the 117 * value is not a boolean. So this is only for documentation purposes. 118 * @param key configuration key 119 * @return the key 120 */ 121 public static String BOOLEAN(String key) { 122 return key; 123 } 124 125 /** 126 * Validates the configuration. 127 * @param conf a configuration to validate 128 */ 129 public static void validate(Configuration conf) { 130 conf.iterator().forEachRemaining(entry -> { 131 Validator validator = validators.get(entry.getKey()); 132 if (validator != null) { 133 validator.validate(conf); 134 } 135 }); 136 } 137 138 @FunctionalInterface 139 private interface NumberGetter<T> { 140 T get() throws NumberFormatException; 141 } 142 143 private static <T> void validateNumeric(String name, String expected, NumberGetter<T> getter, 144 Predicate<T>... predicates) { 145 try { 146 T value = getter.get(); 147 for (Predicate<T> predicate : predicates) { 148 if (!predicate.test(value)) { 149 throw new IllegalArgumentException("Invalid value for '" + name + "': " + value + "."); 150 } 151 } 152 } catch (NumberFormatException e) { 153 throw new IllegalArgumentException(String.format("'%s' must be %s.", name, expected), e); 154 } 155 } 156 157 private static String register(String key, Validator validator) { 158 LOG.debug("Registering config validator for {}", key); 159 validators.put(key, validator); 160 return key; 161 } 162}