Jason,

thanks for a quick response!

On 29. 3. 2016, at 19:09, "Winnebeck, Jason" <jason.winneb...@windstream.com> 
wrote:

> You still have to follow the rules of Java bytecode

right, that's why I wrote it's a Java fault (inherited by Groovy), not a Groovy 
fault.

> that is your class DumbProxy is not related by class hierarchy or interface 
> to AnyClassOfMine, so you can't cast to it.

That is precisely the problem: without casting to it, it can't be used.

> You have to use def/Object type and Groovy's duck typing, or you need to make 
> DumbProxy extend AnyClassOfMine

Far as I understand the Groovy dispatch, this would force me not to go through 
a relatively clean missingXXX APIs; instead, I would probably have to exploit 
metaclass functionality directly, or something like that.

Well OK, that I can live with (unless it gets really slow, which it probably 
would not).

But what if the target class is final? Would never happen with AnyClassOfMine 
of course, but would happen with 3rd party classes whose instances I might need 
to proxy.

> or make an interface for them both to implement and use that.

Same problem there: whilst I can turn AnyClassOfMine to implement anything, I 
can't turn 3rd party classes to do that; and proxying 3rd party classes is 
essential (after all, with my own classes I can easily use other tricks than 
proxying to achieve the same result).

> Other options to consider include:
> http://docs.groovy-lang.org/latest/html/gapi/groovy/util/Proxy.html

Correct me please if I am overlooking anything, but it seems to me this is just 
slightly better implementation of DumbProxy. To redirect methods it uses 
invokeMethod instead of methodMissing (perhaps it is faster? Dunno. One 
advantage might be it would technically allow to forward even methods 
implemented in Proxy itself, but that is prevented by the implementation); but 
it _does_ share the very same typecasting problem we have just established with 
my DumbProxy.

Or does it not? If not, how comes?

> http://docs.groovy-lang.org/latest/html/gapi/groovy/util/ProxyGenerator.html

Well this seems really to create an instance of a dynamically made subclass 
(whoa! But I can see there's nothing else we can do with the bloody Java 
inherited behaviour), but I must be missing something. That instance does not 
work as a proxy, but seems to be half-proxy half-real instance of the target 
class.

Far as I has been able to test, it seems that

pg.instantiateDelegateWithBaseClass([:],[],delegate,class)

(a) creates a new instance of (a private subclas of) given “class” (OK)
(b) which instance gets all the methods (including property getters and 
setters) “class” contains, directly, without notifying the “delegate” anyhow 
(COMPLETELY WRONG)
(c) and only properties a methods the “class” does _not_ contain are directed 
to the delegate (OK)

The functionality needed for proxying differs from that:

(a) the proxied object (a server) exists before a proxy is created;
(b) the delegate (far as I understand what is its purpose?!?) should get _all_ 
the methods called “of the proxy”...
(c) ... so that it can (pre- or post-process them if need be and) forward them 
to the real proxied object.

Here's my testing code; perhaps you can point out what's wrong? The 
documentation is seriously lacking :)

===
class AnyClassOfMine {
  def xxname // renamed from 'name' to be triple sure not to clash with proxy 
name or something
  def foo(bar) { println "foo of $this called with $bar"; 
"${bar.toUpperCase()}, I am $xxname" }
}
class Delegate {
  def server
  def propertyMissing(String name) {
    println "- delegate asked for a property '$name' for server $server"
    if (name!='xxname') return 'nonexistent'
    server."$name"
  }
  def propertyMissing(String name,value) {
    println "- delegate asked to set a property '$name' to $value for server 
$server"
    if (name!='xxname') return 'nonexistent'
    server."$name"
  }
  def methodMissing(String name, args) {
    println "- delegate asked for a method '$name'$args for server $server"
    if (name!='foo') return 'nonexistent'
    server."$name"(*args)
  }
}

def proxied=new AnyClassOfMine(xxname:"Proxied"),delegate=new 
Delegate(server:proxied)
assert delegate.xxname=='Proxied'

def pg=groovy.util.ProxyGenerator.INSTANCE // is this the proper way to do it?
def 
proxy=pg.instantiateDelegateWithBaseClass([:],[],delegate,delegate.server.class)
assert proxy instanceof AnyClassOfMine

println "=== Delegate gets unknown method allright:"
println "-> ${proxy.unknown('hello')}"
println "=== Known method implemented by $proxy, instead of being sent through 
delegate to $proxied!"
println "-> ${proxy.foo('hello')}"
println "=== It should look like this:"
println "-> ${delegate.foo('hello')}"

println "=== Exactly same problem with properties: unknown one correctly 
forwarded..."
println "-> $proxy.unknownProperty"
println "=== Known property though processed directly by proxy, not forwarded 
through delegate to $proxied!"
println "-> $proxy.xxname (differs from $proxied.xxname; or read through 
delegate, $delegate.xxname)"
===

The results are

