views:

246

answers:

2

Greetings, coders,

Background Info and Code

I am trying to create a daemon-type program (e.g., it runs constantly, polling for things to do) that is managed by a GWT application (servlets in a WAR) which is in turn served by an embedded Jetty server (using a WebAppContext). I'm having problems making the GWT application aware of the daemon object.

For testing things, I currently have two projects: The daemon and embedded Jetty server in one (EmbJetTest), and the GWT application in another (DefaultApp). This is the current state of the code:

First, EmbJetTest creates an embedded Jetty server like so, using a ServletContextListener to inject the daemon object into the web application context:

    EmbJetTest.server = new Server(8080);

    // Create and start the daemon
    Daemon daemon = new Daemon();
    Thread thread = new Thread(daemon);
    thread.start();

    // war handler
    WebAppContext waContext = new WebAppContext();
    waContext.setContextPath("/webapp");
    waContext.setWar("./apps/DefaultApp.war");
    waContext.addEventListener(new DaemonLoader(daemon));

    // Add it to the server
    EmbJetTest.server.setHandler(waContext);

    EmbJetTest.server.setThreadPool(new QueuedThreadPool(10));

    // Start the server; join() blocks until we shut down
    EmbJetTest.server.start();
    EmbJetTest.server.join();

    // Stop the daemon thread
    daemon.stopLoop();

Daemon is a very simple object with a couple properties, at the moment. DaemonLoader is the following ServletContextListener implementation:

private Daemon daemon;

public DaemonLoader(Daemon daemon)
{
    this.daemon = daemon;
}

@Override
public void contextDestroyed(ServletContextEvent arg0) {

}

@Override
public void contextInitialized(ServletContextEvent arg0) {
    arg0.getServletContext().setAttribute("daemon", this.daemon);
}

Then, in one of my servlets in the GWT application, I have the following code:

Daemon daemon = (Daemon) this.getServletContext().getAttribute("daemon");

However, when I visit localhost:8080/webapp/* and invoke the servlet, this code throws a ClassCastException, even though the classes are of the same type. This StackOverflow answer indicates that this is because the two classes are loaded with different classloaders.

Question

My question is twofold.

  1. Am I even on the right track here? Am I going about this completely the wrong way? Something tells me I am, but I can't think of another way to make the daemon available to both applications. Is there a better way to communicate with the daemon from the GWT application? Should the GWT app own the daemon and somehow start the daemon itself? The daemon needs to run even if no one visits the one of the GWT app's servlets--how could I do this?
  2. If I am on the right track, how can I get around the classloader issue?

Thanks in advance.

+2  A: 

Why not just run the functionality of the daemon in a separate thread within the GWT web application ? That way you'll avoid all the classloading grief (as you've found out, apps in different .war files run within their own classloaders).

Just create a servlet for your daemon alongside your existing servlets (in the same .war), and start/stop the thread on servlet initialisation and destruction.

Brian Agnew
I'm not 100% sure how to make the daemon class load within the GWT app. As mentioned, it needs to load and run even if no servlets are visited. Also, I'm not sure if this complicates matters, but the class that needs to be loaded and ran will be specified via a configuration file.
BinaryMuse
You can specify a servlet to start on web application initialisation via <load-on-startup/> so you don't need to visit your app. Configuration would occur via web.xml and appropriate parameters within that. Specify a class name as an initialisation parameter and use (say) Class.forName(name).newInstance() in the simplest case.
Brian Agnew
I think this is ideal, the more I think about it. The embedded Jetty instance and related code doesn't need to know about the daemon at all--this would tie the daemon and all management code into the same project. The startup servlet could read the config file and load the appropriate daemon class. Would probably also make it easier to test, as well, since dev. mode will load everything anyway.
BinaryMuse
A: 

Well, in case anyone is interested, I actually managed to solve the classloader issue--originally, I placed the compiled Daemon.class in the GWT app's WEB-INF/classes folder. I deleted this file and now it appears that the GWT app uses the Daemon class from the EmbJetTest project.

That being said, I think Brian's answer is much more noteworthy. :)

BinaryMuse