I recently switched to two phase injection and this has created an error in my servlet binding. I am currently toggling between two error modes and not sure which direction is best to pursue.
The first error I encountered was:
com.sun.jersey.api.container.ContainerException: The ResourceConfig
instance does not contain any root resource classes.
My servlet module looked like this:
public class MyServletModule extends JerseyServletModule {
@Override
protected void configureServlets() {
bind(MyServlet.class).asEagerSingleton();
serve("/*").with(GuiceContainer.class);
}
}
I was able to remove this error by explicitly providing the com.sun.jersey.config.property.packages parameter.
public class MyServletModule extends JerseyServletModule {
@Override
protected void configureServlets() {
bind(MyServlet.class).asEagerSingleton();
Map<String,String> parameters = new HashMap<String, String>();
parameters.put(PackagesResourceConfig.PROPERTY_PACKAGES, MyServlet.class.getPackage().getName());
serve("/*").with(GuiceContainer.class, parameters);
}
}
But when I do this, Guice attempts a Just in Time binding which does not respect the @Inject on my servlet constructor.
com.google.inject.ConfigurationException: Guice configuration errors:
1) Unable to create binding for MyServlet. It was already configured
on one or more child injectors or private modules
bound at MyServletModule.configureServlets(MyServletModule.java:44) If it was
in a PrivateModule, did you forget to expose the binding? while
locating MyServlet
1 error at
com.google.inject.internal.InjectorImpl.getBinding(InjectorImpl.java:150)
My servlet has an @Inject constructor who's arguments cannot be bound just in time. After debugging into InjectorImpl, I believe this is the reason that things fail when I use PROPERTY_PACKAGES.
I'm just not sure if using PROPERTY_PACKAGES is correct and I need to fix some bindings? Or if that is the wrong direction and I need to fix the original ResourceConfig error in a different way.
Help or a push in the right direction is appreciated.
I was able to bind Jersey resources to Guice without using the bind-parameters (without explicitly providing the com.sun.jersey.config.property.packages parameter), by binding resources separately
public class BindJerseyResources extends ServletModule {
@Override
protected void configureServlets() {
// excplictly bind GuiceContainer before binding Jersey resources
// otherwise resource won't be available for GuiceContainer
// when using two-phased injection
bind(GuiceContainer.class);
// bind Jersey resources
PackagesResourceConfig resourceConfig = new PackagesResourceConfig("jersey.resources.package");
for (Class<?> resource : resourceConfig.getClasses()) {
bind(resource);
}
// Serve resources with Jerseys GuiceContainer
serve("/*").with(GuiceContainer.class);
}
}
The resources are like following
@Path("/")
@RequestScoped
public class Resource {
private Storage storage;
@Inject
public Resource(Storage storage) {
this.storage = storage;
}
@GET
@Path("get/{name}")
@Produces(MediaType.TEXT_PLAIN)
public String getGuid(@PathParam("name") String name) {
return storage.get(name);
}
}
Maybe this helps you to avoid the latter problematic binding.
Updated the answer to work with two-phased injection.
Java Part
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import com.google.inject.persist.jpa.JpaPersistModule;
import com.google.inject.servlet.GuiceServletContextListener;
import com.sun.jersey.guice.JerseyServletModule;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
import com.sun.jersey.spi.container.servlet.ServletContainer;
import com.thjug.apipublic.Echo;
public class ServletContextListener extends GuiceServletContextListener {
@Override
protected Injector getInjector() {
final Injector injector = Guice.createInjector(new JerseyServletModule() {
@Override
protected void configureServlets() {
bind(Echo.class);
bind(ServletContainer.class).in(Singleton.class);
serve("/*").with(GuiceContainer.class);
}
}, new JpaPersistModule("dbUnit"), new LoggingModule());
injector.getInstance(JPAInitializer.class);
return injector;
}
}
web.xml
<distributable />
<display-name>API</display-name>
<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>guiceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<description>Guice Initiate</description>
<listener-class>com.thjug.base.ServletContextListener</listener-class>
</listener>
Below is my keynote about How to make REST with Guice
http://www.slideshare.net/nuboat/lightweight-javaee
Here's an article on how to do the binding (including full source code):
implementing-distributed-counter