I am trying to @Inject a Guice service into a @ServerEndpoint. I am using Tomcat 8.0.15 as the JSR-356 implementation. However, the dependency injection isn't working. Is there any additional configuration that needs to be done in order to enable Guice injection? Note that I am using all standard javax annotations only.
-
"the dependency injection isn't working" ... can you describe that in more detail? – Tavian Barnes Dec 16 '14 at 19:41
-
I get an instantiation exception when I try to do a constructor injection using @Inject inside the SeverEndpoint. – Aritra Dec 16 '14 at 21:29
3 Answers
I figured this out. The Websocket endpoint needs to have a custom configurator, which creates and returns instances using the Guice injector instance.
Example:
Custom Guice servlet context listener:
public class CustomServletContextListener extends GuiceServletContextListener {
public static Injector injector;
@Override
protected Injector getInjector() {
injector = Guice.createInjector(...);
return injector;
}
}
Websockets custom configurator:
public class CustomConfigurator extends Configurator {
@Override
public <T> T getEndpointInstance(Class<T> clazz)
throws InstantiationException {
return CustomServletContextListener.injector.getInstance(clazz);
}
}
And then in the Websocket endpoint:
@ServerEndpoint(value = "/ws/sample_endpoint", configurator = CustomConfigurator.class)
public class SampleEndpoint {
private final SomeService service;
@Inject
public SampleEndpoint(SomeService service) {
this.service = service;
}
...
}

- 1,234
- 12
- 20
-
it is much safer to use field/setter injections and do `super.getEndpointInstance(endpointClass)` and then `injector.injectMembers(endpointInstance)`. That's because `super` may return some container-specific decorator wrapping the newly created instance of `endpointClass`. Furthermore the spec requires Endpoint classes to have a paramless constructor, so some containers may refuse to deploy the above `SampleEndpoint` at all... ...and of course storing anything on static vars is just asking for trouble in sooo many ways ;-] – morgwai Aug 20 '23 at 12:48
Building upon Aritra's own answer:
To be honest, I don't know for sure if this works with Guice 3.0, but it does work for 4.0, which is the current stable release.
I think a somewhat cleaner approach is to change your CustomConfigurator into something like this:
public class CustomConfigurator extends Configurator {
@Inject
private static Injector injector;
public <T> T getEndpointInstance(Class<T> endpointClass) {
return injector.getInstance(endpointClass);
}
}
And then from your extended ServletModule
class' configureServlets
method, call requestStaticInjection(CustomConfigurator.class)
That way you won't expose the injector to everyone. I don't know about you, but it gives me a nice and fuzzy feeling inside to know that no one will be able to mess with my injector :-).

- 477
- 5
- 14
-
this answer has exactly the same issues as described in https://stackoverflow.com/questions/27499800/tomcat8-websockets-jsr-356-with-guice-3-0#comment135633934_28031697 – morgwai Aug 20 '23 at 12:51
First, using annotations to do any "magic" behind the scene is a bad idea in general: it's much better to deploy Endpoints
programmatically in ServletContextListener.contextInitialized(event)
with ServerContainer.addEndpoint(config)
, so that you have the full control and can avoid storing injector on static vars.
Now regarding the injection, the solution is to define your custom ServerEndpointConfig.Configurator
as stated in other answers already, however it is much safer to use field/setter injections in Endpoint
classes and call super.getEndpointInstance(endpointClass)
followed by injector.injectMembers(endpointInstance)
. That's because super
(default Configurator
impl of the given container) may return instances of container-specific dynamic subclasses or decorators wrapping the newly created instance of endpointClass
. Furthermore, the spec requires Endpoint
classes to have a paramless constructor, so some containers may refuse to deploy Endpoint
classes that use constructor params for injections.
public class MyConfigurator extends ServerEndpointConfig.Configurator {
public <EndpointT> EndpointT getEndpointInstance(Class<EndpointT> endpointClass)
throws InstantiationException {
EndpointT endpointInstance = super.getEndpointInstance(endpointClass);
injector.injectMembers(endpointInstance);
return endpointInstance;
}
}
Now a SevletContextListener
that adds Endpoints
programmatically:
public class MyServletCtxListener implements SevletContextListener {
ServerEndpointConfig.Configurator endpointConfigurator;
ServerContainer endpointContainer;
void addEndpoint(Class<?> endpointClass, String path) throws DeploymentException {
endpointContainer.addEndpoint(
ServerEndpointConfig.Builder
.create(endpointClass, path)
.configurator(endpointConfigurator)
.build()
);
}
public void contextInitialized(ServletContextEvent initialization) {
final var ctx = initialization.getServletContext();
endpointContainer = (ServerContainer)
ctx.getAttribute("javax.websocket.server.ServerContainer");
final var injector = Guice.createInjector(
// put modules here...
);
endpointConfigurator = new MyConfigurator(injector); // NO STATIC :)
try {
addEndpoint(MyEndpoint.class, "/websocket/my");
addEndpoint(MyOtherEndpoint.class, "/websocket/myOther");
addEndpoint(MyYetAnotherEndpoint.class, "/websocket/myYetAnother");
// ...
} catch (DeploymentException e) {
e.printStackTrace();
System.exit(1); // fail fast
}
}
}
Note that if you go for programmatic adding, your Endpoint
classes should be extending javax.websocket.Endpoint
/ jakarta.websocket.Endpoint
according to the spec (although AFAIR Tomcat specifically used to be relaxed regarding this requirement).
Some related self-promotion: for anyone combining guice with websockets, you may find useful my lib that provides custom Scopes
for ws Endpoints
: https://github.com/morgwai/servlet-scopes

- 2,513
- 4
- 25
- 31