Using guice with servlets

1,834 views
Skip to first unread message

Robin Sheat

unread,
Mar 13, 2007, 3:05:08 AM3/13/07
to google-guice
Hi, I'm fairly new to the whole dependency injection thing, and in
order to learn, decided to start using it for a web app I'm working
on, however I've encountered a problem, most likely due to my lack of
understanding.

My app is pretty much straight Java, no EJBs or anything like that,
running inside an application server. This means there are multiple
entry points: 1) the class I specify that has its
contextInitialized(ServletContextEvent) method called on startup, and
2) the classes that handle HTTP requests.

I currently have this in contextInitialized():
this.injector = Guice.createInjector(new Bindings(dataSource,
this));
where Bindings is obviously a Module that sets up instance and class
bindings.

However, none of the servlet request handling instances are created by
me (or Guice), but by the application container. Therefore, they don't
get their fields injected into, therefore I get NullPointerExceptions
coming up.

What's the recommended method of combining Guice with servlets like
this? The only way I can see is for them to request an injector from
somewhere static, and use that, but that seems to be defeating the
whole purpose, so I'm sure there's a better way.

Thanks, Robin.

Hani Suleiman

unread,
Mar 13, 2007, 11:30:20 AM3/13/07
to google...@googlegroups.com
The problem is that there's no way of hooking into servlet
construction, or even a notification mechanism that's external to the
servlet.

So, you'd have to go with a base class. Something like this:

public abstract class AbstractInjectableServlet

public void init(ServletConfig config)
{
//look up injector from config.getServletContext()
injector.injectMembers(this);
}

That way all the subclasses will get injected at construction time.

peter royal

unread,
Mar 13, 2007, 1:28:45 PM3/13/07
to google...@googlegroups.com
On Mar 13, 2007, at 8:30 AM, Hani Suleiman wrote:
> The problem is that there's no way of hooking into servlet
> construction, or even a notification mechanism that's external to the
> servlet.

If you want to bypass the servlet specification, you can override the
code in Jetty that creates new Filters, Listeners and Servlets and
replace it with something that does injection.

So, its possibly, just non-portable. Works great if you're using
Jetty anyways though.

-pete

--
(peter.royal|osi)@pobox.com - http://fotap.org/~osi

Robin Sheat

unread,
Mar 13, 2007, 7:56:11 PM3/13/07
to google-guice
On Mar 14, 4:30 am, Hani Suleiman <h...@formicary.net> wrote:
> public void init(ServletConfig config)
> {
> //look up injector from config.getServletContext()
> injector.injectMembers(this);
> }
OK, that's almost the way I was doing it in order to get it working, I
was hoping that it would just be a hack I could replace further on :)
Although, putting it into the servlet context is something I didn't
think of, and is nicer than the static reference I was using. At least
with the context, I can mock it.

Cheers, Robin.

Kevin Bourrillion

unread,
Mar 14, 2007, 12:28:58 AM3/14/07
to google...@googlegroups.com
Hey everyone,

I'm glad this was figured out... but, of course, it should not have required figuring out :-), so I'm looking at adding this support to com.google.inject.servlet.  So far I've added:

  public abstract class InjectableHttpServlet extends HttpServlet {
    @Override public void init(ServletConfig config) throws ServletException {
      super.init(config);
      ServletContext context = config.getServletContext();
      Injector injector
          = (Injector) context.getAttribute (Injector.class.getName());
      if (injector == null) {
        throw new UnavailableException("Guice Injector not found "
            + "(did you forget to foo koo joo boo?)");
      }
      injector.injectMembers(this);
    }
  }

Now I'm trying to figure out what to fill in for "foo koo joo boo".  My servlet understanding is weak since we don't tend to do much with them directly here.  I've figured, maybe something like this?

  public class GuiceServletContextListener implements ServletContextListener {
    private final Injector injector;

    @Inject public GuiceServletContextListener(Injector injector) {
      this.injector = injector;
    }
    public void contextInitialized(ServletContextEvent servletContextEvent) {
      ServletContext servletContext = servletContextEvent.getServletContext();
      servletContext.setAttribute(Injector.class.getName(), injector);
    }
    public void contextDestroyed(ServletContextEvent servletContextEvent) {}
  }

But when it comes to how exactly someone would make use of this, I'm fuzzy.  I think they could extend it with a class like this:

  public class MyThing extends GuiceServletContextListener {
    public MyThing() {
      super(createMyInjector());
    }
    private Injector createMyInjector() {
      return Guice.createInjector(moduleA, moduleB, . . . );
    }
  }

