frjaeger220 / google-guice

Automatically exported from code.google.com/p/google-guice
Apache License 2.0
0 stars 0 forks source link

Make proxies serializable #12

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
So you can store proxies on the HTTP session, etc.

Original issue reported on code.google.com by crazybob...@gmail.com on 14 Feb 2007 at 7:13

GoogleCodeExporter commented 9 years ago

Original comment by crazybob...@gmail.com on 14 Feb 2007 at 7:13

GoogleCodeExporter commented 9 years ago
I think I have a possible strategy for the fix, but its tricky.

Suppose the intercepted class looks like this:
  class Foo implements Serializable {
    private final String a;
    private final  long createdTime;
    private boolean haveIBeenSerialized;
    @Inject public Foo(String a) {
      this.a = a;
      this.b = System.currentTimeMillis();
    }
    public void bar() {
      System.out.println(a);
    }
  }

Then the Guice generated AOP class looks approximately like this. This is my 
speculation, I'm not actually 
familiar with the implementation:
  class Foo$EnhancedByGuice extends Foo {
    Interceptors interceptors;
    Foo$EnhancedByGuice(String a) {
      super(a);
    }
    @Override public void bar() {
      interceptors.intercept(...);
    }
  }

The problem is that it's very unlikely that the other end is going to have a 
class like Foo$EnhancedByGuice. 
Basically, serializing generated classes is about as unreliable as it gets. So 
what we want to do kind of might 
look approximately like this:
  class SerializedInterceptable<T> implements Serializable {
    T base;
    Interceptors interceptors;
    public SerializedInterceptable(T base, Interceptors interceptors) { ... }
    public void readResolve() {
      Foo$EnhancedByGuice enhanced = asSubclass(Foo$EnhancedByGuice.class, base);
      enhanced.interceptors = interceptors;
      return enhanced;
    }
  }
  class Foo$EnhancedByGuice extends Foo {
    ...
    public Object writeReplace() {
      Foo baseFoo = removeSubclass(this);
      return new SerializedInterceptable(baseFoo, interceptors);
    }
  }

Of course, the removeSuperclass() and asSubclass() methods need to do something 
quite magical. They need 
to reflectively turn a Foo$EnhancedByGuice into the corresponding Foo and vice 
versa. I suspect this will need 
Sun's Unsafe class, since we must not invoke the Foo constructor a second time 
(it could have side effects).

One requirement is that we must use the user's provided Foo.writeReplace() and 
Foo.readResolve() methods. 
This opens the door to the user type changing from say Foo to Bar during 
serialization. Another trick is that 
we don't have any idea of what Foo will look like upon deserialization. In 
particular, it may have a completely 
different set of methods. I suspect the best heuristic to solve these problems 
will be to re-run the matchers 
post-serialization. So any newly-added methods will be intercepted if they 
match the initial criteria.

One way to get around Unsafe would be to get Java's serialization to do the 
unsafe stuff for us. Perhaps a 
clever implementation of asSubclass() could create a byte[] that looked like 
just the Foo-parts of the class. 
We'd get reflection to do the unsafety for us. Whichever way we go we're going 
to need to try to conserve 
complexity!

Original comment by limpbizkit on 30 May 2008 at 4:22

GoogleCodeExporter commented 9 years ago
I talked to Bob about this. We're not going to implement this since it has 
dangerous consequences, especially if it 
were to work "transparently".

* - serializing interceptors is dangerous (they require heavyweight 
configuration, like transaction managers, 
which aren't serializable in general)
* - not serializing interceptors is dangerous (transaction management shouldn't 
disappear upon serialization)

Original comment by limpbizkit on 3 Jun 2008 at 9:39

GoogleCodeExporter commented 9 years ago
Turns out it isn't too difficult to implement removeSuperclass() and 
asSubclass() using reflection and 
serialization. You'll need this tip:
  http://sixlegs.com/blog/java/skipping-constructors.html

Original comment by limpbizkit on 4 Jun 2008 at 12:29