jersey/jersey — pull request #123 by sarxos (2014-11-06 16:38:02+00:00)
Title: Add possibility to use parent service locator with Jersey servlet

Hello Jersey Team!

Let me describe what is the intention of this pull request.

Our application is build on top of embedded Jetty hosting few servlets designed for different things (REST, WebSockets, JPS GUI, etc), but the REST API build on top of Jersey is a heart of the overall system. Because Jersey is using HK2 to wire everything, we decided to stick into this approach (for the sake of simplicity, compatibility, and because we found that using HK2 mixed with Spring or Guice is a little bit tricky). While the application growth we had to extract more and more (services, models, daos, etc) to the abstraction layer above the Jersey server and Jetty itself. This is when we found that we are not able to tell Jersey to use our own HK2 ```ServiceLocator``` instance. There is a possibility to pass ```ServiceLocator``` as one of the arguments in ```ApplicationHandler``` (this is good), but we are using ```org.glassfish.jersey.servlet.ServletContainer``` to start Jersey and it does not define API to do such a thing.

Our ```web.xml```

```xml

rest-api
org.glassfish.jersey.servlet.ServletContainer

javax.ws.rs.Application
pl.megazord.foo.RestApplication

com.sun.jersey.api.json.POJOMappingFeature
true

1

```

```ServiceLocator```, created by Jersey, have no knowledge of our services produced by the Maven hk2-inhabitant-generator plugin:

```xml

org.glassfish.hk2
hk2-inhabitant-generator
2.3.0

generate-inhabitants

```

To workaround these issues we had to implement very ugly hack, which basically do the following:
* capture ```ServiceLocator``` created in the Jersey server internals (on servlet startup),
* store it in static class to be accessed as global variable,
* populate it with ```Populator``` from ```DynamicConfigurationService```.

```java
public class RestApplication extends ResourceConfig {

// inject ServiceLocator into application constructor so we can capture it
@Inject
public RestApplication(ServiceLocator locator) {
// by doing this, we can use InjectionUtils to access ServiceLocator in other
// places, not limited to injection path
InjectionUtils.init(locator);
}
// ...
}
```

```java
// ugly
public static ServiceLocator init(ServiceLocator locator) {

LOCATOR = locator;

DynamicConfigurationService dcs = LOCATOR.getService(DynamicConfigurationService.class);
Populator populator = dcs.getPopulator();
List> descriptors = null;
try {
descriptors = populator.populate();
} catch (IOException e) {
throw new MultiException(e);
}

return LOCATOR;
}
```

But the above hack has one, very big, issue - the Jersey must start early, immediately after application boostrap (because it acts as a ```ServiceLocator``` factory). We created solution to delay other components creation waiting for the Jersey, but this sucks and becomes to be not maintainable.

The clean solution we've implemented is provided in this pull request. The idea is to get register ```ServiceLocator``` in web application context before the ```ServletContainer``` is started, and let it read it from servlet context when ```WebComponent``` is being created.

This is part of our application bootstrap code:

```java
// create default service locator which will populate inhabitants
ServiceLocator locator = ServiceLocatorUtilities.createAndPopulateServiceLocator();

// create org.eclipse.jetty.webapp.WebAppContext.WebAppContext
WebAppContext ctx = new WebAppContext();
ctx.setServer(server);
ctx.setContextPath("/");
ctx.setWar(webApplicationPath);
ctx.setAttribute(WebComponent.SERVICE_LOCATOR_ATTRIBUTE, locator);
```

This setting is later read in ```WebComponent``` constructor (the ```null``` will be returned if it has not been set) and passed to ```ApplicationHandler```:

```java
// ...
ServiceLocator locator = (ServiceLocator) webConfig.getServletContext().getAttribute(SERVICE_LOCATOR_ATTRIBUTE);
this.appHandler = new ApplicationHandler(resourceConfig, webComponentBinder, locator);
// ...
```

This is the very idea of this pull request.

I also added public ```getWebComponent()``` and ```getAppHandler()``` methods so there is no need to hack ```ServletContainer``` with reflections to access existing ```ApplicationHandler``` instance.

I'm sending this pull request to branch 2.6.x because this is the version we are using and the one I tested these modifications against. I will be happy to cherry pick it to master (as separate pull request) when there is no issue with the proposed code change.

PS. Please let me know if there is a cleaner/better approach of how this should be resolved. For now I identified this as the easiest and the best solution I could think of.