Repository: incubator-juneau Updated Branches: refs/heads/master dabe77a54 -> 4bc7b351c
https://issues.apache.org/jira/browse/JUNEAU-4? Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/4bc7b351 Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/4bc7b351 Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/4bc7b351 Branch: refs/heads/master Commit: 4bc7b351c27cc14c0b1e0ce281a985a9fbe2c525 Parents: dabe77a Author: jamesbognar <[email protected]> Authored: Fri Aug 5 12:46:30 2016 -0400 Committer: jamesbognar <[email protected]> Committed: Fri Aug 5 12:46:30 2016 -0400 ---------------------------------------------------------------------- .../java/org/apache/juneau/ContextFactory.java | 125 ++++++++++--------- .../org/apache/juneau/internal/HashCode.java | 18 ++- .../org/apache/juneau/CT_ContextFactory.java | 1 - .../src/test/java/org/apache/juneau/a/A1.java | 4 + 4 files changed, 84 insertions(+), 64 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4bc7b351/org.apache.juneau/src/main/java/org/apache/juneau/ContextFactory.java ---------------------------------------------------------------------- diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/ContextFactory.java b/org.apache.juneau/src/main/java/org/apache/juneau/ContextFactory.java index becf8f1..bc957ca 100644 --- a/org.apache.juneau/src/main/java/org/apache/juneau/ContextFactory.java +++ b/org.apache.juneau/src/main/java/org/apache/juneau/ContextFactory.java @@ -239,7 +239,7 @@ public final class ContextFactory extends Lockable { // All configuration properties in this object. // Keys are property prefixes (e.g. 'BeanContext'). // Values are maps containing properties for that specific prefix. - private Map<String,PropertyMap> properties = new ConcurrentHashMap<String,PropertyMap>(); + private Map<String,PropertyMap> properties = new ConcurrentSkipListMap<String,PropertyMap>(); // Context cache. // This gets cleared every time any properties change on this object. @@ -248,7 +248,7 @@ public final class ContextFactory extends Lockable { // Global Context cache. // Context factories that are the 'same' will use the same maps from this cache. // 'same' means the context properties are all the same when converted to strings. - private static final ConcurrentHashMap<ContextFactory, ConcurrentHashMap<Class<? extends Context>,Context>> globalContextCache = new ConcurrentHashMap<ContextFactory, ConcurrentHashMap<Class<? extends Context>,Context>>(); + private static final ConcurrentHashMap<Integer, ConcurrentHashMap<Class<? extends Context>,Context>> globalContextCache = new ConcurrentHashMap<Integer, ConcurrentHashMap<Class<? extends Context>,Context>>(); private ReadWriteLock lock = new ReentrantReadWriteLock(); private Lock rl = lock.readLock(), wl = lock.writeLock(); @@ -264,7 +264,7 @@ public final class ContextFactory extends Lockable { private static Comparator<Object> PROPERTY_COMPARATOR = new Comparator<Object>() { @Override public int compare(Object o1, Object o2) { - return ContextFactory.toString(o1).compareTo(ContextFactory.toString(o2)); + return normalize(o1).toString().compareTo(normalize(o2).toString()); } }; @@ -546,9 +546,10 @@ public final class ContextFactory extends Lockable { if (! contexts.containsKey(c)) { // Try to get it from the global cache. - if (! globalContextCache.containsKey(this)) - globalContextCache.putIfAbsent(clone(), new ConcurrentHashMap<Class<? extends Context>,Context>()); - ConcurrentHashMap<Class<? extends Context>, Context> cacheForThisConfig = globalContextCache.get(this); + Integer key = hashCode(); + if (! globalContextCache.containsKey(key)) + globalContextCache.putIfAbsent(key, new ConcurrentHashMap<Class<? extends Context>,Context>()); + ConcurrentHashMap<Class<? extends Context>, Context> cacheForThisConfig = globalContextCache.get(key); if (! cacheForThisConfig.containsKey(c)) cacheForThisConfig.putIfAbsent(c, c.getConstructor(ContextFactory.class).newInstance(this)); @@ -733,16 +734,10 @@ public final class ContextFactory extends Lockable { @Override /* Object */ public int hashCode() { - return this.properties.hashCode(); - } - - @Override /* Object */ - public boolean equals(Object o) { - if (o instanceof ContextFactory) { - ContextFactory c = (ContextFactory)o; - return c.properties.equals(properties); - } - return false; + HashCode c = new HashCode(); + for (PropertyMap m : properties.values()) + c.add(m); + return c.get(); } //-------------------------------------------------------------------------------- @@ -750,6 +745,17 @@ public final class ContextFactory extends Lockable { //-------------------------------------------------------------------------------- /** + * Hashcode generator that treats strings and primitive values the same. + * (e.g. <code>123</code> and <js>"123"</js> result in the same hashcode.) + */ + protected static class NormalizingHashCode extends HashCode { + @Override /* HashCode */ + protected Object normalize(Object o) { + return ContextFactory.normalize(o); + } + } + + /** * Contains all the properties for a particular property prefix (e.g. <js>'BeanContext'</js>) * <p> * Instances of this map are immutable from outside this class. @@ -762,12 +768,14 @@ public final class ContextFactory extends Lockable { @SuppressWarnings("hiding") public class PropertyMap { - private Map<String,Property> map = new ConcurrentSkipListMap<String,Property>(); - volatile int hashCode = 0; - ReadWriteLock lock = new ReentrantReadWriteLock(); - Lock rl = lock.readLock(), wl = lock.writeLock(); + private final Map<String,Property> map = new ConcurrentSkipListMap<String,Property>(); + private volatile int hashCode = 0; + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + private final Lock rl = lock.readLock(), wl = lock.writeLock(); + private final String prefix; private PropertyMap(String prefix) { + this.prefix = prefix; prefix = prefix + '.'; Properties p = System.getProperties(); for (Map.Entry<Object,Object> e : p.entrySet()) @@ -779,6 +787,7 @@ public final class ContextFactory extends Lockable { * Copy constructor. */ private PropertyMap(PropertyMap orig) { + this.prefix = orig.prefix; for (Map.Entry<String,Property> e : orig.map.entrySet()) this.map.put(e.getKey(), Property.create(e.getValue().name, e.getValue().value())); } @@ -926,7 +935,7 @@ public final class ContextFactory extends Lockable { rl.lock(); try { if (hashCode == 0) { - HashCode c = HashCode.create(); + HashCode c = new HashCode().add(prefix); for (Property p : map.values()) c.add(p); this.hashCode = c.get(); @@ -955,15 +964,11 @@ public final class ContextFactory extends Lockable { @Override public String toString() { - ObjectMap m = new ObjectMap(); - m.put("id", System.identityHashCode(this)); - m.put("hashcode", hashCode()); - m.put("values", map); - return JsonSerializer.DEFAULT_LAX.toString(m); + return "PropertyMap(id="+System.identityHashCode(this)+")"; } } - private abstract static class Property implements Comparable<Property> { + private abstract static class Property { private final String name, type; private final Object value; @@ -1005,36 +1010,22 @@ public final class ContextFactory extends Lockable { @Override /* Object */ public int hashCode() { - HashCode c = HashCode.create().add(name); + HashCode c = new NormalizingHashCode().add(name); if (value instanceof Map) { for (Map.Entry<?,?> e : ((Map<?,?>)value).entrySet()) - c.add(ContextFactory.toString(e.getKey())).add(ContextFactory.toString(e.getValue())); + c.add(e.getKey()).add(e.getValue()); } else if (value instanceof Collection) { for (Object o : (Collection<?>)value) - c.add(ContextFactory.toString(o)); + c.add(o); } else { - c.add(ContextFactory.toString(value)); + c.add(value); } return c.get(); } - @Override /* Object */ - public boolean equals(Object o) { - if (o instanceof Property) { - Property p = (Property)o; - return ContextFactory.same(value, p.value); - } - return false; - } - - @Override - public int compareTo(Property p) { - return name.compareTo(p.name); - } - @Override public String toString() { - return JsonSerializer.DEFAULT_LAX.toString(value); + return "Property(name="+name+",type="+type+")"; } } @@ -1064,12 +1055,14 @@ public final class ContextFactory extends Lockable { for (Object o : (Collection<Object>)val) add(o); else { - String s = val.toString(); - if (s.startsWith("[") && s.endsWith("]")) { - try { - add(new ObjectList(s)); - return; - } catch (Exception e) {} + if (val instanceof String) { + String s = val.toString(); + if (s.startsWith("[") && s.endsWith("]")) { + try { + add(new ObjectList(s)); + return; + } catch (Exception e) {} + } } for (Object o : value) if (same(val, o)) @@ -1087,12 +1080,14 @@ public final class ContextFactory extends Lockable { for (Object o : (Collection<Object>)val) remove(o); else { - String s = val.toString(); - if (s.startsWith("[") && s.endsWith("]")) { - try { - remove(new ObjectList(s)); - return; - } catch (Exception e) {} + if (val instanceof String) { + String s = val.toString(); + if (s.startsWith("[") && s.endsWith("]")) { + try { + remove(new ObjectList(s)); + return; + } catch (Exception e) {} + } } for (Iterator<Object> i = value.iterator(); i.hasNext();) if (same(i.next(), val)) @@ -1201,10 +1196,18 @@ public final class ContextFactory extends Lockable { } } - private static String toString(Object o) { + /** + * Converts an object to a normalized form for comparison purposes. + * + * @param o The object to normalize. + * @return The normalized object. + */ + private static final Object normalize(Object o) { if (o instanceof Class) return ((Class<?>)o).getName(); - return o.toString(); + if (o instanceof Number || o instanceof Boolean) + return o.toString(); + return o; } /* @@ -1244,7 +1247,7 @@ public final class ContextFactory extends Lockable { } return false; } else { - return ContextFactory.toString(o1).equals(ContextFactory.toString(o2)); + return normalize(o1).equals(normalize(o2)); } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4bc7b351/org.apache.juneau/src/main/java/org/apache/juneau/internal/HashCode.java ---------------------------------------------------------------------- diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/HashCode.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/HashCode.java index 5bdfb80..fc1e72b 100644 --- a/org.apache.juneau/src/main/java/org/apache/juneau/internal/HashCode.java +++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/HashCode.java @@ -22,7 +22,7 @@ package org.apache.juneau.internal; * * @author James Bognar ([email protected]) */ -public final class HashCode { +public class HashCode { private int hashCode = 1; @@ -35,7 +35,6 @@ public final class HashCode { return new HashCode(); } - /** * Hashes the hashcode of the specified object into this object. * @@ -43,6 +42,7 @@ public final class HashCode { * @return This object (for method chaining). */ public HashCode add(Object o) { + o = normalize(o); add(o == null ? 1 : o.hashCode()); return this; } @@ -68,4 +68,18 @@ public final class HashCode { public int get() { return hashCode; } + + /** + * Converts the object to a normalized form before grabbing it's hashcode. + * Subclasses can override this method to provide specialized handling + * (e.g. converting numbers to strings so that <code>123</code> and <js>"123"</js> + * end up creating the same hashcode.) + * Default implementation does nothing. + * + * @param o The object to normalize before getting it's hashcode. + * @return The normalized object. + */ + protected Object normalize(Object o) { + return o; + } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4bc7b351/org.apache.juneau/src/test/java/org/apache/juneau/CT_ContextFactory.java ---------------------------------------------------------------------- diff --git a/org.apache.juneau/src/test/java/org/apache/juneau/CT_ContextFactory.java b/org.apache.juneau/src/test/java/org/apache/juneau/CT_ContextFactory.java index 865552c..271410c 100644 --- a/org.apache.juneau/src/test/java/org/apache/juneau/CT_ContextFactory.java +++ b/org.apache.juneau/src/test/java/org/apache/juneau/CT_ContextFactory.java @@ -202,7 +202,6 @@ public class CT_ContextFactory { ContextFactory.PropertyMap p1 = f1.getPropertyMap("A"); ContextFactory.PropertyMap p2 = f2.getPropertyMap("A"); assertEquals(p1.hashCode(), p2.hashCode()); - assertTrue(p1.equals(p2)); } @SuppressWarnings("unchecked") http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4bc7b351/org.apache.juneau/src/test/java/org/apache/juneau/a/A1.java ---------------------------------------------------------------------- diff --git a/org.apache.juneau/src/test/java/org/apache/juneau/a/A1.java b/org.apache.juneau/src/test/java/org/apache/juneau/a/A1.java index e93afa0..6b663c8 100755 --- a/org.apache.juneau/src/test/java/org/apache/juneau/a/A1.java +++ b/org.apache.juneau/src/test/java/org/apache/juneau/a/A1.java @@ -88,6 +88,7 @@ public class A1 { return x; } + @Bean(sort=true) public static class A2 { public int f1; protected int f2; @@ -112,6 +113,7 @@ public class A1 { } } + @Bean(sort=true) protected static class A3 { public int f1; protected int f2; @@ -136,6 +138,7 @@ public class A1 { } } + @Bean(sort=true) static class A4 { public int f1; protected int f2; @@ -160,6 +163,7 @@ public class A1 { } } + @Bean(sort=true) private static class A5 { public int f1; protected int f2;
