Merge pull request #75 to the 3.2 branch. Also remove @override

annotation for interface implementation to make it possible to compile
and use with java5
This commit is contained in:
norman 2011-11-23 15:07:02 +01:00
parent 8d40487ff7
commit 9424c26caf
11 changed files with 353 additions and 57 deletions

View File

@ -0,0 +1,31 @@
package org.jboss.netty.handler.codec.serialization;
import java.util.Map;
class CachingClassResolver implements ClassResolver {
private final Map<String, Class<?>> classCache;
private final ClassResolver delegate;
CachingClassResolver(ClassResolver delegate, Map<String, Class<?>> classCache) {
this.delegate = delegate;
this.classCache = classCache;
}
@Override
public Class<?> resolve(String className) throws ClassNotFoundException {
// Query the cache first.
Class<?> clazz;
clazz = classCache.get(className);
if (clazz != null) {
return clazz;
}
// And then try to load.
clazz = delegate.resolve(className);
classCache.put(className, clazz);
return clazz;
}
}

View File

@ -0,0 +1,11 @@
package org.jboss.netty.handler.codec.serialization;
/**
* please use {@link ClassResolvers} as instance factory
*/
public interface ClassResolver {
Class<?> resolve(String className) throws ClassNotFoundException;
}

View File

@ -0,0 +1,74 @@
package org.jboss.netty.handler.codec.serialization;
import java.lang.ref.Reference;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
public class ClassResolvers {
/**
* cache disabled
* @param classLoader - specific classLoader to use, or null if you want to revert to default
* @return new instance of class resolver
*/
public static ClassResolver cacheDisabled(ClassLoader classLoader) {
return new ClassloaderClassResolver(defaultClassLoader(classLoader));
}
/**
* 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<String, Class<?>>(new HashMap<String, Reference<Class<?>>>()));
}
/**
* 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<String, Class<?>>(new HashMap<String, Reference<Class<?>>>()));
}
/**
* 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<String, Class<?>>(new ConcurrentHashMap<String, Reference<Class<?>>>()));
}
/**
* 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<String, Class<?>>(new ConcurrentHashMap<String, Reference<Class<?>>>()));
}
static ClassLoader defaultClassLoader(ClassLoader classLoader) {
if (classLoader != null) {
return classLoader;
}
final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
if (contextClassLoader != null) {
return contextClassLoader;
}
return ClassResolvers.class.getClassLoader();
}
}

View File

@ -0,0 +1,24 @@
package org.jboss.netty.handler.codec.serialization;
class ClassloaderClassResolver implements ClassResolver {
private final ClassLoader classLoader;
ClassloaderClassResolver(ClassLoader classLoader) {
this.classLoader = classLoader;
}
/*
* (non-Javadoc)
* @see org.jboss.netty.handler.codec.serialization.ClassResolver#resolve(java.lang.String)
*/
public Class<?> resolve(String className) throws ClassNotFoundException {
try {
return classLoader.loadClass(className);
} catch (ClassNotFoundException e) {
return Class.forName(className, false, classLoader);
}
}
}

View File