And then I think they have some way of registering this class as a servlet context listener, but I suspect that the way they do this registration may possibly not be standardized between all servlet engine implementations, because I can find no method in javax.servlet that actually takes in one of these things.  But again, I'm out of my comfort zone here, so I hope y'all can set me straight about this fancy servlet stuff.  Thanks!

K

Bob Lee

unread,
Mar 14, 2007, 12:33:43 AM3/14/07
to google...@googlegroups.com
On 3/13/07, Kevin Bourrillion <kevi...@gmail.com> wrote:
But when it comes to how exactly someone would make use of this, I'm fuzzy.  I think they could extend it with a class like this:

Could GSCL just extend AbstractModule?

Bob

Kevin Bourrillion

unread,
Mar 14, 2007, 12:48:34 AM3/14/07
to google...@googlegroups.com
It could, but I'd see this as very confusing.  I think the "Guice model" is clearest and easiest to understand when we suitably distinguish the concept of modules from the concept of applications.

A module contains implementation classes and a Module class which brings these all and binds them.  (darkness optional)

An application is what creates an Injector and bootstraps.  For a standalone application, the application contains your main() method; for a servlet app, it contains your GuiceServletContextListener(???).  Etc etc.

The relationship between modules and applications is many-to-many, and I think this is very crucial to understand.

In any case, my main question still stands, which is how to advise users how they would take this GSCL and hook it up.  Or what the best practice otherwise should be for those who live in servletville.

thanks!

K

Hani Suleiman

unread,
Mar 14, 2007, 12:50:15 AM3/14/07
to google...@googlegroups.com

On Mar 14, 2007, at 12:28 AM, Kevin Bourrillion wrote:

