How to share ClassLoader bewteen different Plugins

Hi,

I am trying develop Elatsicsearch plugins and currently facing some issue as how to share single request model and allow one plugin to accept the request in the TransportAction from other plugin.

Error I message I am getting is

Internal error: class foo.bar.MyRequest cannot be cast to class foo.bar.MyRequest (foo.bar.MyRequest is in unnamed module of loader java.net.FactoryURLClassLoader @19a64eae; foo.bar.MyRequest is in unnamed module of loader java.net.FactoryURLClassLoader @697a34af). See the Elasticsearch.log for details
java.lang.ClassCastException: class foo.bar.MyRequest cannot be cast to class foo.bar.MyRequest (foo.bar.MyRequest is in unnamed module of loader java.net.FactoryURLClassLoader @19a64eae; foo.bar.MyRequest is in unnamed module of loader java.net.FactoryURLClassLoader @697a34af)

To be clear I have shared dependency on package that has MyRequest class in both Plugins.

Thanks in advance!

Alternatively, how do I write a High Level Rest Client from one plugin to be used by another plugin?

I think this may be a bug, due to optimizations to avoid serialization for local requests. It may be difficult to fix, though. Can you show the full stacktrace from the elasticsearch log?

Thank you for the response. This is the stack trace from the elasticsearch log.
For now I am trying to do a simple client.execute(MyAction.INSTANCE, MyRequest()).actionGet() from SamplePlugin to another plugin. At which, it fails to cast into same type of class.

    Internal error: class foo.bar.MyRequest cannot be cast to class foo.bar.MyRequest (foo.bar.MyRequest is in unnamed module of loader java.net.FactoryURLClassLoader @55cff952; foo.bar.MyRequest is in unnamed module of loader java.net.FactoryURLClassLoader @66ec9390). See the Elasticsearch.log for details
    java.lang.ClassCastException: class foo.bar.MyRequest cannot be cast to class foo.bar.MyRequest (foo.bar.MyRequest is in unnamed module of loader java.net.FactoryURLClassLoader @55cff952; foo.bar.MyRequest is in unnamed module of loader java.net.FactoryURLClassLoader @66ec9390)
            at foo.bar.MyTransportAction.doExecute(MyTransportAction.java:30) ~[?:?]
            at org.elasticsearch.action.support.TransportAction.doExecute(TransportAction.java:143) ~[elasticsearch-6.6.2.jar:6.6.2]
            at org.elasticsearch.action.support.TransportAction$RequestFilterChain.proceed(TransportAction.java:167) ~[elasticsearch-6.6.2.jar:6.6.2]
            at org.elasticsearch.action.support.TransportAction.execute(TransportAction.java:139) ~[elasticsearch-6.6.2.jar:6.6.2]
            at org.elasticsearch.action.support.TransportAction.execute(TransportAction.java:81) ~[elasticsearch-6.6.2.jar:6.6.2]
            at org.elasticsearch.client.node.NodeClient.executeLocally(NodeClient.java:87) ~[elasticsearch-6.6.2.jar:6.6.2]
            at org.elasticsearch.client.node.NodeClient.doExecute(NodeClient.java:76) ~[elasticsearch-6.6.2.jar:6.6.2]
            at org.elasticsearch.client.support.AbstractClient.execute(AbstractClient.java:403) ~[elasticsearch-6.6.2.jar:6.6.2]
            at org.elasticsearch.client.support.AbstractClient.execute(AbstractClient.java:391) ~[elasticsearch-6.6.2.jar:6.6.2]
            at foo.bar.SampleClass$runTask$1.run(SampleClass.java:184) [SamplePlugin.jar:Sample]
            at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]
            at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
            at org.elasticsearch.common.util.concurrent.ThreadContext$ContextPreservingRunnable.run(ThreadContext.java:660) [elasticsearch-6.6.2.jar:6.6.2]
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
            at java.lang.Thread.run(Thread.java:834) [?:?]

This is what I thought: the node client is executing the action directly, since both actions are executing on the same node. However, is your plugin trying to do the cast which is causing the problem, or is this due to generics? If you could take in the superclass ActionRequest, you could do your own serialization/deserialization, but I'm not sure the generics will allow that (I haven't tried experimenting, just hypothesizing).

1 Like

Thank you for the response. This idea worked wonderfully.
To share the things I did,

  1. I created a shared package that will be consumed by both plugin.
  2. Create implementation of ActionRequest and ActionResponse that is right for you. Make sure to override readFrom and writeTo. This is key to enable recreating the Request and Response when communicating between plugin.
  3. In the shared plugin, created a class that implements from org.elasticsearch.action.Action which accepts specific request type and return generic ActionResponse.
  4. In Plugin that is receiving the call, create class that implements HandledTransportAction with generic ActionRequest and MyResponse which is the implementation of ActionResponse
  5. In MyHandledTransportAction will use readFrom in ActionRequest to create correct RequestType. Once MyRequest is recreated then all the information for the request is there and now do what is needed for the Request and return MyResponse.
  6. From MyPlugin send request with specific request format but it will get back ActionResponse. Once the ActionResponse is returned from the client, use the readFrom method to get the specific type of the response back.

Thank you for the help and feel free to close this thread.

Make sure to override readFrom and writeTo

Just a heads up: Streamable will eventually go away, so just be aware you will eventually need to change your actions to use Writeable/Readable. See https://github.com/elastic/elasticsearch/issues/34389

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.