Chapter 25. Custom Injection and Lifecycle Management
Prev |  | Next

Links: Table of Contents | Single HTML

# Chapter 25. Custom Injection and Lifecycle Management

Since version 2.0, Jersey uses Glassfish-HK2 library for component life cycle management and dependency injection. Rather than spending a lot of effort in maintaining Jersey specific API (as it used to be before Jersey 2.0 version), Jersey defines several extension points where end-user application can directly manipulate Jersey HK2 bindings using the HK2 public API to customize life cycle management and dependency injection of application components.

Jersey user guide can by no means supply an exhaustive documentation of HK2 API in its entire scope. This chapter only points out the most common scenarios related to dependency injection in Jersey and suggests possible options to implement these scenarios. It is highly recommended to check out the Glassfish-HK2 website and read HK2 documentation in order to get better understanding of suggested approaches. HK2 documentation should also help in resolving use cases that are not discussed in this writing.

There are typically three main use cases, where your application may consider dealing with HK2 APIs exposed in Jersey:

- Implementing a custom injection provider that allows an application to define additional types to be injectable into Jersey-managed JAX-RS components.
- Defining a custom injection annotation (other than @Inject or @Context) to mark application injection points.
- Specifying a custom component life cycle management for your application components.

Since Jersey 2.26, the injection has been abstracted, so that Glassfish-HK2 can be eventually replaced by the CDI or any other injection framework. In the next chapters, we document possibilities provided directly by Glassfish-HK2 and by Jersey abstraction components.

## 25.1. InjectionManager

Since Jersey 2.26, Jersey comes with the main abstraction interface to communicate with the DI container, the InjectionManager. What is `ServiceLocator` for Glassfish-HK2, or `BeanManager` for CDI, that's InjectionManager for Jersey.

InjectionManager can be injected into the user provided classes instantiated by Jersey. It can also be obtained programmatically by InjectionManagerClientProvider and `InjectionManagerProvider` from Jakarta REST components, such as FeatureContext, or MessageBodyReader<T> and MessageBodyReader<T>.

Customers used to the ServiceLocator can still use it directly; the ServiceLocator can be obtained either directly by injection, or programmatically as `InjectionManager.getInstance(ServiceLocator.class)`.

## 25.2. Implementing Custom Injection Provider

Relying on Servlet HTTP session concept is not very RESTful. It turns the originally state-less HTTP communication schema into a state-full manner. However, it could serve as a good example that will help me demonstrate implementation of the use cases described above. The following examples should work on top of Jersey Servlet integration module. The approach that will be demonstrated could be further generalized. Below we will show how to make actual Servlet HttpSession injectable into JAX-RS components and how to make this injection work with a custom inject annotation type. Finally, we will demonstrate how you can write `HttpSession`-scoped JAX-RS resources.

Jersey implementation allows you to directly inject HttpServletRequest instance into your JAX-RS components. It is quite straight forward to get the appropriate `HttpSession` instance out of the injected request instance. Let say, you want to get `HttpSession` instance directly injected into your JAX-RS types like in the code snippet below.

    @Path("di-resource")
    public class MyDiResource {

        @Inject HttpSession httpSession;

        ...

    }

### 25.2.1. Using HK2 classes

To make the above injection work, you will need to define an additional HK2 binding in your application ResourceConfig. Let's start with a custom HK2 Factory implementation that knows how to extract `HttpSession` out of given `HttpServletRequest`.

    import org.glassfish.hk2.api.Factory;
        ...

        public class HttpSessionFactory implements Factory<HttpSession> {

        private final HttpServletRequest request;

        @Inject
        public HttpSessionFactory(HttpServletRequest request) {
            this.request = request;
        }

        @Override
        public HttpSession provide() {
           return request.getSession();
        }

        @Override
        public void dispose(HttpSession t) {
        }
    }

Please note that the factory implementation itself relies on having the actual `HttpServletRequest` instance injected. In your implementation, you can of course depend on other types (and inject them conveniently) as long as these other types are bound to the actual HK2 service locator by Jersey or by your application. The key notion to remember here is that your HK2 `Factory` implementation is responsible for implementing the `provide()` method that is used by HK2 runtime to retrieve the injected instance. Those of you who worked with Guice binding API in the past will most likely find this concept very familiar.

