--- old/src/java.base/share/classes/java/util/Properties.java 2015-05-01 10:10:42.000000000 -0700 +++ new/src/java.base/share/classes/java/util/Properties.java 2015-05-01 10:10:42.000000000 -0700 @@ -34,6 +34,12 @@ import java.io.Writer; import java.io.OutputStreamWriter; import java.io.BufferedWriter; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; import jdk.internal.util.xml.PropertiesDefaultHandler; @@ -144,6 +150,9 @@ * @param defaults the defaults. */ public Properties(Properties defaults) { + // use package-private constructor to + // initialize unused fields with dummy values + super((Void) null); this.defaults = defaults; } @@ -160,7 +169,7 @@ * @see #getProperty * @since 1.2 */ - public synchronized Object setProperty(String key, String value) { + public Object setProperty(String key, String value) { return put(key, value); } @@ -312,7 +321,7 @@ * @throws NullPointerException if {@code reader} is null. * @since 1.6 */ - public synchronized void load(Reader reader) throws IOException { + public void load(Reader reader) throws IOException { Objects.requireNonNull(reader, "reader parameter is null"); load0(new LineReader(reader)); } @@ -338,7 +347,7 @@ * @throws NullPointerException if {@code inStream} is null. * @since 1.2 */ - public synchronized void load(InputStream inStream) throws IOException { + public void load(InputStream inStream) throws IOException { Objects.requireNonNull(inStream, "inStream parameter is null"); load0(new LineReader(inStream)); } @@ -829,18 +838,16 @@ } bw.write("#" + new Date().toString()); bw.newLine(); - synchronized (this) { - for (Enumeration e = keys(); e.hasMoreElements();) { - String key = (String)e.nextElement(); - String val = (String)get(key); - key = saveConvert(key, true, escUnicode); - /* No need to escape embedded and trailing spaces for value, hence - * pass false to flag. - */ - val = saveConvert(val, false, escUnicode); - bw.write(key + "=" + val); - bw.newLine(); - } + for (Map.Entry e : entrySet()) { + String key = (String)e.getKey(); + String val = (String)e.getValue(); + key = saveConvert(key, true, escUnicode); + /* No need to escape embedded and trailing spaces for value, hence + * pass false to flag. + */ + val = saveConvert(val, false, escUnicode); + bw.write(key + "=" + val); + bw.newLine(); } bw.flush(); } @@ -876,7 +883,7 @@ * Encoding in Entities * @since 1.5 */ - public synchronized void loadFromXML(InputStream in) + public void loadFromXML(InputStream in) throws IOException, InvalidPropertiesFormatException { Objects.requireNonNull(in); @@ -971,7 +978,7 @@ * @see #defaults */ public String getProperty(String key) { - Object oval = super.get(key); + Object oval = map.get(key); String sval = (oval instanceof String) ? (String)oval : null; return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval; } @@ -1009,7 +1016,7 @@ * @see #stringPropertyNames */ public Enumeration propertyNames() { - Hashtable h = new Hashtable<>(); + Hashtable h = new Hashtable<>(); enumerate(h); return h.keys(); } @@ -1033,7 +1040,7 @@ * @since 1.6 */ public Set stringPropertyNames() { - Hashtable h = new Hashtable<>(); + Map h = new HashMap<>(); enumerateStringProperties(h); return h.keySet(); } @@ -1048,11 +1055,11 @@ */ public void list(PrintStream out) { out.println("-- listing properties --"); - Hashtable h = new Hashtable<>(); + Map h = new HashMap<>(); enumerate(h); - for (Enumeration e = h.keys() ; e.hasMoreElements() ;) { - String key = e.nextElement(); - String val = (String)h.get(key); + for (Map.Entry e : h.entrySet()) { + String key = e.getKey(); + String val = (String)e.getValue(); if (val.length() > 40) { val = val.substring(0, 37) + "..."; } @@ -1076,11 +1083,11 @@ */ public void list(PrintWriter out) { out.println("-- listing properties --"); - Hashtable h = new Hashtable<>(); + Map h = new HashMap<>(); enumerate(h); - for (Enumeration e = h.keys() ; e.hasMoreElements() ;) { - String key = e.nextElement(); - String val = (String)h.get(key); + for (Map.Entry e : h.entrySet()) { + String key = e.getKey(); + String val = (String)e.getValue(); if (val.length() > 40) { val = val.substring(0, 37) + "..."; } @@ -1089,33 +1096,33 @@ } /** - * Enumerates all key/value pairs in the specified hashtable. - * @param h the hashtable + * Enumerates all key/value pairs in the specified Map. + * @param h the Map * @throws ClassCastException if any of the property keys * is not of String type. */ - private synchronized void enumerate(Hashtable h) { + private void enumerate(Map h) { if (defaults != null) { defaults.enumerate(h); } - for (Enumeration e = keys() ; e.hasMoreElements() ;) { - String key = (String)e.nextElement(); - h.put(key, get(key)); + for (Map.Entry e : entrySet()) { + String key = (String)e.getKey(); + h.put(key, e.getValue()); } } /** - * Enumerates all key/value pairs in the specified hashtable + * Enumerates all key/value pairs in the specified Map * and omits the property if the key or value is not a string. - * @param h the hashtable + * @param h the Map */ - private synchronized void enumerateStringProperties(Hashtable h) { + private void enumerateStringProperties(Map h) { if (defaults != null) { defaults.enumerateStringProperties(h); } - for (Enumeration e = keys() ; e.hasMoreElements() ;) { - Object k = e.nextElement(); - Object v = get(k); + for (Map.Entry e : entrySet()) { + Object k = e.getKey(); + Object v = e.getValue(); if (k instanceof String && v instanceof String) { h.put((String) k, (String) v); } @@ -1134,4 +1141,229 @@ private static final char[] hexDigit = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }; -} + + // + // Hashtable methods overridden and delegated to a ConcurrentHashMap instance + + private transient ConcurrentHashMap map = + new ConcurrentHashMap<>(8); + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + @Override + public Enumeration keys() { + return map.keys(); + } + + @Override + public Enumeration elements() { + return map.elements(); + } + + @Override + public boolean contains(Object value) { + return map.contains(value); + } + + @Override + public boolean containsValue(Object value) { + return map.containsValue(value); + } + + @Override + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + @Override + public Object get(Object key) { + return map.get(key); + } + + @Override + public Object put(Object key, Object value) { + return map.put(key, value); + } + + @Override + public Object remove(Object key) { + return map.remove(key); + } + + @Override + public void putAll(Map t) { + map.putAll(t); + } + + @Override + public void clear() { + map.clear(); + } + + @Override + public String toString() { + return map.toString(); + } + + @Override + public Set keySet() { + return map.keySet(); + } + + @Override + public Set> entrySet() { + return map.entrySet(); + } + + @Override + public Collection values() { + return map.values(); + } + + @Override + public boolean equals(Object o) { + return map.equals(o); + } + + @Override + public int hashCode() { + return map.hashCode(); + } + + @Override + public Object getOrDefault(Object key, Object defaultValue) { + return map.getOrDefault(key, defaultValue); + } + + @Override + public void forEach(BiConsumer action) { + map.forEach(action); + } + + @Override + public void replaceAll(BiFunction function) { + map.replaceAll(function); + } + + @Override + public Object putIfAbsent(Object key, Object value) { + return map.putIfAbsent(key, value); + } + + @Override + public boolean remove(Object key, Object value) { + return map.remove(key, value); + } + + @Override + public boolean replace(Object key, Object oldValue, Object newValue) { + return map.replace(key, oldValue, newValue); + } + + @Override + public Object replace(Object key, Object value) { + return map.replace(key, value); + } + + @Override + public Object computeIfAbsent(Object key, + Function mappingFunction) { + return map.computeIfAbsent(key, mappingFunction); + } + + @Override + public Object computeIfPresent(Object key, + BiFunction remappingFunction) { + return map.computeIfPresent(key, remappingFunction); + } + + @Override + public Object compute(Object key, + BiFunction remappingFunction) { + return map.compute(key, remappingFunction); + } + + @Override + public Object merge(Object key, Object value, + BiFunction remappingFunction) { + return map.merge(key, value, remappingFunction); + } + + // + // Special Hashtable methods + + @Override + protected void rehash() { + // no-op + } + + @Override + public Object clone() { + Properties clone = (Properties) cloneHashtable(); + clone.map = new ConcurrentHashMap<>(map); + return clone; + } + + // + // Hashtable serialization overrides + // (these should emit and consume Hashtable-compatible stream) + + @Override + void writeHashtable(ObjectOutputStream s) throws IOException { + List entryStack = new ArrayList<>(map.size() * 2); // an estimate + + for (Map.Entry entry : map.entrySet()) { + entryStack.add(entry.getValue()); + entryStack.add(entry.getKey()); + } + + // Write out the simulated threshold, loadfactor + float loadFactor = 0.75f; + int count = entryStack.size() / 2; + int length = (int)(count / loadFactor) + (count / 20) + 3; + if (length > count && (length & 1) == 0) { + length--; + } + synchronized (map) { // in case of multiple concurrent serializations + defaultWriteHashtable(s, length, loadFactor); + } + + // Write out simulated length and real count of elements + s.writeInt(length); + s.writeInt(count); + + // Write out the key/value objects from the stacked entries + for (int i = entryStack.size() - 1; i >= 0; i--) { + s.writeObject(entryStack.get(i)); + } + } + + @Override + void readHashtable(ObjectInputStream s) throws IOException, + ClassNotFoundException { + // Read in the threshold and loadfactor + s.defaultReadObject(); + + // Read the original length of the array and number of elements + int origlength = s.readInt(); + int elements = s.readInt(); + + // create CHM of appropriate capacity + map = new ConcurrentHashMap<>(elements); + + // Read all the key/value objects + for (; elements > 0; elements--) { + Object key = s.readObject(); + Object value = s.readObject(); + map.put(key, value); + } + } +}