diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index d83b54060..2ad0107d6 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -20,6 +20,7 @@ import net.i2p.data.Base64; import net.i2p.data.DataHelper; import net.i2p.util.I2PAppThread; import net.i2p.util.Log; +import net.i2p.util.OrderedProperties; /** * Manage multiple snarks @@ -126,7 +127,7 @@ public class SnarkManager implements Snark.CompleteListener { /** null to set initial defaults */ public void loadConfig(String filename) { if (_config == null) - _config = new Properties(); + _config = new OrderedProperties(); if (filename != null) { File cfg = new File(filename); if (!cfg.isAbsolute()) diff --git a/core/java/src/net/i2p/util/OrderedProperties.java b/core/java/src/net/i2p/util/OrderedProperties.java index 3c10dcb92..731f27a85 100644 --- a/core/java/src/net/i2p/util/OrderedProperties.java +++ b/core/java/src/net/i2p/util/OrderedProperties.java @@ -9,371 +9,46 @@ package net.i2p.util; * */ -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Iterator; +import java.util.Comparator; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TreeSet; -import net.i2p.data.DataHelper; - /** * Properties map that has its keySet ordered consistently (via the key's lexicographical ordering). * This is useful in environments where maps must stay the same order (e.g. for signature verification) * This does NOT support remove against the iterators / etc. * + * @author zzz Rewritten + * + * Now unsorted until the keyset or entryset is requested. + * The class is unsynchronized. + * The keySet() and entrySet() methods return ordered sets. + * Others - such as the enumerations values(), keys(), propertyNames() - do not. */ public class OrderedProperties extends Properties { - private final static Log _log = new Log(OrderedProperties.class); - /** ordered set of keys (strings) stored in the properties */ - private TreeSet _order; - /** simple key=value mapping of the actual data */ - private Map _data; - - /** lock this before touching _order or _data */ - private final Object _lock = new Object(); public OrderedProperties() { super(); - _order = new TreeSet(); - _data = new HashMap(); - } - - @Override - public boolean contains(Object value) { - return containsValue(value); - } - - @Override - public boolean containsKey(Object key) { - synchronized (_lock) { - return _data.containsKey(key); - } - } - - @Override - public boolean containsValue(Object value) { - synchronized (_lock) { - return _data.containsValue(value); - } - } - - @Override - public boolean equals(Object obj) { - if ((obj != null) && (obj instanceof OrderedProperties)) { - synchronized (_lock) { - return _data.equals(obj); - } - } - - return false; - } - - @Override - public int hashCode() { - synchronized (_lock) { - return _data.hashCode(); - } - } - - @Override - public boolean isEmpty() { - return size() == 0; - } - - @Override - public String getProperty(String key) { - return getProperty((Object) key); - } - - @Override - public Object get(Object key) { - return getProperty(key); - } - - private String getProperty(Object key) { - if (key == null) return null; - synchronized (_lock) { - Object rv = _data.get(key); - if ((rv != null) && (rv instanceof String)) return (String) rv; - - return null; - } - } - - @Override - public Object setProperty(String key, String val) { - if ((key == null) || (val == null)) throw new IllegalArgumentException("Null values are not supported"); - synchronized (_lock) { - _order.add(key); - Object rv = _data.put(key, val); - return rv; - } - } - - @Override - public Object put(Object key, Object val) { - if ((key == null) || (val == null)) throw new NullPointerException("Null values or keys are not allowed"); - if (!(key instanceof String) || !(val instanceof String)) - throw new IllegalArgumentException("Key or value is not a string"); - return setProperty((String) key, (String) val); - } - - @Override - public void putAll(Map data) { - if (data == null) return; - for (Iterator iter = data.entrySet().iterator(); iter.hasNext();) { - Map.Entry entry = (Map.Entry)iter.next(); - Object key = entry.getKey(); - Object val = entry.getValue(); - put(key, val); - } - } - - @Override - public Object clone() { - synchronized (_lock) { - OrderedProperties rv = new OrderedProperties(); - rv.putAll(this); - return rv; - } - } - - @Override - public void clear() { - synchronized (_lock) { - _order.clear(); - _data.clear(); - } - } - - @Override - public int size() { - synchronized (_lock) { - return _order.size(); - } - } - - @Override - public Object remove(Object key) { - synchronized (_lock) { - _order.remove(key); - Object rv = _data.remove(key); - return rv; - } } @Override public Set keySet() { - synchronized (_lock) { - return Collections.unmodifiableSortedSet((TreeSet) _order.clone()); - } + return Collections.unmodifiableSortedSet(new TreeSet(super.keySet())); } @Override public Set entrySet() { - synchronized (_lock) { - return Collections.unmodifiableSet(buildEntrySet((TreeSet) _order.clone())); - } + TreeSet rv = new TreeSet(new EntryComparator()); + rv.addAll(super.entrySet()); + return Collections.unmodifiableSortedSet(rv); } - @Override - public Collection values() { - synchronized (_lock) { - Collection values = new ArrayList(_data.size()); - for (Iterator iter = _data.values().iterator(); iter.hasNext();) { - values.add(iter.next()); - } - return values; - } - } - - @Override - public Enumeration elements() { - return Collections.enumeration(values()); - } - - @Override - public Enumeration keys() { - return Collections.enumeration(keySet()); - } - - @Override - public Enumeration propertyNames() { - return Collections.enumeration(keySet()); - } - - @Override - public void list(PrintStream out) { // nop - } - - @Override - public void list(PrintWriter out) { // nop - } - - @Override - public void load(InputStream in) { // nop - } - - //public void save(OutputStream out, String header) {} - @Override - public void store(OutputStream out, String header) { // nop - } - - private Set buildEntrySet(Set data) { - TreeSet ts = new TreeSet(); - for (Iterator iter = data.iterator(); iter.hasNext();) { - String key = (String) iter.next(); - String val = getProperty(key); - ts.add(new StringMapEntry(key, val)); - } - return ts; - } - - private static class StringMapEntry implements Map.Entry, Comparable { - private Object _key; - private Object _value; - - public StringMapEntry(String key, String val) { - _key = key; - _value = val; - } - - public Object getKey() { - return _key; - } - - public Object getValue() { - return _value; - } - - public Object setValue(Object value) { - Object old = _value; - _value = value; - return old; - } - - public int compareTo(Object o) { - if (o == null) return -1; - if (o instanceof StringMapEntry) return ((String) getKey()).compareTo((String)((StringMapEntry) o).getKey()); - if (o instanceof String) return ((String) getKey()).compareTo((String)o); - return -2; - } - - /* FIXME missing hashCode() method FIXME */ - @Override - public boolean equals(Object o) { - if (o == null) return false; - if (!(o instanceof StringMapEntry)) return false; - StringMapEntry e = (StringMapEntry) o; - return DataHelper.eq(e.getKey(), getKey()) && DataHelper.eq(e.getValue(), getValue()); - } - } - - /// - /// tests - /// - - public static void main(String args[]) { - test(new OrderedProperties()); - _log.debug("After ordered"); - //test(new Properties()); - //System.out.println("After normal"); - test2(); - testThrash(); - } - - private static void test2() { - OrderedProperties p = new OrderedProperties(); - p.setProperty("a", "b"); - p.setProperty("c", "d"); - OrderedProperties p2 = new OrderedProperties(); - try { - p2.putAll(p); - } catch (Throwable t) { - t.printStackTrace(); - } - _log.debug("After test2"); - } - - private static void test(Properties p) { - for (int i = 0; i < 10; i++) - p.setProperty(i + "asdfasdfasdf", "qwerasdfqwer"); - for (Iterator iter = p.keySet().iterator(); iter.hasNext();) { - String key = (String) iter.next(); - String val = p.getProperty(key); - _log.debug("[" + key + "] = [" + val + "]"); - } - p.remove(4 + "asdfasdfasdf"); - _log.debug("After remove"); - for (Iterator iter = p.keySet().iterator(); iter.hasNext();) { - String key = (String) iter.next(); - String val = p.getProperty(key); - _log.debug("[" + key + "] = [" + val + "]"); - } - try { - p.put("nullVal", null); - _log.debug("Null put did NOT fail!"); - } catch (NullPointerException npe) { - _log.debug("Null put failed correctly"); - } - } - - /** - * Set 100 concurrent threads trying to do some operations against a single - * OrderedProperties object a thousand times. Hopefully this will help - * flesh out any synchronization issues. - * - */ - private static void testThrash() { - OrderedProperties prop = new OrderedProperties(); - for (int i = 0; i < 100; i++) - prop.setProperty(i + "", i + " value"); - _log.debug("Thrash properties built"); - for (int i = 0; i < 100; i++) - thrash(prop, i); - } - - private static void thrash(Properties props, int i) { - I2PThread t = new I2PThread(new Thrash(props)); - t.setName("Thrash" + i); - t.start(); - } - - private static class Thrash implements Runnable { - private Properties _props; - - public Thrash(Properties props) { - _props = props; - } - - public void run() { - int numRuns = 1000; - _log.debug("Begin thrashing " + numRuns + " times"); - for (int i = 0; i < numRuns; i++) { - Set keys = _props.keySet(); - //_log.debug("keySet fetched"); - int cur = 0; - for (Iterator iter = keys.iterator(); iter.hasNext();) { - Object o = iter.next(); - Object val = _props.get(o); - //_log.debug("Value " + cur + " fetched"); - cur++; - } - //_log.debug("Values fetched"); - int size = _props.size(); - _log.debug("Size calculated"); - } - _log.debug("Done thrashing " + numRuns + " times"); + private static class EntryComparator implements Comparator { + public int compare(Object l, Object r) { + return ((String)((Map.Entry)l).getKey()).compareTo(((String)((Map.Entry)r).getKey())); } } }