Once implemented, the factory can be used in a custom HK2 `Binder` to define the new injection binding for `HttpSession`. Finally, the implemented binder can be registered in your ResourceConfig:

    import org.glassfish.hk2.utilities.binding.AbstractBinder;
    ...

    public class MyApplication extends ResourceConfig {

        public MyApplication() {

            ...

            register(new AbstractBinder() {
                @Override
                protected void configure() {
                    bindFactory(HttpSessionFactory.class).to(HttpSession.class)
                       .proxy(true).proxyForSameScope(false).in(RequestScoped.class);
                }
            });
        }
    }

Note that if we did not define any explicit injection scope for the new injection binding, By default, HK2 factories are bound in a HK2 PerLookup scope, which is in most cases a good choice, and it is suitable also in our example.

To summarize the approach described above, here is a list of steps to follow when implementing custom injection provider in your Jersey application :

- Implement your own HK2 `Factory` to provide the injectable instances.
- Use the HK2 `Factory` to define an injection binding for the injected instance via custom HK2 `Binder`.
- Register the custom HK2 `Binder` in your application `ResourceConfig`.

While the `Factory`-based approach is quite straight-forward and should help you to quickly prototype or even implement final solutions, you should bear in mind, that your implementation does not need to be based on factories. You can for instance bind your own types directly, while still taking advantage of HK2 provided dependency injection. Also, in your implementation you may want to pay more attention to defining or managing injection binding scopes for the sake of performance or correctness of your custom injection extension.

### Important

While the individual injection binding implementations vary and depend on your use case, to enable your custom injection extension in Jersey, you must register your custom HK2 Binder implementation in your application ResourceConfig!

### 25.2.2. Injection Provider Using Jersey API

To make the `HttpSession` injection work without using HK2 API, we will need to create a custom supplier that knows how to extract `HttpSession` out of given `HttpServletRequest`.

    import java.util.function.Supplier
        ...

    public class HttpSessionSupplier implements Supplier<HttpSession> {

        private final HttpServletRequest request;

        @Inject
        public HttpSessionSupplier(HttpServletRequest request) {
            this.request = request;
        }

        @Override
        public HttpSession get() {
            return request.getSession();
        }

    }

Once implemented, the supplier can be used in a custom Jersey AbstractBinder to define the new injection binding for `HttpSession`. Finally, the implemented binder can be registered in your ResourceConfig:

    import org.glassfish.jersey.internal.inject.AbstractBinder;
        ...
    public class MyApplication extends ResourceConfig {

        public MyApplication() {

            ...

            register(new AbstractBinder() {
                @Override
                protected void configure() {
                    bindFactory(HttpSessionSupplier.class).to(HttpSession.class)
                        .proxy(true).proxyForSameScope(false).in(RequestScoped.class);
                }
            });
        }
    }

The default scope for Jersey binder is similarly as for the HK2, the PerLookup.

To summarize the approach described above, here is a list of steps to follow when implementing custom injection provider in your Jersey application :

- Implement your own Supplier to provide the injectable instances.
- Use the Supplier to define an injection binding for the injected instance via custom AbstractBinder.
- Register the custom AbstractBinder in your application `ResourceConfig`.

### Important

Similarly to the HK2, to enable your custom injection extension in Jersey, you must register your custom AbstractBinder implementation in your application ResourceConfig!

## 25.3. Defining Custom Injection Annotation

Java annotations are a convenient way for attaching metadata to various elements of Java code. Sometimes you may even decide to combine the metadata with additional functionality, such as ability to automatically inject the instances based on the annotation-provided metadata. The described scenario is one of the use cases where having means of defining a custom injection annotation in your Jersey application may prove to be useful. Obviously, this use case applies also to re-used existing, 3rd-party annotation types.

In the following example, we will describe how a custom injection annotation can be supported. Let's start with defining a new custom `SessionInject` injection annotation that we will specifically use to inject instances of HttpSession (similarly to the previous example):

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface SessionInject { }

The above `@SessionInject` annotation should be then used as follows:

    @Path("di-resource")
    public class MyDiResource {

        @SessionInject HttpSession httpSession;

        ...

    }

