Over a million developers have joined DZone.

JAX-RS 2: Custom @Context Injection of a Limited, Thread-Unsafe Resource

· Integration Zone

Visually compose APIs with easy-to-use tooling. Learn how IBM API Connect provides near-universal access to data and services both on-premises and in the cloud, brought to you in partnership with IBM.

I've been working on wrapping a stateful CGI/Perl web application in a stateless, RESTful interface using JAX-RS 2. Besides the mechanics of interacting with the CGI/Perl web application (think judicious use of HTTP clients and HTML scraping), one challenging aspect has been ensuring that simultaneous requests to the REST service do not end up sharing a CGI/Perl application session.

Each request must establish and end a session for a particular webapp user; any incoming requests during that time should not establish a session with that same user.

My initial idea was to customize Tomcat's thread pool executor so that each thread was bound to a particular user. I made progess in this area, but abandoned the approach after struggles with the under-documented tomcat7-maven-plugin. Besides, I was leery of being coupled to Tomcat at such a low level given how my custom org.apache.catalina.Executor "borrowed" heavily from the standard implementation.

The approach I did utimately settle on consists of the following components:

  • A Maven-based Spring Boot + Jersey 2 project with two sub-modules
    • The CGI/Perl web application interaction library
    • The JAX-RS web service providing the RESTful interface around that library
  • java.util.concurrent.BlockingQueue to manage Thread access to the pool of limited user ids, along with a custom Servlet Filter to manage the queue.
  • custom injection provider, responsible for injecting sessions into JAX-RS resources via@Context or @Inject annotations.

The Maven, Spring Boot, and JAX-RS 2 with Jersey Platform

Here's POM configuration for the JAX-RS component (the parent POM and the POM for the interaction library are not exciting or relevant to the matter at hand).

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.    apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">




Important areas to highlight:

  • Because I had my own parent POM, I used the dependencyManagement POM import approach to pull in Spring Boot dependencies.
  • I customized the maven-war-plugin to not failOnMissingWebXml since I was using annotations to configure my Servlet application and did not want to create an empty web.xml

I defined my pool of CGI/Perl application user ids in within asrc/main/resources/application.properties file


and created a custom configuration class for retrieving these users as a java.util.Set to avoid issues if the same user id were specified multiple times:

import com.google.common.base.Splitter;
import static org.apache.commons.collections.IteratorUtils.toList;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
public class MyAppConfig {
    private String userIdsConf;

