From 7e6d07afaf057df683a48901f23aff55a117588e Mon Sep 17 00:00:00 2001 From: alepar Date: Tue, 22 Nov 2011 18:16:30 +0400 Subject: [PATCH] use weak cache as default cache - it doesn't break class unloading providing multiple implementations for caching, each good for its own specific use-case --- .../codec/serialization/ClassResolver.java | 4 + .../codec/serialization/ClassResolvers.java | 58 +++++++++++-- .../codec/serialization/ObjectDecoder.java | 2 +- .../ObjectDecoderInputStream.java | 2 +- .../codec/serialization/ReferenceMap.java | 87 +++++++++++++++++++ .../codec/serialization/SoftReferenceMap.java | 18 ++++ .../codec/serialization/WeakReferenceMap.java | 18 ++++ 7 files changed, 179 insertions(+), 10 deletions(-) create mode 100644 src/main/java/org/jboss/netty/handler/codec/serialization/ReferenceMap.java create mode 100644 src/main/java/org/jboss/netty/handler/codec/serialization/SoftReferenceMap.java create mode 100644 src/main/java/org/jboss/netty/handler/codec/serialization/WeakReferenceMap.java diff --git a/src/main/java/org/jboss/netty/handler/codec/serialization/ClassResolver.java b/src/main/java/org/jboss/netty/handler/codec/serialization/ClassResolver.java index a6fa1c52d7..6f9f13b887 100644 --- a/src/main/java/org/jboss/netty/handler/codec/serialization/ClassResolver.java +++ b/src/main/java/org/jboss/netty/handler/codec/serialization/ClassResolver.java @@ -1,5 +1,9 @@ package org.jboss.netty.handler.codec.serialization; + +/** + * please use {@link ClassResolvers} as instance factory + */ interface ClassResolver { Class resolve(String className) throws ClassNotFoundException; diff --git a/src/main/java/org/jboss/netty/handler/codec/serialization/ClassResolvers.java b/src/main/java/org/jboss/netty/handler/codec/serialization/ClassResolvers.java index e802838080..4df8e3d3bc 100644 --- a/src/main/java/org/jboss/netty/handler/codec/serialization/ClassResolvers.java +++ b/src/main/java/org/jboss/netty/handler/codec/serialization/ClassResolvers.java @@ -1,24 +1,66 @@ package org.jboss.netty.handler.codec.serialization; +import java.lang.ref.Reference; import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; public class ClassResolvers { - - public static ClassResolver cachingResolver(ClassLoader classLoader) { - if (classLoader == null) { - classLoader = defaultClassLoader(); - } - return new CachingClassResolver(new ClassloaderClassResolver(classLoader), new HashMap>()); + /** + * non-agressive non-concurrent cache + * good for non-shared default cache + * + * @param classLoader - specific classLoader to use, or null if you want to revert to default + * @return new instance of class resolver + */ + public static ClassResolver weakCachingResolver(ClassLoader classLoader) { + return new CachingClassResolver(new ClassloaderClassResolver(defaultClassLoader(classLoader)), new WeakReferenceMap>(new HashMap>>())); } - static ClassLoader defaultClassLoader() { + /** + * agressive non-concurrent cache + * good for non-shared cache, when we're not worried about class unloading + * + * @param classLoader - specific classLoader to use, or null if you want to revert to default + * @return new instance of class resolver + */ + public static ClassResolver softCachingResolver(ClassLoader classLoader) { + return new CachingClassResolver(new ClassloaderClassResolver(defaultClassLoader(classLoader)), new SoftReferenceMap>(new HashMap>>())); + } + + /** + * non-agressive concurrent cache + * good for shared cache, when we're worried about class unloading + * + * @param classLoader - specific classLoader to use, or null if you want to revert to default + * @return new instance of class resolver + */ + public static ClassResolver weakCachingConcurrentResolver(ClassLoader classLoader) { + return new CachingClassResolver(new ClassloaderClassResolver(defaultClassLoader(classLoader)), new WeakReferenceMap>(new ConcurrentHashMap>>())); + } + + /** + * agressive concurrent cache + * good for shared cache, when we're not worried about class unloading + * + * @param classLoader - specific classLoader to use, or null if you want to revert to default + * @return new instance of class resolver + */ + public static ClassResolver softCachingConcurrentResolver(ClassLoader classLoader) { + return new CachingClassResolver(new ClassloaderClassResolver(defaultClassLoader(classLoader)), new SoftReferenceMap>(new ConcurrentHashMap>>())); + } + + static ClassLoader defaultClassLoader(ClassLoader classLoader) { + if (classLoader != null) { + return classLoader; + } + final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); if (contextClassLoader != null) { return contextClassLoader; } - return CompactObjectInputStream.class.getClassLoader(); + return ClassResolvers.class.getClassLoader(); } } diff --git a/src/main/java/org/jboss/netty/handler/codec/serialization/ObjectDecoder.java b/src/main/java/org/jboss/netty/handler/codec/serialization/ObjectDecoder.java index 4efab6c9c8..73fd1eb2c0 100644 --- a/src/main/java/org/jboss/netty/handler/codec/serialization/ObjectDecoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/serialization/ObjectDecoder.java @@ -64,7 +64,7 @@ public class ObjectDecoder extends LengthFieldBasedFrameDecoder { * will be raised. */ public ObjectDecoder(int maxObjectSize) { - this(maxObjectSize, ClassResolvers.cachingResolver(null)); + this(maxObjectSize, ClassResolvers.weakCachingResolver(null)); } /** diff --git a/src/main/java/org/jboss/netty/handler/codec/serialization/ObjectDecoderInputStream.java b/src/main/java/org/jboss/netty/handler/codec/serialization/ObjectDecoderInputStream.java index a1590fee8c..234035bd74 100644 --- a/src/main/java/org/jboss/netty/handler/codec/serialization/ObjectDecoderInputStream.java +++ b/src/main/java/org/jboss/netty/handler/codec/serialization/ObjectDecoderInputStream.java @@ -104,7 +104,7 @@ public class ObjectDecoderInputStream extends InputStream implements } else { this.in = new DataInputStream(in); } - this.classResolver = ClassResolvers.cachingResolver(classLoader); + this.classResolver = ClassResolvers.weakCachingResolver(classLoader); this.maxObjectSize = maxObjectSize; } diff --git a/src/main/java/org/jboss/netty/handler/codec/serialization/ReferenceMap.java b/src/main/java/org/jboss/netty/handler/codec/serialization/ReferenceMap.java new file mode 100644 index 0000000000..64a3e3c750 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/codec/serialization/ReferenceMap.java @@ -0,0 +1,87 @@ +package org.jboss.netty.handler.codec.serialization; + +import java.lang.ref.Reference; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +abstract class ReferenceMap implements Map { + + private final Map> delegate; + + protected ReferenceMap(Map> delegate) { + this.delegate = delegate; + } + + abstract Reference fold(V value); + + private V unfold(Reference ref) { + if (ref == null) { + return null; + } + + return ref.get(); + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return delegate.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public V get(Object key) { + return unfold(delegate.get(key)); + } + + @Override + public V put(K key, V value) { + return unfold(delegate.put(key, fold(value))); + } + + @Override + public V remove(Object key) { + return unfold(delegate.remove(key)); + } + + @Override + public void putAll(Map m) { + for (Entry entry : m.entrySet()) { + delegate.put(entry.getKey(), fold(entry.getValue())); + } + } + + @Override + public void clear() { + delegate.clear(); + } + + @Override + public Set keySet() { + return delegate.keySet(); + } + + @Override + public Collection values() { + throw new UnsupportedOperationException(); + } + + @Override + public Set> entrySet() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/org/jboss/netty/handler/codec/serialization/SoftReferenceMap.java b/src/main/java/org/jboss/netty/handler/codec/serialization/SoftReferenceMap.java new file mode 100644 index 0000000000..7bf82e02da --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/codec/serialization/SoftReferenceMap.java @@ -0,0 +1,18 @@ +package org.jboss.netty.handler.codec.serialization; + +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.util.Map; + +public class SoftReferenceMap extends ReferenceMap { + + public SoftReferenceMap(Map> delegate) { + super(delegate); + } + + @Override + Reference fold(V value) { + return new SoftReference(value); + } + +} diff --git a/src/main/java/org/jboss/netty/handler/codec/serialization/WeakReferenceMap.java b/src/main/java/org/jboss/netty/handler/codec/serialization/WeakReferenceMap.java new file mode 100644 index 0000000000..9217baac0c --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/codec/serialization/WeakReferenceMap.java @@ -0,0 +1,18 @@ +package org.jboss.netty.handler.codec.serialization; + +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.Map; + +public class WeakReferenceMap extends ReferenceMap { + + public WeakReferenceMap(Map> delegate) { + super(delegate); + } + + @Override + Reference fold(V value) { + return new WeakReference(value); + } + +}