> Now I'm trying to figure out what to fill in for "foo koo joo
> boo". My servlet understanding is weak since we don't tend to do
> much with them directly here. I've figured, maybe something like
> this?
>
> public class GuiceServletContextListener implements
> ServletContextListener {
> private final Injector injector;
>
> @Inject public GuiceServletContextListener(Injector injector) {
> this.injector = injector;
> }

No, the context listener creates the injector, it isn't a target of
injection itself.

> And then I think they have some way of registering this class as a
> servlet context listener, but I suspect that the way they do this
> registration may possibly not be standardized between all servlet
> engine implementations, because I can find no method in
> javax.servlet that actually takes in one of these things. But
> again, I'm out of my comfort zone here, so I hope y'all can set me
> straight about this fancy servlet stuff. Thanks!
>

It's done in web.xml, via a <listener> element, and it's part of the
spec.

Dhanji R. Prasanna

unread,
Mar 14, 2007, 12:56:56 AM3/14/07
to google...@googlegroups.com
This is my listener for a generic guice context loader (aka spring's ContextLoaderListener) that I just got done writing
It requires you to specify some config info in web.xml using <context-param> tags.

disclaimer: it's not tested! (sorry about the big post)

/*
 * Created with IntelliJ IDEA.
 *
 * @author dprasanna
 * @since 1.0
 */
public class GuiceLoaderListener implements ServletContextListener {
    private String guiceVarName;
    private static final String DEFAULT_GUICE_VAR_NAME = Injector.class.getName();

    public void contextInitialized(ServletCont extEvent servletContextEvent) {
        //read config parameters from servlet context and use defaults as necessary
        final ServletContext servletContext = servletContextEvent.getServlet Context();
        final String moduleProviderClass = servletContext.getInitParamete r("guice-module-provider");
        final String stageParam = servletContext.getInitParamete r("guice-stage");
        guiceVarName = (null == servletContext.getInitParamete r("guice-injector-name")) ? DEFAULT_GUICE_VAR_NAME
                : servletContext.getInitParamete r("guice-injector-name");

        //try to parse a stage config value (if there is one)
        Stage stage = null;
        if (null != stageParam) {
            try {
                stage = Stage.valueOf(stageParam);
            } catch(IllegalArgumentException e) {
                failStartup("Value specified by parameter guice-stage MUST be one of { DEVELOPMENT, PRODUCTION }, you specified: " + moduleProviderClass);
            }
        }

        //attem pt to locate supplied guice modules provider class and instantiate it
        GuiceModulesProvider modulesProvider = null;
        try {
            modulesProvider = (GuiceModulesProvider) Class.forName(moduleProviderClass).newInstance();
        } catch (ClassNotFoundException e) {
            failStartup("Could not locate class specified by parameter guice-module-provider: " + moduleProviderClass);
        } catch (IllegalAccessException e) {
            failStartup("Could not access class specified by parameter guice-module-provider: " + moduleProviderClass);
        } catch (InstantiationException e) {
            failStartup("Could not instantiate class specified by parameter guice-module-provider: " + moduleProviderClass);
        } catch (ClassCastException e) {
            failStartup("Class specified by parameter guice-module-provider MUST implement interface GuiceModulesProvider, you specified: " + moduleProviderClass);
        }

        //create the guice injector (with stage) if specified
        Injector injector;
        if (null != stage)
            injector = Guice.createInjector(stage, modulesProvider.listModules()) ;
        else
            injector = Guice.createInjector(modulesProvider.listModules());

        //store the created injector into supplied variable (or default) in servlet context
        servletContext.setAttribute(guiceVarName, injector);
    }

    /**
     * A helper method that throws Guice CreationException from the supplied message
     *
     * @param message The message to populate the single-msg CreationException with
     */
    private void failStartup(String message) {
        throw new CreationException(Collections.nCopies(1, new Message(this, message)));
    }

    public void contextDestroyed(ServletContex tEvent servletContextEvent) {
        //dispose injector from servlet context (if it's still there)
        servletContextEvent.getServletContext().setAttribute(gu iceVarName, null);
    }

    /**
     * An interface exposed by any class that supplies a collection of guice modules
     * which is used by the GuiceLoaderListener to populate the Guice injector.
     */
    public static interface GuiceModulesProvider {
        Collection<Module> listModules();

Dhanji R. Prasanna

unread,
Mar 14, 2007, 12:57:40 AM3/14/07
to google...@googlegroups.com
On 3/14/07, Dhanji R. Prasanna <dha...@gmail.com> wrote:
This is my listener for a generic guice context loader (aka spring's ContextLoaderListener) that I just got done writing
It requires you to specify some config info in web.xml using <context-param> tags.

that aka should read "akin to"

Kevin Bourrillion

unread,
Mar 14, 2007, 1:07:25 AM3/14/07
to google...@googlegroups.com
Hi again Dhanji,

Ahh, I see -- you are going a step further, to externalize all the properties that configure Guice.  A nice value-add for those that want it, though I think it's above and beyond what com.google.inject.servlet itself will offer.

Bob and I were talking about setting up a /contrib/ directory where users can share bits of code with each other more easily and durably.  I'll let everyone know what we come up with.

K

Kevin Bourrillion

unread,
Mar 14, 2007, 1:18:52 AM3/14/07
to google...@googlegroups.com
On 3/13/07, Hani Suleiman <ha...@formicary.net> wrote:

On Mar 14, 2007, at 12:28 AM, Kevin Bourrillion wrote:

> Now I'm trying to figure out what to fill in for "foo koo joo
> boo".  My servlet understanding is weak since we don't tend to do
> much with them directly here.  I've figured, maybe something like
> this?
>
>   public class GuiceServletContextListener implements
> ServletContextListener {
>     private final Injector injector;
>
>     @Inject public GuiceServletContextListener(Injector injector) {
>       this.injector = injector;
>     }

No, the context listener creates the injector, it isn't a target of
injection itself.

I figured this would be the normal usage, but I put @Inject there anyway because why the heck not?  It's no-cost, and some servlet engines (probably including ours we use in my company) may have programmatic listener registration and might find it easier to create the Injector first and pull the listener instance from it.  Waaait a minute, am I being insane again?  Gotta stop that...


> And then I think they have some way of registering this class as a
> servlet context listener, but I suspect that the way they do this
> registration may possibly not be standardized between all servlet
> engine implementations, because I can find no method in
> javax.servlet that actually takes in one of these things.  But
> again, I'm out of my comfort zone here, so I hope y'all can set me
> straight about this fancy servlet stuff.  Thanks!
>
It's done in web.xml , via a <listener> element, and it's part of the
spec.

Ah.  So do you think, "subclass this class we provide you, implement the createInjector() method, and list your class as a <listener> in web.xml" is a decent instruction for getting people up and running?

thanks Hani -- by the way, you've been an awesome and welcome presence on the mailing list!  Thanks so much for all the help answering questions.

K

mbr...@gmail.com

unread,
Mar 14, 2007, 3:06:21 AM3/14/07
to google-guice

On Mar 14, 1:18 am, "Kevin Bourrillion" <kevin...@gmail.com> wrote:
>
> Ah. So do you think, "subclass this class we provide you, implement the
> createInjector() method, and list your class as a <listener> in web.xml" is
> a decent instruction for getting people up and running?
>

This seems perfectly reasonable to me. Since in a non-web
application, you're basically creating your Injector in something like
your main() method (or at startup, etc). This would just do the same
thing.

Here's a modification of Dhanji's class to do just that. I just moved
the Injector creation code to an abstract method. You would probably
want to also move the stageParam and guiceVarName code to abstract
methods as well, just to keep the configuration in one place... Of
course, if you want to configure it all in the web.xml file, Dhanji's
approach is more than sufficient.

Marcus

-----

/**
* @author dprasanna
* @author mbreese
*/
public abstract class AbstractGuiceServletContextListener implements
ServletContextListener {
private String guiceVarName;
protected Stage stage = null;

private static final String DEFAULT_GUICE_VAR_NAME =
Injector.class.getName();

public void contextInitialized(ServletContextEvent
servletContextEvent) {
//read config parameters from servlet context and use defaults
as necessary
final ServletContext servletContext =
servletContextEvent.getServletContext();

final String stageParam =
servletContext.getInitParameter("guice-stage");

guiceVarName = (null == servletContext.getInitParameter("guice-
injector-name")) ? DEFAULT_GUICE_VAR_NAME
: servletContext.getInitParameter("guice-injector-
name");

//try to parse a stage config value (if there is one)

if (null != stageParam) {
try {
stage = Stage.valueOf(stageParam);
} catch(IllegalArgumentException e) {
failStartup("Value specified by parameter guice-stage
MUST be one of { DEVELOPMENT, PRODUCTION }, you specified: " +

stageParam);
}
}

Injector injector = null;
injector = createInjector();
if (injector == null) {
failStartup("Could not create injector: createInjector()
returned null");
}

servletContext.setAttribute(guiceVarName, injector);
}

public abstract Injector createInjector(); // protected?

Hani Suleiman

unread,
Mar 14, 2007, 8:39:25 AM3/14/07
to google...@googlegroups.com
On Mar 14, 2007, at 1:18 AM, Kevin Bourrillion wrote:

> I figured this would be the normal usage, but I put @Inject there
> anyway because why the heck not? It's no-cost, and some servlet
> engines (probably including ours we use in my company) may have
> programmatic listener registration and might find it easier to
> create the Injector first and pull the listener instance from it.
> Waaait a minute, am I being insane again? Gotta stop that...
>

Well, in terms of why not, I think it's because it's confusing and
doesn't apply in most cases. Most people will wonder 'how on earth
can guice inject into something that's creating the injector?'

If you're a weirdo and DO need that, then you can subclass the
listener and add the annotation yourself.

>
> Ah. So do you think, "subclass this class we provide you,
> implement the createInjector() method, and list your class as a
> <listener> in web.xml" is a decent instruction for getting people
> up and running?
>

Yep, final step would be to stuff the injector into ServletContext,
which you can then access from anywhere (and note that it can be a
singleton since it's horribly threadsafe).

> thanks Hani -- by the way, you've been an awesome and welcome
> presence on the mailing list! Thanks so much for all the help
> answering questions.
>

My pleasure!

Kevin Bourrillion

unread,
Mar 14, 2007, 11:21:12 AM3/14/07
to google...@googlegroups.com
On 3/14/07, Hani Suleiman <ha...@formicary.net> wrote:

> Well, in terms of why not, I think it's because it's confusing and
> doesn't apply in most cases. Most people will wonder 'how on earth
> can guice inject into something that's creating the injector?'

You're absolutely right!


> > Ah. So do you think, "subclass this class we provide you,
> > implement the createInjector() method, and list your class as a
> > <listener> in web.xml" is a decent instruction for getting people
> > up and running?
> >
> Yep, final step would be to stuff the injector into ServletContext,
> which you can then access from anywhere (and note that it can be a
> singleton since it's horribly threadsafe).

"Horribly threadsafe", I like that. But this injector-stuffing to
which you refer -- is exactly what my listener class is doing.


> > thanks Hani -- by the way, you've been an awesome and welcome
> > presence on the mailing list! Thanks so much for all the help
> > answering questions.
>
> My pleasure!

Oh. What a happy coincidence!

K

Reply all
Reply to author
Forward
0 new messages