blackdrag commented on PR #3086:
URL: https://github.com/apache/groovy/pull/3086#issuecomment-6750516676

   Hibernate proxy rules as I understand what has to be there to support lazy 
init:
   1.  method must be not private, final or static.
   2.  fields are not supported using the subclass proxy.
   3. supporting package private and protected methods requires same package 
and class loader (and module), which requires ByteBuddy for example to go to 
quite some lengths to support that properly, including exposing a LookupObject 
that has full access to the entity.
   4. interface default methods may or may not be in the proxy, depending on 
settings. Technically they do not exist on the entity
   5. constructor is not on the proxy
   6. proxy will contain additional methods to support the interfaces it 
implements, which excludes them from the entity as possible method names
   7. equals (and hashcode) may require special handling.
   8. inherited parent methods for entity may call methods from entity
   
   In case of inheritance in Java we have these rules for the proxy
   1.  cannot be overwritten, private methods are not really inherited, but 
also not visible outside. Proxy does not contain them
   2. fields are inherited, if not private, but they break lazy init in the 
hibernate proxy. Proxy does not contain them.
   3. package private and protected methods are visible in the proxy, if in the 
same package and class loader.  Proxy must contain them.
   4. Proxy may contain them
   5. no entity constructor on the proxy
   6.  obviously the proxy will contain them, but that restricts available 
method names in the entity
   7.  assume I have a check `other.getClass() == MyEntity.class`, then a proxy 
in `other` would break. 
   8. need to create proxy methods for them. the field restriction also applies 
to parent classes
    
   What happens if we use such a proxy in Groovy? Because the proxy is a Groovy 
class it will proxy getMetaClass and deliver the entity meta class instead of 
the proxy metaclass.
   What would happen if metaClass(proxy) is the meta class for the proxy class? 
In that case we would see something similar to what Java sees, so all should be 
fine within the Java rules.  
   If entity has no normal meta class, or for example EMC with a method added? 
Then the meta class of proxy may not see that. 
   
   If the proxy uses the meta class of the entity it does proxy, then you would 
see methods added through runtime meta programming to entity also on proxy. It 
would not see additional methods on proxy, it would claim to have private 
methods and fields on proxy, that are not on proxy. It would report the 
constructors of entity, not the ones from proxy. A static method of the same 
name on proxy would be called instead of entity if used like an instance 
method. 
   
   Which reminds me of a chapter I should open here as well... that is 
closures. A closure in the entity would operate on the proxy instance. If that 
closure tries to call a private method/field on entity it my fail because it 
actually works on proxy but thinks it operates on entity.
   
   ```Groovy
   class X {
     private foo() {"foo X"}
     public bar() {def cl = {foo()}; cl()}
   }
   assert new X().bar() == "foo X"
   
   class Y extends X {
     private foo() {"foo Y"}
   }
   assert new Y().bar() == "foo Y"
   
   def y = new Y()
   def x = new X()
   y.metaClass = x.metaClass
   assert y.bar() == "foo X" // <-- changed because of wrong meta class
   
   
   class Z extends X{}
   def z = new Z()
   assert z.bar() == "foo X"  // may fail in Groovy 4
   z.metaClass = x.metaClass
   assert z.bar() == "foo X" // <-- correct even though wrong meta class, 
because Z has no foo()
   ```
   
   And of course traits:
   ```
   trait MyTrait { 
     private secret() {"secret"}
     String state = "state from trait "
     String bar() { foo() + this.class.name + " " + secret() + " " + 
this.metaClass.theClass.name} 
     def foo(){};
   }
   
   class X implements MyTrait{
     def foo() {state}
   }
   assert new X().bar() == "state from trait X secret X"
   
   class Y extends X {
     def foo() {"foo Y "}
   }
   assert new Y().bar() == "foo Y Y secret Y"
   
   def y = new Y()
   def x = new X()
   y.metaClass = x.metaClass
   assert y.bar() == "foo Y Y secret X"
   
   class Z extends X{}
   def z = new Z()
   assert z.bar() == "state from trait Z secret Z"
   z.metaClass = x.metaClass
   assert z.bar() == "state from trait Z secret X"
   ```
   which does not proof something breaks right away, but of course the instance 
metaclass leaks into the trait.
   
   Of course with @POJO the situation would look very different again.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to