Again, the semantics remains the same as in the example described in the previous section. You want to have the actual HTTP Servlet session instance injected into your `MyDiResource` instance. This time however, you expect that the `httpSession` field to be injected must be annotated with a custom `@SessionInject` annotation. Obviously, in this simplistic case the use of a custom injection annotation is an overkill, however, the simplicity of the use case will help us to avoid use case specific distractions and allow us better focus on the important aspects of the job of defining a custom injection annotation.

### 25.3.1. Custom Injection Annotation using HK2

If you remember from the previous section, to make the injection in the code snippet above work, you first need to implement the injection provider (HK2 Factory) as well as define the injection binding for the `HttpSession` type. That part we have already done in the previous section. We will now focus on what needs to be done to inform the HK2 runtime about our `@SessionInject` annotation type that we want to support as a new injection point marker annotation. To do that, we need to implement our own HK2 InjectionResolver for the annotation as demonstrated in the following listing:

    import jakarta.inject.Inject;
    import jakarta.inject.Named;

    import jakarta.servlet.http.HttpSession;

    import org.glassfish.hk2.api.InjectionResolver;
    import org.glassfish.hk2.api.ServiceHandle;

    ...

    public class SessionInjectResolver implements InjectionResolver<SessionInject> {

        @Inject
        @Named(InjectionResolver.SYSTEM_RESOLVER_NAME)
        InjectionResolver<Inject> systemInjectionResolver;

        @Override
        public Object resolve(Injectee injectee, ServiceHandle<?> handle) {
            if (HttpSession.class == injectee.getRequiredType()) {
                return systemInjectionResolver.resolve(injectee, handle);
            }

            return null;
        }

        @Override
        public boolean isConstructorParameterIndicator() {
            return false;
        }

        @Override
        public boolean isMethodParameterIndicator() {
            return false;
        }
    }

The `SessionInjectResolver` above just delegates to the default HK2 system injection resolver to do the actual work.

You again need to register your injection resolver with your Jersey application, and you can do it the same was as in the previous case. Following listing includes HK2 binder that registers both, the injection provider from the previous step as well as the new HK2 inject resolver with Jersey application `ResourceConfig`. Note that in this case we're explicitly binding the `SessionInjectResolver` to a @Singleton scope to avoid the unnecessary proliferation of `SessionInjectResolver` instances in the application:

    import org.glassfish.hk2.api.TypeLiteral;
    import org.glassfish.hk2.utilities.binding.AbstractBinder;

    import jakarta.inject.Singleton;

    ...

    public class MyApplication extends ResourceConfig {

        public MyApplication() {

            ...

            register(new AbstractBinder() {
                @Override
                protected void configure() {
                    bindFactory(HttpSessionFactory.class).to(HttpSession.class);

                    bind(SessionInjectResolver.class)
                        .to(new TypeLiteral<InjectionResolver<SessionInject>>(){})
                        .in(Singleton.class);
                }
            });
        }
    }

### 25.3.2. Custom Injection Annotation using Jersey InjectionResolver

Jersey also comes with its InjectionResolver used to translate into the HK2 InjectionResolver during runtime. The abstraction is important for allowing to support the custom injection annotation in various DI containers. For instance, the abstraction is used when supporting injection using @Context in the CDI container (`jersey-cdi2-se` module).

The SessionInjectResolver then looks as follows:

    import jakarta.inject.Inject;

    import jakarta.servlet.http.HttpSession;

    import org.glassfish.jersey.internal.inject.InjectionResolver;

    ...

    public class SessionInjectResolver implements InjectionResolver<SessionInject> {

        private final InjectionManger injectionManager;

        public SessionInjectResolver(InjectionManager) {
            this.injectionManager = injectionManager;
        }

        @Override
        public Object resolve(Injectee injectee) {
            if (HttpSession.class == injectee.getRequiredType()) {
                return injectionManager.getInstance(HttpServletRequest.class).getSession();
            }
            return null;
        }

        @Override
        public boolean isConstructorParameterIndicator() {
            return false;
        }

        @Override
        public boolean isMethodParameterIndicator() {
            return false;
        }

        @Override
        public Class<SessionInject> getAnnotation() {
            return SessionInject.class;
        }
    }