@ -21,8 +21,6 @@ import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.io.StreamCorruptedException;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
@ -32,22 +30,16 @@ import java.util.Map;
*
*/
class CompactObjectInputStream extends ObjectInputStream {
private final ClassResolver classResolver;
private final Map<String, Class<?>> classCache = new HashMap<String, Class<?>>();
private final ClassLoader classLoader;
CompactObjectInputStream(InputStream in) throws IOException {
this(in, null);
}
CompactObjectInputStream(InputStream in, ClassLoader classLoader) throws IOException {
CompactObjectInputStream(InputStream in, ClassResolver classResolver) throws IOException {
super(in);
this.classLoader = classLoader;
this.classResolver = classResolver;
}
@Override
protected void readStreamHeader() throws IOException,
StreamCorruptedException {
protected void readStreamHeader() throws IOException {
int version = readByte() & 0xFF;
if (version != STREAM_VERSION) {
throw new StreamCorruptedException(
@ -67,7 +59,7 @@ class CompactObjectInputStream extends ObjectInputStream {
return super.readClassDescriptor();
case CompactObjectOutputStream.TYPE_THIN_DESCRIPTOR:
String className = readUTF();
Class<?> clazz = loadClass(className);
Class<?> clazz = classResolver.resolve(className);
return ObjectStreamClass.lookupAny(clazz);
default:
throw new StreamCorruptedException(
@ -77,45 +69,14 @@ class CompactObjectInputStream extends ObjectInputStream {
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
// Query the cache first.
String className = desc.getName();
Class<?> clazz = classCache.get(className);
if (clazz != null) {
return clazz;
}
// And then try to resolve.
Class<?> clazz;
try {
clazz = loadClass(className);
clazz = classResolver.resolve(desc.getName());
} catch (ClassNotFoundException ex) {
clazz = super.resolveClass(desc);
}
classCache.put(className, clazz);
return clazz;
}
protected Class<?> loadClass(String className) throws ClassNotFoundException {
// Query the cache first.
Class<?> clazz;
clazz = classCache.get(className);
if (clazz != null) {
return clazz;
}
// And then try to load.
ClassLoader classLoader = this.classLoader;
if (classLoader == null) {
classLoader = Thread.currentThread().getContextClassLoader();
}
if (classLoader != null) {
clazz = classLoader.loadClass(className);
} else {
clazz = Class.forName(className);
}
classCache.put(className, clazz);
return clazz;
}
}

View File

@ -43,18 +43,34 @@ import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
*/
public class ObjectDecoder extends LengthFieldBasedFrameDecoder {
private final ClassLoader classLoader;
private final ClassResolver classResolver;
/**
* Creates a new decoder whose maximum object size is {@code 1048576}
* bytes. If the size of the received object is greater than
* {@code 1048576} bytes, a {@link StreamCorruptedException} will be
* raised.
*
* @deprecated use {@link #ObjectDecoder(ClassResolver)}
*/
@Deprecated
public ObjectDecoder() {
this(1048576);
}
/**
* Creates a new decoder whose maximum object size is {@code 1048576}
* bytes. If the size of the received object is greater than
* {@code 1048576} bytes, a {@link StreamCorruptedException} will be
* raised.
*
* @param classResolver the {@link ClassResolver} to use for this decoder
*/
public ObjectDecoder(ClassResolver classResolver) {
this(1048576, classResolver);
}
/**
* Creates a new decoder with the specified maximum object size.
*
@ -62,9 +78,11 @@ public class ObjectDecoder extends LengthFieldBasedFrameDecoder {
* if the length of the received object is greater
* than this value, {@link StreamCorruptedException}
* will be raised.
* @deprecated use {@link #ObjectDecoder(int, ClassResolver)}
*/
@Deprecated
public ObjectDecoder(int maxObjectSize) {
this(maxObjectSize, null);
this(maxObjectSize, ClassResolvers.weakCachingResolver(null));
}
/**
@ -74,14 +92,31 @@ public class ObjectDecoder extends LengthFieldBasedFrameDecoder {
* if the length of the received object is greater
* than this value, {@link StreamCorruptedException}
* will be raised.
* @param classLoader the {@link ClassLoader} which will load the class
* @param classResolver the {@link ClassResolver} which will load the class
* of the serialized object
*/
public ObjectDecoder(int maxObjectSize, ClassLoader classLoader) {
public ObjectDecoder(int maxObjectSize, ClassResolver classResolver) {
super(maxObjectSize, 0, 4, 0, 4);
this.classLoader = classLoader;
this.classResolver = classResolver;
}
/**
* Create a new decoder with the specified maximum object size and the {@link ClassLoader} wrapped in {@link ClassResolvers#weakCachingResolver(ClassLoader)}
*
*
* @param maxObjectSize the maximum byte length of the serialized object.
* if the length of the received object is greater
* than this value, {@link StreamCorruptedException}
* will be raised.
* @param classLoader the the classloader to use
* @deprecated use {@link #ObjectDecoder(int, ClassResolver)}
*/
@Deprecated
public ObjectDecoder(int maxObjectSize, ClassLoader classLoader) {
this(maxObjectSize, ClassResolvers.weakCachingResolver(classLoader));
}
@Override
protected Object decode(
ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
@ -92,7 +127,7 @@ public class ObjectDecoder extends LengthFieldBasedFrameDecoder {
}
return new CompactObjectInputStream(
new ChannelBufferInputStream(frame), classLoader).readObject();
new ChannelBufferInputStream(frame), classResolver).readObject();
}
@Override

View File

@ -35,8 +35,8 @@ public class ObjectDecoderInputStream extends InputStream implements
ObjectInput {
private final DataInputStream in;
private final ClassLoader classLoader;
private final int maxObjectSize;
private final ClassResolver classResolver;
/**
* Creates a new {@link ObjectInput}.
@ -104,7 +104,7 @@ public class ObjectDecoderInputStream extends InputStream implements
} else {
this.in = new DataInputStream(in);
}
this.classLoader = classLoader;
this.classResolver = ClassResolvers.weakCachingResolver(classLoader);
this.maxObjectSize = maxObjectSize;
}
@ -118,7 +118,7 @@ public class ObjectDecoderInputStream extends InputStream implements
"data length too big: " + dataLen + " (max: " + maxObjectSize + ')');
}
return new CompactObjectInputStream(in, classLoader).readObject();
return new CompactObjectInputStream(in, classResolver).readObject();
}
@Override

View File

@ -0,0 +1,124 @@
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<K, V> implements Map<K, V> {
private final Map<K, Reference<V>> delegate;
protected ReferenceMap(Map<K, Reference<V>> delegate) {
this.delegate = delegate;
}
abstract Reference<V> fold(V value);
private V unfold(Reference<V> ref) {
if (ref == null) {
return null;
}
return ref.get();
}
/*
* (non-Javadoc)
* @see java.util.Map#size()
*/
public int size() {
return delegate.size();
}
/*
* (non-Javadoc)
* @see java.util.Map#isEmpty()
*/
public boolean isEmpty() {
return delegate.isEmpty();
}
/*
* (non-Javadoc)
* @see java.util.Map#containsKey(java.lang.Object)
*/
public boolean containsKey(Object key) {
return delegate.containsKey(key);
}
/*
* (non-Javadoc)
* @see java.util.Map#containsValue(java.lang.Object)
*/
public boolean containsValue(Object value) {
throw new UnsupportedOperationException();
}
/*
* (non-Javadoc)
* @see java.util.Map#get(java.lang.Object)
*/
public V get(Object key) {
return unfold(delegate.get(key));
}
/*
* (non-Javadoc)
* @see java.util.Map#put(java.lang.Object, java.lang.Object)
*/
public V put(K key, V value) {
return unfold(delegate.put(key, fold(value)));
}
/*
* (non-Javadoc)
* @see java.util.Map#remove(java.lang.Object)
*/
public V remove(Object key) {
return unfold(delegate.remove(key));
}
/*
* (non-Javadoc)
* @see java.util.Map#putAll(java.util.Map)
*/
public void putAll(Map<? extends K, ? extends V> m) {
for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
delegate.put(entry.getKey(), fold(entry.getValue()));
}
}
/*
* (non-Javadoc)
* @see java.util.Map#clear()
*/
public void clear() {
delegate.clear();
}
/*
* (non-Javadoc)
* @see java.util.Map#keySet()
*/
public Set<K> keySet() {
return delegate.keySet();
}
/*
* (non-Javadoc)
* @see java.util.Map#values()
*/
public Collection<V> values() {
throw new UnsupportedOperationException();
}
/*
* (non-Javadoc)
* @see java.util.Map#entrySet()
*/
public Set<Entry<K, V>> entrySet() {
throw new UnsupportedOperationException();
}
}

View File

@ -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<K, V> extends ReferenceMap<K, V> {
public SoftReferenceMap(Map<K, Reference<V>> delegate) {
super(delegate);
}
@Override
Reference<V> fold(V value) {
return new SoftReference<V>(value);
}
}

View File

@ -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<K, V> extends ReferenceMap<K, V> {
public WeakReferenceMap(Map<K, Reference<V>> delegate) {
super(delegate);
}
@Override
Reference<V> fold(V value) {
return new WeakReference<V>(value);
}
}

View File

@ -32,7 +32,7 @@ public class CompactObjectSerializationTest {
PipedOutputStream pipeOut = new PipedOutputStream();
PipedInputStream pipeIn = new PipedInputStream(pipeOut);
CompactObjectOutputStream out = new CompactObjectOutputStream(pipeOut);
CompactObjectInputStream in = new CompactObjectInputStream(pipeIn);
CompactObjectInputStream in = new CompactObjectInputStream(pipeIn, ClassResolvers.cacheDisabled(null));
out.writeObject(List.class);
Assert.assertSame(List.class, in.readObject());
}