    public Set<String> getUserIds() {
        return userIdsConf == null ? null 
            : new HashSet<String>(toList(Splitter.on(',').trimResults().split(userIdsConf).iterator()));

By marking this class as a @Component, I could rely on Spring to manage and inject this configuration into other beans.

Finally, I bootstrapped my web application using a custom Jersey ResourceConfig extension, decorated with Spring Boot goodness.

import com.example.bdkosher.restapi.AppSessionInjectionFactory;
import com.example.bdkosher.AppSession;
import javax.ws.rs.ApplicationPath;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Component;

public class CgiWrapplication extends ResourceConfig {
    public CgiWrapplication() {
        register(new AbstractBinder() {
            protected void configure() {

    public static void main(String... args) {
        SpringApplication.run(CgiWrapplication.class, args);

Within the constructor, I bind the AppSession instances I wish to inject to the factory class I will use to inject them, the creatively-named AppSessionInjectionFactory.

The AppSession class is the entry point of the CGI/Perl application interaction library. Its code is irrelevant to the topic aside from the fact that

  • Each AppSession instance is tied to a particular, precious user id String.
  • AppSession implements the java.lang.AutoCloseable interface. This allows it to be used inside try-with-resources blocks, as well as informing API users that this is a resource that must be closed.

Servlet Filter

I wanted to manage the BlockingQueue within AppSessionInjectionFactory exclusively, but hit scoping issues.

Namely, the default behavior of Jersey was to create a new instance of the factory for each injection. I attempted changing the scopes of the bindings and changing my overall binding strategy tobindFactory(new AppSessionInjectionFactory()).to(AppSession.class) (note how I create a single instance rather than provide the bindFactory method with the class literal). In both cases, I couldn't get Spring to inject the AppConfig bean into AppSessionInjectionFactory. Thus, I settled on using a Servlet Filter, which I knew would only be instantiated once. Here is the code for the filter, the most interesting part of the application, in my opinion.

import static com.google.common.base.Preconditions.checkNotNull;
import com.example.bdkosher.restapi.AppConfig;
import com.example.bdkosher.AppSession;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import javax.inject.Singleton;
import javax.servlet.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@WebFilter(description = "Enforces that each HTTP Servlet Request is associated with one and only one session.")
public class AppSessionFilter implements Filter {
    private BlockingQueue<String> sessionUserPool;

    private AppConfig config;

    public void init(FilterConfig ignored) throws ServletException {
        Set<String> userIds = checkNotNull(checkNotNull(config).getUserIds());
        sessionUserPool = new ArrayBlockingQueue<>(userIds.size(), true, userIds);

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        String userId = null;
        try {
            /* This method call will block until a userId is avaialable from the pool. */
            userId = sessionUserPool.take();
            /* The try-with-resources block ensures the session is closed, even if the filter chain leaves it open. */
            try (AppSession session = AppSession.open(userId)) {
                req.setAttribute(AppSession.class.getName(), session); // stash the session in the ServletRequest
                chain.doFilter(req, res);
        } catch (InterruptedException ex) {
            throw new ServletException("Issue obtaining session user from pool.", ex);
        } finally {
            /* Return the userId into the pool so future requests can use it. */
            if (userId != null) {

    public void destroy() {

The doFilter method attempts to take a user id from the BlockingQueue. If there are no user ids available, it blocks until a user id is put back into the pool. Once a user id is obtained, it will instantiate an AppSession and stash it in a Servlet Request attribute for future use by other filters in the chain. Once the filter chain returns, it replaces the user id in the BlockingQueue.

Custom Injection Factory

The final piece is the factory itself, which makes the request attribute-bound AppSession available to our JAX-RS resources.

The code for this class is very straightforward. The only tricky part is annotating the class to be request-scoped so that it can be injected with the current HttpServletRequest upon instantiation.

import com.example.bdkosher.AppSession;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import org.glassfish.hk2.api.Factory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

public class AppSessionInjectionFactory implements Factory<AppSession> {
    private final AppSession session;

    public AppSessionInjectionFactory(HttpServletRequest request) {
        Object sessionObj = request.getAttribute(AppSession.class.getName());
        if (sessionObj == null) {
            throw new IllegalStateException("No Session found to inject. Did you configure the AppSessionFilter correctly?");
        this.session = (AppSession) sessionObj;

    public AppSession provide() {
       return session;

    public void dispose(AppSession session) {
       // intentionally empty

The Payoff

Now that we've gone through all of this hassle to ensure no two request threads are sharing the same AppSession instance, let's actually take a look at an example JAX-RS resource.

import com.example.bdkosher.AppSession;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import org.springframework.stereotype.Component;

public class HelloWorldResource {

    public String greet(@Context AppSession session) {
        return "Hello, " + session.getUserId() + "!";

In the real application, I'm interacting with the AppSession object's more useful methods. But this simple example illustrates the main objective: I've injected my limited resource into my JAX-RS resource, freeing my service code from the responsibilties of managing it and protecting it from shared access by other request threads.

Visually compose APIs with easy-to-use tooling. Learn how IBM API Connect provides near-universal access to data and services both on-premises and in the cloud, brought to you in partnership with IBM.

concurency,maven,jersey,jax-rs,jax-rs 2,spring boot,java,integration,rest

The best of DZone straight to your inbox.

Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}