===
- delegate asked for a property 'xxname' for server AnyClassOfMine@263d0564
=== Delegate gets unknown method allright:
- delegate asked for a method 'unknown'[hello] for server 
AnyClassOfMine@263d0564
-> nonexistent
=== Known method implemented by Delegate1_groovyProxy@837a151, instead of being 
sent through delegate to AnyClassOfMine@263d0564!
foo of Delegate1_groovyProxy@837a151 called with hello
-> HELLO, I am null
=== It should look like this:
- delegate asked for a method 'foo'[hello] for server AnyClassOfMine@263d0564
foo of AnyClassOfMine@263d0564 called with hello
-> HELLO, I am Proxied
=== Exactly same problem with properties: unknown one correctly forwarded...
- delegate asked for a property 'unknownProperty' for server 
AnyClassOfMine@263d0564
-> nonexistent
=== Known property though processed directly by proxy, not forwarded through 
delegate to AnyClassOfMine@263d0564!
- delegate asked for a property 'xxname' for server AnyClassOfMine@263d0564
-> null (differs from Proxied; or read through delegate, Proxied)
===

Instead, proxy.foo should redirect to the delegate (and through it to server). 
Similarly, proxy.xxname should get/set the server xxname (through the delegate, 
again).

> http://docs.groovy-lang.org/latest/html/gapi/groovy/lang/Delegate.html

Again, correct me please if I am overlooking something, but this does not seem 
to have _anything_ in common with the thing we are solving, i.e., proxying.

Far as I understand, this exploits ASTTs to generate automatically things like

===
class Foo {
  Bar server
  def someBarMethod() { server.someBarMethod() }
  void anotherBarMethod(foo,bar,baz) { server.anotherBarMethod(foo,bar,baz) }
  ....
  // and so forth
}
===

That's rather ugly solution compared with dynamic redirection (though it might 
be much faster in the Java world, I did not benchmark it, but I can guess this 
probably will be the point of it; along with the problem that dynamically 
redirected methods would not work if called from pure Java, which might be 
enormously important for someone, luckily, completely irrelevant to me :)).

Thanks a lot,
OC


> -----Original Message-----
> From: OC [mailto:o...@ocs.cz] 
> Sent: Tuesday, March 29, 2016 12:16 PM
> To: users@groovy.apache.org
> Subject: Proxying how to?!? (was: changing dynamically the name of classes in 
> a source code)
> 
> Incidentally...
> 
> On 28. 3. 2016, at 18:10, OC <o...@ocs.cz> wrote:
>> completely absurd and very anti-object-oriented) "Cannot cast object" 
>> exception.
> 
> ... this reminded me of a problem I so far haven't been able to find a proper 
> solution for: how the heck do you proxy in Groovy?
> 
> In ObjC, I can write
> 
> ===
> @interface AnyClassOfMine:Beanlike
> @property NSString *name;
> @end
> @implementation AnyClassOfMine @end
> 
> @interface DumbProxy:Beanlike
> @property id server;
> @end
> @implementation DumbProxy
> -forwardingTargetForSelector:(SEL)sel { return self.server; }
> @end
> 
> id objects=@[[AnyClassOfMine new:@"name":@"Direct"],[DumbProxy 
> new:@"server":[AnyClassOfMine new:@"name":@"Proxied"]]];
> for (AnyClassOfMine *o in objects) NSLog(@"got %@",o.name);
> ===
> 
> and it works precisely as assumed, writing out
> 
> ===
> 2016-03-29 17:57:37.501 a.out[5387:707] got Direct
> 2016-03-29 17:57:37.503 a.out[5387:707] got Proxied
> ===
> 
> (Note if interested: the Beanlike superclass is irrelevant, it just simulates 
> the new Foo(bar:bax) functionality of Groovy, which ObjC does not have, by 
> implementing the new:(property-name):(value) method.)
> 
> Groovy -- unlike pure Java -- is smart enough to allow me to _implement_ such 
> a proxy, but for sweet world, I cannot find a way to _use_ it?
> 
> ===
> class AnyClassOfMine {
>  def name
> }
> 
> class DumbProxy {
>  def server
>  def propertyMissing(String name) {
>    server."$name"
>  }
> }
> 
> def objects=[new AnyClassOfMine(name:"Direct"),new DumbProxy(server:new 
> AnyClassOfMine(name:"Proxied"))]
> for (AnyClassOfMine o in objects) println "got $o.name"
> ===
> 
> Alas, instead of working as expected, this fails with the aforementioned 
> nonsensical exception:
> 
> ===
> got Direct
> Caught: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot 
> cast object 'DumbProxy@73f43791' with class 'DumbProxy' to class 
> 'AnyClassOfMine'
> ...
> ===
> 
> How do you write and use a proxy in Groovy, so that it works properly?
> 
> (Note: "for (o in objects) ..." would work is this case, but would bring 
> other problems, e.g., if there was a method "foo(AnyClassOfMine obj)" called 
> as "for (o in objects) foo(o)", it would cause "No signature of method is 
> applicable for argument types: (DumbProxy)".)
> 
> Thanks a lot,
> OC
> 
> ----------------------------------------------------------------------
> This email message and any attachments are for the sole use of the intended 
> recipient(s). Any unauthorized review, use, disclosure or distribution is 
> prohibited. If you are not the intended recipient, please contact the sender 
> by reply email and destroy all copies of the original message and any 
> attachments.

Reply via email to