/* * Copyright 2008-2009 LinkedIn, 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 voldemort.utils; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import voldemort.annotations.concurrency.NotThreadsafe; /** * A less fucked properties class - Implements Map instead of extending HashMap * - Hash helpers for getting typed values * * */ @NotThreadsafe public class Props implements Map<String, String> { private final Map<String, String> props; public Props() { this.props = new HashMap<String, String>(); } public Props(File... files) throws FileNotFoundException, IOException { this.props = new HashMap<String, String>(); for(int i = files.length - 1; i >= 0; i--) { Properties properties = new Properties(); InputStream input = new BufferedInputStream(new FileInputStream(files[i].getAbsolutePath())); properties.load(input); for(Entry<Object, Object> e: properties.entrySet()) this.props.put((String) e.getKey(), (String) e.getValue()); input.close(); } } public Props(Map<String, String>... props) { this.props = new HashMap<String, String>(); for(int i = props.length - 1; i >= 0; i--) this.props.putAll(props[i]); } public Props(Properties... properties) { this.props = new HashMap<String, String>(); loadProperties(properties); } public void loadProperties(Properties... properties) { for(int i = properties.length - 1; i >= 0; i--) for(Entry<Object, Object> e: properties[i].entrySet()) this.props.put((String) e.getKey(), (String) e.getValue()); } @Override public void clear() { props.clear(); } @Override public boolean containsKey(Object k) { return props.containsKey(k); } @Override public boolean containsValue(Object value) { return props.containsValue(value); } @Override public Set<Entry<String, String>> entrySet() { return props.entrySet(); } @Override public String get(Object key) { try { return props.get(key); } catch (NullPointerException e) { // Overriding NPEs so that we get a more informative message. throw new NullPointerException("The key '" + key.toString() + "' contains a null value."); } } @Override public boolean isEmpty() { return props.isEmpty(); } @Override public Set<String> keySet() { return props.keySet(); } @Override public String put(String key, String value) { return props.put(key, value); } public String put(String key, Integer value) { return props.put(key, value.toString()); } public String put(String key, Long value) { return props.put(key, value.toString()); } public String put(String key, Double value) { return props.put(key, value.toString()); } public String put(String key, Boolean value) { return props.put(key, value.toString()); } public String put(String key, List<String> listOfValues) { String serializedValue = null; for (String singleValue: listOfValues) { if (serializedValue == null) { serializedValue = singleValue; } else { serializedValue += "," + singleValue; } } return props.put(key, serializedValue); } public Props with(String key, String value) { put(key, value); return this; } public Props with(String key, Integer value) { put(key, value); return this; } public Props with(String key, Double value) { put(key, value); return this; } public Props with(String key, Long value) { put(key, value); return this; } public Props with(String key, Boolean value) { put(key, value); return this; } @Override public void putAll(Map<? extends String, ? extends String> m) { props.putAll(m); } @Override public String remove(Object s) { return props.remove(s); } @Override public int size() { return props.size(); } @Override public Collection<String> values() { return props.values(); } public String getString(String key, String defaultValue) { if(containsKey(key)) return get(key); else return defaultValue; } public String getString(String key) { if(containsKey(key)) return get(key); else throw new UndefinedPropertyException(key); } public boolean getBoolean(String key, boolean defaultValue) { if(containsKey(key)) return "true".equalsIgnoreCase(get(key)); else return defaultValue; } public boolean getBoolean(String key) { if(containsKey(key)) return "true".equalsIgnoreCase(get(key)); else throw new UndefinedPropertyException(key); } public long getLong(String name, long defaultValue) { if(containsKey(name)) return Long.parseLong(get(name)); else return defaultValue; } public long getLong(String name) { if(containsKey(name)) return Long.parseLong(get(name)); else throw new UndefinedPropertyException(name); } public int getInt(String name, int defaultValue) { if(containsKey(name)) return Integer.parseInt(get(name)); else return defaultValue; } /** * @param name of the key to retrieve * @return an int of the requested value * @throws UndefinedPropertyException if the requested key is absent */ public int getInt(String name) { if(containsKey(name)) return Integer.parseInt(get(name)); else throw new UndefinedPropertyException(name); } /** * @param name of the key to retrieve * @return an Integer of the requested value, or null if absent */ public Integer getNullableInt(String name) { if(containsKey(name)) return Integer.parseInt(get(name)); else return null; } public double getDouble(String name, double defaultValue) { if(containsKey(name)) return Double.parseDouble(get(name)); else return defaultValue; } public double getDouble(String name) { if(containsKey(name)) return Double.parseDouble(get(name)); else throw new UndefinedPropertyException(name); } public long getBytes(String name, long defaultValue) { if(containsKey(name)) return getBytes(name); else return defaultValue; } /** * Attempts to return the Class that corresponds to the Props value. If the * class doesn't exit, an IllegalArgumentException will be thrown. * * @param key * @return */ public Class<?> getClass(String key) { try { if (containsKey(key)) { return Class.forName(get(key)); } else { throw new UndefinedPropertyException( "Missing required property '" + key + "'"); } } catch (ClassNotFoundException e) { throw new IllegalArgumentException(e); } } /** * Gets the class from the Props. If it doesn't exist, it will return the * defaultClass * * @param key * @param defaultClass * @return */ public Class<?> getClass(String key, Class<?> defaultClass) { if (containsKey(key)) { return getClass(key); } else { return defaultClass; } } @Override public boolean equals(Object o) { return this.props.equals(o); } @Override public int hashCode() { return this.props.hashCode(); } @Override public String toString() { return toString(false); } public String toString(boolean oneLinePerEntry) { StringBuilder builder = new StringBuilder("{"); boolean firstLine = true; SortedSet<Entry<String, String>> sortedEntries = new TreeSet<Entry<String, String>>(new Comparator<Entry<String, String>>(){ @Override public int compare(Entry<String, String> o1, Entry<String, String> o2) { return o1.getKey().compareTo(o2.getKey()); } }); sortedEntries.addAll(this.props.entrySet()); for(Entry<String, String> entry: sortedEntries) { if (oneLinePerEntry) { builder.append("\n\t"); } else { if (firstLine) { firstLine = false; } else { builder.append(", "); } } builder.append(entry.getKey()); builder.append(": "); builder.append(entry.getValue()); } if (oneLinePerEntry) { builder.append("\n"); } builder.append("}"); return builder.toString(); } public long getBytes(String name) { if(!containsKey(name)) throw new UndefinedPropertyException(name); String bytes = get(name); String bytesLc = bytes.toLowerCase().trim(); if(bytesLc.endsWith("kb")) return Long.parseLong(bytes.substring(0, bytes.length() - 2)) * 1024; else if(bytesLc.endsWith("k")) return Long.parseLong(bytes.substring(0, bytes.length() - 1)) * 1024; else if(bytesLc.endsWith("mb")) return Long.parseLong(bytes.substring(0, bytes.length() - 2)) * 1024 * 1024; else if(bytesLc.endsWith("m")) return Long.parseLong(bytes.substring(0, bytes.length() - 1)) * 1024 * 1024; else if(bytesLc.endsWith("gb")) return Long.parseLong(bytes.substring(0, bytes.length() - 2)) * 1024 * 1024 * 1024; else if(bytesLc.endsWith("g")) return Long.parseLong(bytes.substring(0, bytes.length() - 1)) * 1024 * 1024 * 1024; else return Long.parseLong(bytes); } public List<String> getList(String key, List<String> defaultValue) { if(!containsKey(key)) return defaultValue; String value = get(key); String[] pieces = value.split("\\s*,\\s*"); return Arrays.asList(pieces); } public List<String> getList(String key) { if(!containsKey(key)) throw new UndefinedPropertyException(key); return getList(key, null); } /** * Store all properties, those local and also those in parent props * * @param file * The file to store to * @throws IOException * If there is an error writing */ public void storeFlattened(File file) throws IOException { BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream(file)); try { storeFlattened(out); } finally { out.close(); } } /** * Store all properties, those local and also those in parent props * * @param out * The stream to write to * @throws IOException * If there is an error writing */ public void storeFlattened(OutputStream out) throws IOException { Properties p = new Properties(); for (String key : keySet()) { if (!p.containsKey(key)) { p.setProperty(key, get(key)); } } p.store(out, null); } }