parent script and interfaces

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

parent script and interfaces

travikk
Hey,

So I seem to be running into weird situation.
In my model I have parent-child relationship. In my child I want to to get hold of my parent, which implements interface ZZZ.

Casting my parent to ZZZ doesn't seem to work (I worked out I have to actually cast parent.script instead of parent which is not mentioned in documentation...) and I get following exception:

Cannot cast object 'MY_PARENT_CLASS@2ee1934a' with class 'MY_PARENT_CLASS' to class 'ZZZ'

Any idea why that is?
Reply | Threaded
Open this post in threaded view
|

Re: parent script and interfaces

frenchyan
Administrator
I am not sure if this will work, but you can try this:

ZZZ zzz = parent as ZZZ

this is a groovy syntax which will essentially create a proxy and delegate all calls to parent.


I don't think you should be accessing parent.script directly but access it through parent.

Yan
Reply | Threaded
Open this post in threaded view
|

Re: parent script and interfaces

travikk
So, I tried doing that and it throws exception that ZZZ class could not be found:
[+] org.linkedin.glu.agent.api.ScriptExecutionCauseException: [java.lang.NoClassDefFoundError]: ZZZ

However, if I do:

def ZZZ somedep = new MyParentImplementingZZZ()

it works. This is getting really strange...
Reply | Threaded
Open this post in threaded view
|

Re: parent script and interfaces

frenchyan
Administrator
Is ZZZ in the classpath of the child script?

Each script has its own classloader. So if it is not in the classpath that could explain your problems. http://pongasoft.github.io/glu/docs/latest/html/glu-script.html#packaging-a-glu-script

Yan
Reply | Threaded
Open this post in threaded view
|

Re: parent script and interfaces

travikk
It is, otherwise this wouldn't work either:

def ZZZ somedep = new MyParentImplementingZZZ()
Reply | Threaded
Open this post in threaded view
|

Re: parent script and interfaces

frenchyan
Administrator
After doing some deeper investigation, I think I know what the problem is. When you write something like:

    GluScriptInterface p = (GluScriptInterface) parent.script
// Cannot cast object 'org.acme.parent.GluScriptParent@6a790041' with class 'org.acme.parent.GluScriptParent' to class 'org.acme.common.GluScriptInterface'

it does not work because each script is loaded in its own independent class loader. So the GluScriptInterface loaded by the child is unfortunately a different interface than the GluScriptInterface loaded by the parent (this is how java works... 2 classes are only the same class if they are loaded by the same class loader). The error message is obviously confusing because it seems that they are the same class but because of different class loaders they are not.

The syntax 

    GluScriptInterface p = parent.script as GluScriptInterface


will not work either because (this was my misunderstanding) it does not create a dynamic proxy unless it is a map or a closure. In this case it is equivalent to the first call and you end up with the same result.

If you want a partial solution, you actually need a dynamic proxy something like this:

// need to import java.lang.reflect.*

    def ih = { proxy, method, args -> 
      if(args)
        parent.script."${method.name}"(args) 
      else
        parent.script."${method.name}"() 
        
      }
    
    GluScriptInterface p = 
      Proxy.newProxyInstance(GluScriptInterface.class.getClassLoader(),
                             [GluScriptInterface.class] as Class[],
                             ih as InvocationHandler)


What this does is actually create a dynamic proxy using the GluScriptInterface from the child  and delegate all methods to parent.script using groovy dynamic method invocation. Note that this will NOT work for overloaded method (same name for methods with different parameters) unless you add the proper handling in ih closure.

I thought there was an easier way to do this in groovy. Maybe there is that I am not aware of.

In the process I have discovered a bug. You should be able to use parent instead of parent.script but due to a bug it is not working. I created a bug to fix it: https://github.com/pongasoft/glu/issues/279

I wished there was a (cleaner) solution for your problem, but it has nothing to do with glu: it is the fact that each entry is loaded in its own class loader.

Yan

Reply | Threaded
Open this post in threaded view
|

Re: parent script and interfaces

frenchyan
Administrator
In reply to this post by travikk
Actually unlike what I said, the proxy will work for overloaded method if you use this instead:

// need to import java.lang.reflect.*

    def ih = { proxy, method, args -> 
      if(args)
        parent.script."${method.name}"(*args) 
      else
        parent.script."${method.name}"() 
        
      }
    
    GluScriptInterface p = 
      Proxy.newProxyInstance(GluScriptInterface.class.getClassLoader(),
                             [GluScriptInterface.class] as Class[],
                             ih as InvocationHandler)


​args need to be "spread" (*args syntax). groovy should pick the proper method based on arguments at runtime.

Yan
Reply | Threaded
Open this post in threaded view
|

Re: parent script and interfaces

travikk
Thanks for your answer, this explains the problem.
I've actually run into the bug that you created here: https://github.com/pongasoft/glu/issues/279 so it's good to know, that we keep track of things.