So, I am
constructing some server logic in my webapp where it would be very
useful to know when the WebAppContext is being shut down. Basically, I
need to cleanly release resources...
I
have been attempting to do this by writing a LifeCycle object,
implementing org.eclipse.jetty.util.component.LifeCycle, and then in my
jetty-web.xml deploying this to the WebAppContext using the addManaged
method;
<Call name="addManaged">
<Arg><New id="mg" class="test.LifecycleObject"/></Arg>
</Call>
Based
on what I have seen so far, this feels like a decent approach for how
to do this. I could also do this, using a LifeCycle.Listener and the
addLifeCycleListener method, but I see the same problem described below
using both approaches.
The problem I am having is that the call to addManaged fails,
java.lang.IllegalStateException:
No Method: <Call name="addManaged"><Arg><New id="mg"
class="test.LifecycleObject"/></Arg></Call> on class
org.eclipse.jetty.webapp.WebAppContext
at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.call(XmlConfiguration.java:738)
at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.configure(XmlConfiguration.java:417)
at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.configure(XmlConfiguration.java:298)
...
Caused by:
java.lang.NoSuchMethodException: addManaged
at org.eclipse.jetty.util.TypeUtil.call(TypeUtil.java:537)
at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.call(XmlConfiguration.java:730)
at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.configure(XmlConfiguration.java:417)
...
My object that implements the LifeCycle interface is attached.
Ultimately,
the problem appears to be classloader incompatabilities... When I run
the whole process in the debugger and put a breakpoint in the call
method of XmlConfiguration and then interrogate the objects involved
just before the call is passed in to TypeUtil.call() on the line;
Object n= TypeUtil.call(oClass,method,obj,arg);
I am seeing the following;
oClass.getName() -> org.eclipse.jetty.webapp.WebAppContext
arg[0].getClass() -> class test.LifecycleObject
arg[0].getClass().getInterfaces() -> [interface org.eclipse.jetty.util.component.LifeCycle]
so I have ther right objects and my LifecycleObject class does implement the right interface. But,
LifeCycle.class.isInstance(arg[0]) -> false
This
clearly explains the call failure... When I dig closer, I see that the
LifeCycle.class object is loaded with server class loader and my
LifecycleObject is loaded with the web app context class loader.
LifeCycle.class.getClassLoader() -> startJarLoader@217e9fe8
arg[0].getClass().getClassLoader() -> WebAppClassLoader=84066694@502c186
I
get why this does not work, I am just trying to figure out how I should
be doing this... What I am trying to do seems like a pretty common use
case, so I figure someone else has solved this already. Besides I am
getting tired of banging my head on the wall!
I
have tried this with my LifecycleObject class in my war file for my
webapp as well as having it in a jar file loaded using the --lib option
when I start the server. I get the same results both ways, so the class
loaded seems to be driven purely based on the fact the instance is
created using <New> in the jetty-web.xml.
Note that I am seeing all of this with Jetty 9.2.6.
I appreciate any suggestions anyone can give!
Thanks,
Scott