0

I have a resource in my Jersey REST api that has a private instance variable:

@Path("test")
public class TestResource implements ServletContextListener
{
    private String someString;

    @GET
    public void test()
    {
        System.out.println("someString " + someString);
    }

    @Override
    public void contextDestroyed(ServletContextEvent ctxt) {
        System.out.println("Destroying context");
    }

    @Override
    public void contextInitialized(ServletContextEvent ctxt) {
        System.out.println("TestResource initialized!");

        someString = "SET";
        System.out.println("someString has been set. someString: " + someString);
    }
}

On server startup/restart the instance variable someString is initialized during contextInitialized() and is correctly printed out. However when I set a GET request to http://localhost:8080/myapp/api/test (i.e. invoke the test() method above) the variable someString is null.

How can I prevent that?

Sanchit Patiyal
  • 4,910
  • 1
  • 14
  • 31
tenticon
  • 2,639
  • 4
  • 32
  • 76
  • 1
    I'm gonna go out on a limb here and suggest that contextInitialized is not being called on the instance of TestResource created to handle the GET. Just sayin... – JJF Dec 05 '17 at 18:10
  • ah ok, yes makes sense. This is terrible for me because I will have to work with a variable that is very expensive to initialize and the same instance should be used for all http requests. any ideas? – tenticon Dec 05 '17 at 18:13
  • making the variable static helps but I feel like I am not really understanding jersey at this point – tenticon Dec 05 '17 at 18:22
  • Singleton pattern? – JJF Dec 06 '17 at 00:21
  • yes, that's also an option. Similarly, there is a `@Singleton` annotation in jersey where you can force to reuse the resource for each request but I don't know how smart this is for concurrent requests. See https://stackoverflow.com/questions/18914130/when-to-use-singleton-annotation-of-jersey – tenticon Dec 06 '17 at 08:12

2 Answers2

2

From the JAX-RS specification:

By default a new resource class instance is created for each request to that resource.

So, any state you set on your resource class instance is meaningless, since the instance is never used again. If you want to persist a value, place it in the ServletContext’s attributes:

// All classes in the web app share a ServletContext, so attribute names
// need to start with package names to prevent collisions.
private static final String SOME_ATTRIBUTE = TestResource.class.getName() + ".someAttribute";

@Override
public void contextInitialized(ServletContextEvent ctxt) {
    System.out.println("TestResource initialized!");

    String someString = "SET";
    System.out.println("someString has been set. someString: " + someString);

    ctxt.getServletContext().setAttribute(SOME_ATTRIBUTE, someString);
}

@GET
public void test(@Context ServletContext context) {
    System.out.println("someString " + context.getAttribute(SOME_ATTRIBUTE));
}

Storing values in static fields will require you to implement thread safety and will not work in a distributed production environment.

VGR
  • 40,506
  • 4
  • 48
  • 63
1

I believe this should be a comment but I don't have enough reputation. So I am writing as an answer.

This question gives an example for @Singleton annotation. It provides a cleaner approach.

Orçun Çolak
  • 182
  • 1
  • 1
  • 7