-
Notifications
You must be signed in to change notification settings - Fork 33
Home
Jersey/HK2 uses unfortunately SPIs and the Singleton pattern internally. Your code is effectively racing against the Servlet container's code and the first one to initialize HK2's ServiceLocatorGenerator
inside its ServiceLocatorFactory
wins.
This library uses two approaches to override HK2's own ServiceLocatorGenerator
. It first tries to use a SPI and if it can't it'll fall back to reflection to replace a private static
field. Regardless of the approach it's still a race against the Servlet container.
HK2's ServiceLocators
are the equivalent of Guice Injectors
. There's one per ApplicationHandler
(effectively one per Connector). Each locator has a name and they're numbered in the order of instantiation. The naming convention appears to be "__HK2_Generated_0"
... "__HK2_Generated_N"
.
In the following example we're saying that we want to override HK2's 0th
locator with Guice. This is perfectly fine code if you have something like a Jetty Embedded application with one Connector.
public static void main(String[] args) {
List<Module> modules = new ArrayList<>();
modules.add(new JerseyGuiceModule("__HK2_Generated_0"));
modules.add(new ServletModule());
modules.add(new AbstractModule() {
@Override
protected void configure() {
// ...
}
});
Injector injector = Guice.createInjector(modules);
JerseyGuiceUtils.install(injector);
// ... continue ...
}
A more likely scenario is that you want to replace all "__HK2_Generated_*"
instances. For something like a Dropwizard environment it'd replace the locator for the service connector as well as the admin connector.
public static void main(String[] args) {
List<Module> modules = new ArrayList<>();
modules.add(new ServletModule());
modules.add(new AbstractModule() {
@Override
protected void configure() {
// ...
}
});
final Injector parentInjector = Guice.createInjector(modules);
JerseyGuiceUtils.install(new ServiceLocatorGenerator() {
@Override
public ServiceLocator create(String name, ServiceLocator parent) {
if (!name.startsWith("__HK2_Generated_")) {
return null;
}
List<Module> modules = new ArrayList<>();
modules.add(new JerseyGuiceModule(name));
modules.add(new AbstractModule() {
@Override
protected void configure() {
// ...
}
});
return parentInjector.createChildInjector(modules)
.getInstance(ServiceLocator.class);
}
});
// ... continue ...
}
Please remember that Guice's AOP is only working with objects that were instantiated by Guice. The problem with HK2 is that it will instantiate an object on its own if it can.
You can force HK2 to use Guice by simply adding explicit bindings for these classes.
new AbstractModule() {
@Override
protected void configure() {
bind(MyResource.class);
}
};