Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[jetty-users] Loading managed object in WebAppContext

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

package test;

import org.apache.log4j.Logger;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.webapp.WebAppContext;

public class LifecycleObject implements LifeCycle {
	public enum RunState {
		STOPPED, STARTING, STARTED, STOPPING, FAILED;
	}
	
    private static final Logger LOG = Logger.getLogger(LifecycleObject.class);    
    private RunState m_state = RunState.STOPPED;
    
    public LifecycleObject() {
        LOG.debug("construction");
    }
    
    public static void registerStatic(final WebAppContext wac,
    		             		      final LifecycleObject obj) {
    	wac.addManaged(obj);
    }

	@Override
	public void start() throws Exception {
        LOG.debug("start");
        m_state = RunState.STARTING;
        m_state = RunState.STARTED;
	}

	@Override
	public void stop() throws Exception {
        LOG.debug("stop");
        m_state = RunState.STOPPING;
        m_state = RunState.STOPPED;
	}

	@Override
	public boolean isRunning() {
        LOG.debug("isRunning");
        if (m_state == RunState.STARTED) {
        	return true;
        } else {
        	return false;
        }
	}

	@Override
	public boolean isStarted() {
        LOG.debug("isStarted");
        if (m_state == RunState.STARTED) {
        	return true;
        } else {
        	return false;
        }
	}

	@Override
	public boolean isStarting() {
        LOG.debug("isStarting");
        if (m_state == RunState.STARTING) {
        	return true;
        } else {
        	return false;
        }
	}

	@Override
	public boolean isStopping() {
        LOG.debug("isStopping");
        if (m_state == RunState.STOPPING) {
        	return true;
        } else {
        	return false;
        }
	}

	@Override
	public boolean isStopped() {
        LOG.debug("isStopped");
        if (m_state == RunState.STOPPED) {
        	return true;
        } else {
        	return false;
        }
	}

	@Override
	public boolean isFailed() {
        LOG.debug("isFailed");
        if (m_state == RunState.FAILED) {
        	return true;
        } else {
        	return false;
        }
	}

	@Override
	public void addLifeCycleListener(Listener listener) {
        LOG.debug("addLifeCycleListener");
	}

	@Override
	public void removeLifeCycleListener(Listener listener) {
        LOG.debug("removeLifeCycleListener");
	}

}

Back to the top