3

Here I have a singleton, that I whant to inject to my application

@Singleton
@Path("singleton-bean")
public class MyContext {

    private MyContext() {
        instances++;
    }

    private static MyContext instance;

    public static MyContext getInstance(){
        if (instance == null)
            instance = new MyContext();
        return instance;
    }

    public static int instances = 0;

}

Here's how I register it:

@ApplicationPath("webresources")
public class ApplicationConfig extends Application {

    @Override
    public Set<Object> getSingletons() {
        final Set<Object> singletons = new HashSet<>();
        singletons.add(MyContext.getInstance());
        return singletons;
    }

    //.....

Finally, I print the nuber of singletons in request:

@Path("foo")
public class Foo {

    @Inject
    public MyContext message;

    @GET
    public String index() throws UnknownHostException {
        return String.format("%s number of instances: %s", message, MyContext.instances);
    }

It returns two instances. I understand that Jersey uses reflections to access private constructor and create another instance. Why is this happening and how do I prevent this?

Rami
  • 7,879
  • 12
  • 36
  • 66
Ben
  • 3,989
  • 9
  • 48
  • 84

2 Answers2

4

The getSingletons has nothing to do with injections. It is meant to register singleton JAX-RS components (i.e. resources classes and providers), which don't actually need to be "classic" singletons. They can just be an instance of a regular class.

To handle injection of arbitrary components/services with Jersey 2.x, see Custom Injection and Lifecycle Management.

The general pattern is to create a Factory<T> implementation, with T being the injectable type. Then the factory needs to be registered with the Jersey runtime. One way to do that is through an AbstractBinder. For example

public class MyContextProvider implements Factory<MyContext> {

    @Override
    public MyContext provide() {
        return new MyContext();
    }

    @Override
    public void dispose(Bar bar) {}
}

Then bind it in your subclass of ResourceConfig (which is a subclass of Application).

@ApplicationPath("/webresources")
public class AppConfig extends ResourceConfig {

    public AppConfig() {

        packages("com.stackoverflow.jersey");

        register(new AbstractBinder(){
            @Override
            protected void configure() {
                bindFactory(MyContextProvider.class)
                        .to(MyContext.class)
                        .in(Singleton.class);
            }
        });
    }
}

The packages method allows for scanning of the package and sub-packages for resource classes (classes annotated with @Path) and providers (classes annotated with @Provider), so you don't need to explicitly register them.

You will also need to make sure you have all the needed compile-time dependencies. If you are using Maven, just use

<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>2.19</version>
    <scope>provided</scope>
</dependency> 

The provided scope is for if you are using Glassfish, as Glassfish alredy has the jars. You don't want to duplicate the jars with different version. If you are just in a servlet container like Tomcat, you can remove the <scope>. If you are not using Maven, then you need to add the jars manually from the Jersey JAX-RS 2.0 RI bundle. And likewise, if you are in Glassfish, you need to make the jars only compile-time jars. You do not want to include them into the build of the war.

svarog
  • 9,477
  • 4
  • 61
  • 77
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
-4

You can add this in your constructor :

if (instance == null)
   instance = this;
Andy
  • 49,085
  • 60
  • 166
  • 233
Bfr
  • 1
  • 1
  • When jersey will instantiate your class with the constructor it will store in in instance. Then when you will do get instance, it will return object create by jersey instead of create a new one. – Bfr Aug 24 '15 at 22:07
  • You only move a static pointer to a new object, that does not change the fact that multiple instances are being created. – Ben Aug 24 '15 at 22:09
  • If you don't call getInstance method will you have 2 instances ? – Bfr Aug 24 '15 at 22:11
  • Ok I thought one was created by jersey (reflection) and the other one by the call of getInstance method. – Bfr Aug 24 '15 at 22:18
  • That's right, but your fix does not change the fact that multiple instances are being created. – Ben Aug 24 '15 at 22:20
  • With my fix, only one will be created. (the one by jersey) then when getInstance will be called it will return the one create by reflection instead of creating a new one. – Bfr Aug 24 '15 at 22:22
  • Only if jersey calls constructor **before** the instance getter does. It does not. – Ben Aug 24 '15 at 22:28
  • Can you inject your MyContext in your ApplicationConfig and add it in your singletons ? Instead of using getInstance – Bfr Aug 24 '15 at 22:32
  • You need that in application config in order to be able to use dependency ijection – Ben Aug 24 '15 at 22:34
  • Isn't because you put a `@Path` and `@Singleton` then ? – Bfr Aug 24 '15 at 22:39