CGLibProxySerializer
CGLibProxySerializer only works if proxy object has one callback look at http://grepcode.com/file/repository.springsource.com/net.sourceforge.cglib/com.springsource.net.sf.cglib/2.2.0/net/sf/cglib/proxy/Enhancer.java?av=f#354 without filter you cann't recreate proxy object
http://sourceforge.net/p/cglib/bugs/6/
Can you provide an example that shows the issue?
cglib-2.2.0
import java.lang.reflect.Method;
import net.sf.cglib.proxy.*;
import java.io.*;
import org.junit.*;
import static org.junit.Assert.*;
public class Issue18Test {
@BeforeClass
public static void setUpClass() throws Exception {
}
@AfterClass
public static void tearDownClass() throws Exception {
}
@Before
public void setUp() {
}
@After
public void tearDown() {
}
public Issue18Test(){}
@Test
public void test(){
Bad joe=new Bad(50);
Enhancer e=new Enhancer();
e.setSuperclass(Bad.class);
e.setCallbacks(new Callback[]{new SerializableNoOp()});
Bad simpleProxy=null;
System.out.println("--simple proxy with one callbacks--");
try{
simpleProxy=(Bad)e.create();
assertTrue(true);
assertNotSame(joe, simpleProxy);
System.out.println("No errors");
}catch(IllegalStateException ex){
System.out.println(ex.getMessage());
assertTrue(false);
}
e=new Enhancer();
e.setSuperclass(Bad.class);
e.setCallbacks(new Callback[]{new SerializableNoOp(), new StaticUnadvisedInterceptor(joe)});
Bad joeProxy=null;
System.out.println("--composite proxy without filter--");
try{
joeProxy=(Bad)e.create();
assertTrue(false);
}catch(IllegalStateException ex){
System.out.println("Error:"+ex.getMessage());
assertTrue(true);
}
e.setCallbackFilter(new ProxyCallbackFilter());
System.out.println("--composite proxy with filter--");
try{
joeProxy=(Bad)e.create();
assertTrue(true);
assertEquals(joe, joeProxy);
System.out.println("No errors");
}catch(IllegalStateException ex){
System.out.println(ex.getMessage());
assertTrue(false);
}
}
/**
* pojo
*/
public static class Bad{
private int _score;
public Bad(){
}
public Bad(int score){
_score=score;
}
public int getScore() {
return _score;
}
public void setScore(int _score) {
this._score = _score;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Bad)) return false;
return _score==((Bad)obj).getScore();
}
}
/**
* Callbacks and filter
*/
public static class SerializableNoOp implements NoOp, Serializable {
}
private static Object massageReturnTypeIfNecessary(Object proxy, Object target, Method method, Object retVal) {
if (retVal != null && retVal == target) {
retVal = proxy;
}
return retVal;
}
private static class StaticUnadvisedInterceptor implements MethodInterceptor, Serializable {
private final Object target;
public StaticUnadvisedInterceptor(Object target) {
this.target = target;
}
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object retVal = methodProxy.invoke(this.target, args);
return massageReturnTypeIfNecessary(proxy, this.target, method, retVal);
}
}
private static class ProxyCallbackFilter implements CallbackFilter {
@Override
public int accept(Method method) {
return 1;
}
}
}
Thanx. I ran the test in the kryo1 branch (in master there's a VerifyError), and there it's green. The console prints
--simple proxy with one callbacks--
No errors
--composite proxy without filter--
Error:Multiple callback types possible but no filter specified
--composite proxy with filter--
No errors
PASSED: test
I haven't looked in details what's happening, can you tell what should be changed to CGLibProxySerializer?
Btw, are you working on master or kryo1 branch?
Btw, are you working on master or kryo1 branch? I'm working with my project (tomcat redis session). I was looking solution for this problem but found only your serilization class but it has also problem. I haven't looked in details what's happening, can you tell what should be changed to CGLibProxySerializer? You have to add exception when many callbacks or fix cglb because developers hide filter in constructor and i didn't find solution how to pull filter from proxy.
Tomcat redis session sounds interesting, which project exactly are you referring to? I'm working on memcached-session-manager btw, which should get pluggable backends soon, with an implementation for Redis provided. Maybe we can combine the forces?
You have to add exceptin when many callbacks or fix cglb because developers hide filter in constructor and i didn't find solution how to pull filter from proxy.
Do you want to submit a pull request? I can publish a release soon then.
we took as a basis https://github.com/jcoleman/tomcat-redis-session-manager, now we are facing with problem: spring session scope.
with an implementation for Redis provided
Do you want to implement redis in your project? Did you test with spring mvc+tomcat?
maybe the better solution for cglib will be the check: does have proxy class writeReplace function
Do you want to implement redis in your project?
Yes. There's this pull request that also comes with a Redis implementation which I want to pick up soon.
Did you test with spring mvc+tomcat?
We used spring session scope as well, but IIRC I didn't test spring mvc separately (we were using wicket).
maybe the better solution for cglib will be the check: does have proxy class writeReplace function
Yes, this would be possible. But when using kryo, wouldn't it be better to rely on kryo concepts to serialize/deserialize the callbacks (the user can supply/register appropriate kryo serializers)?
wouldn't it be better to rely on kryo concepts to serialize/deserialize the callbacks (the user can supply/register appropriate kryo serializers)?
Yes, it will be better, but serialization for cglib will be work only if somebody fix cglib and add filter to proxy as field For example spring has own writeReplae for bean, look at http://grepcode.com/file/repo1.maven.org/maven2/org.springframework/spring-beans/3.1.2.RELEASE/org/springframework/beans/factory/support/DefaultListableBeanFactory.java#984
Shouldn't it work to register the kryo JavaSerializer for DefaultListableBeanFactory?
Do you have a simple spring sample application that we could use to create a test case for this?
Do you have a simple spring sample application that we could use to create a test case for this?
I'll prepare simple app with spring session scope, now i'm researching how to work DefaultListableBeanFactory, because function writeReplace works with standartsession(tomcat) writeObject, but via custom serialization doesnt' (something strange) spring session scope is a proxy cglib object (if you set in context aop:scoped-proxy proxy-target-class="true") and when serializing must be called writeReplace, because for example if we restart tomcat then our proxy will not load because it class already is not present in the classloader
I just put together a simple test that shows how to serialize a bean that holds a DefaultListableBeanFactory, see commit 9baf56ca (or branch spring-DefaultListableBeanFactory-ser).
I was hoping that beanfactory solves problem with aop proxy object but one of callbacks proxy has reference on factory. I did not correctly read stack of calls when an spring aop proxy object is serialized. Now is too late, tomorrow i'll describe problem with scope session in more detail with a fresh mind. thanks for test.
https://github.com/PaulWeb/simplemvc run tomcat with you session manager deploy app set name stop tomcat run it and look at catalina log ''' SEVERE: Unable to deserialize into session java.lang.ClassNotFoundException: org.wp.issue18.User$$EnhancerByCGLIB$$c148b9f6 at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1714) at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:264) at org.apache.catalina.util.CustomObjectInputStream.resolveClass(CustomObjectInputStream.java:76) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1593) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1514) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1750) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369) at org.apache.catalina.session.StandardSession.readObject(StandardSession.java:1595) at org.apache.catalina.session.StandardSession.readObjectData(StandardSession.java:1060) at com.radiadesign.catalina.session.JavaSpringSerializer.deserializeInto(JavaSpringSerializer.java:54) at com.radiadesign.catalina.session.RedisSessionManager.loadSessionFromRedis(RedisSessionManager.java:403) at com.radiadesign.catalina.session.RedisSessionManager.findSession(RedisSessionManager.java:325) at org.apache.catalina.connector.Request.isRequestedSessionIdValid(Request.java:2391) at org.apache.catalina.connector.CoyoteAdapter.parseSessionCookiesId(CoyoteAdapter.java:955) at org.apache.catalina.connector.CoyoteAdapter.postParseRequest(CoyoteAdapter.java:689) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:403) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1008) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603) at java.lang.Thread.run(Thread.java:722) ''' problem was in using @SessionAttributes({"user"}) and session scope together, but cglib proxy object cannot be serialized and deserialized in different class loaders. Of course there is proxy serialization pattern but how to create proxy after deserializing