The SessionInjectResolver uses InjectionManager described in Section 25.1, “InjectionManager”.

Unlike with Glassfish-HK2, Jersey InjectionResolver can only be bound as instance in the AbstractBinder. That is why the InjectionManager is used in the `InjectionResolver` to resolve the `HttpSession` instance.

The InjectionResolver can be registered in the with Jersey application `ResourceConfig` as follows:

    import jakarta.ws.rs.core.Feature;

    import org.glassfish.jersey.InjectionManagerProvider;
    import org.glassfish.jersey.internal.inject.AbstractBinder;
    import org.glassfish.jersey.internal.inject.InjectionManager;

    import jakarta.inject.Singleton;

    ...

    public class MyApplication extends ResourceConfig {

    public MyApplication() {

        ...

        register(new Feature() {
            @Override
            public boolean configure(FeatureContext context) {
                final InjectionManager injectionManager = InjectionManagerProvider.getInjectionManager(context);
                context.register(new AbstractBinder() {
                    @Override
                    protected void configure() {
                        bind(new SessionInjectResolver(injectionManager)).to(HttpSession.class).in(Singleton.class);
                    }
                });
                return true;
            }
        });
    }

## 25.4. Custom Life Cycle Management

The last use case discussed in this chapter will cover managing custom-scoped components within a Jersey application. If not configured otherwise, then all JAX-RS resources are by default managed on a per-request basis. A new instance of given resource class will be created for each incoming request that should be handled by that resource class. Let say you want to have your resource class managed in a per-session manner. It means a new instance of your resource class should be created only when a new Servlet HttpSession is established. (As with previous examples in the chapter, this example assumes the deployment of your application to a Servlet container.)

Following is an example of such a resource class that builds on the support for `HttpSession` injection from the earlier examples described in this chapter. The `PerSessionResource` class allows you to count the number of requests made within a single client session and provides you a handy sub-resource method to obtain the number via a HTTP `GET` method call:

    @Path("session")
    public class PerSessionResource {

        @SessionInject HttpSession httpSession;

        AtomicInteger counter = new AtomicInteger();

        @GET
        @Path("id")
        public String getSession() {
            counter.incrementAndGet();
            return httpSession.getId();
        }

        @GET
        @Path("count")
        public int getSessionRequestCount() {
            return counter.incrementAndGet();
        }
    }

Should the above resource be per-request scoped (default option), you would never be able to obtain any other number but 1 from it's getReqs sub-resource method, because then for each request a new instance of our `PerSessionResource` class would get created with a fresh instance `counter` field set to 0. The value of this field would get incremented to 1 in the the `getSessionRequestCount` method before this value is returned. In order to achieve what we want, we have to find a way how to bind the instances of our `PerSessionResource` class to `HttpSession` instances and then reuse those bound instances whenever new request bound to the same HTTP client session arrives. Let's see how to achieve this.

To get better control over your Jersey component instantiation and life cycle, you need to implement a custom Jersey ComponentProvider SPI, that would manage your custom components. Although it might seem quite complex to implement such a thing, the component provider concept in Jersey is in fact very simple. It allows you to define your own HK2 injection bindings for the types that you are interested in, while informing the Jersey runtime at the same time that it should back out and leave the component management to your provider in such a case. By default, if there is no custom component provider found for any given component type, Jersey runtime assumes the role of the default component provider and automatically defines the default HK2 binding for the component type.

Following example shows a simple `ComponentProvider` implementation, for our use case. Some comments on the code follow.

    import jakarta.inject.Inject;
    import jakarta.inject.Provider;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpSession;
    ...
    import org.glassfish.hk2.api.DynamicConfiguration;
    import org.glassfish.hk2.api.DynamicConfigurationService;
    import org.glassfish.hk2.api.Factory;
    import org.glassfish.hk2.api.PerLookup;
    import org.glassfish.hk2.api.ServiceLocator;
    import org.glassfish.hk2.utilities.binding.BindingBuilderFactory;
    import org.glassfish.jersey.server.spi.ComponentProvider;

    @jakarta.ws.rs.ext.Provider
    public class PerSessionComponentProvider implements ComponentProvider {

        private ServiceLocator locator;

        static class PerSessionFactory implements Factory<PerSessionResource>{
            static ConcurrentHashMap<String, PerSessionResource> perSessionMap
                    = new ConcurrentHashMap<String, PerSessionResource>();

            private final Provider<HttpServletRequest> requestProvider;
            private final ServiceLocator locator;

            @Inject
            public PerSessionFactory(
                    Provider<HttpServletRequest> request,
                    ServiceLocator locator) {

                this.requestProvider = request;
                this.locator = locator;
            }

            @Override
            @PerLookup
            public PerSessionResource provide() {
                final HttpSession session = requestProvider.get().getSession();

                if (session.isNew()) {
                    PerSessionResource newInstance = createNewPerSessionResource();
                    perSessionMap.put(session.getId(), newInstance);

                    return newInstance;
                } else {
                    return perSessionMap.get(session.getId());
                }
            }

            @Override
            public void dispose(PerSessionResource r) {
            }

            private PerSessionResource createNewPerSessionResource() {
                final PerSessionResource perSessionResource = new PerSessionResource();
                locator.inject(perSessionResource);
                return perSessionResource;
            }
        }

        @Override
        public void initialize(ServiceLocator locator) {
            this.locator = locator;
        }

        @Override
        public boolean bind(Class<?> component, Set<Class<?>> providerContracts) {
            if (component == PerSessionResource.class) {

                final DynamicConfigurationService dynamicConfigService =
                    locator.getService(DynamicConfigurationService.class);
                final DynamicConfiguration dynamicConfiguration =
                    dynamicConfigService.createDynamicConfiguration();

                BindingBuilderFactory
                    .addBinding(BindingBuilderFactory.newFactoryBinder(PerSessionFactory.class)
                    .to(PerSessionResource.class), dynamicConfiguration);

                dynamicConfiguration.commit();

                return true;
            }
            return false;
        }

        @Override
        public void done() {
        }
    }

The first and very important aspect of writing your own `ComponentProvider` in Jersey is to store the actual HK2 ServiceLocator instance that will be passed to you as the only argument of the provider `initialize` method. Your component provider instance will not get injected at all so this is more or less your only chance to get access to the HK2 runtime of your application. Please bear in mind, that at the time when your component provider methods get invoked, the `ServiceLocator` is not fully configured yet. This limitation applies to all component provider methods, as the main goal of any component provider is to take part in configuring the application's `ServiceLocator`.

Now let's examine the `bind` method, which is where your provider tells the HK2 how to bind your component. Jersey will invoke this method multiple times, once for each type that is registered with the actual application. Every time the `bind` method is invoked, your component provider needs to decide if it is taking control over the component or not. In our case we know exactly which Java type we are interested in (`PerSessionResource` class), so the logic in our `bind` method is quite straightforward. If we see our `PerSessionResource` class it is our turn to provide our custom binding for the class, otherwise we just return false to make Jersey poll other providers and, if no provider kicks in, eventually provide the default HK2 binding for the component. Please, refer to the Glassfish-HK2 documentation for the details of the concrete HK2 APIs used in the `bind` method implementation above. The main idea behind the code is that we register a new HK2 Factory (`PerSessionFactory`), to provide the `PerSessionResource` instances to HK2.

The implementation of the `PerSessionFactory` is also included above. Please note that as opposed to a component provider implementation that should never itself rely on an injection support, the factory bound by our component provider would get injected just fine, since it is only instantiated later, once the Jersey runtime for the application is fully initialized including the fully configured HK2 runtime. Whenever a new session is seen, the factory instantiates and injects a new PerSessionResource instance. The instance is then stored in the perSessionMap for later use (for future calls).

In a real life scenario, you would want to pay more attention to possible synchronization issues. Also, we do not consider a mechanism that would clean-up any obsolete resources for closed, expired or otherwise invalidated HTTP client sessions. We have omitted those considerations here for the sake of brevity of our example.

Prev |  | Next
Chapter 24. Monitoring and Diagnostics | Home | Chapter 26. Jersey CDI Container Agnostic Support