0

For example, what if several resource endpoints need access to some message bus to handle requests? Surely there is some way to register a singleton service class and inject it into the resources when the service class itself is NOT a resource but used by the resources.
All of the examples I've seen with providers or custom HK2 bindings refer to resources.

The closest thing I found to what I'm looking for was with this question:
Trouble creating a simple singleton class in Jersey 2 using built-in Jersey dependency injection

What is the best JAX-RS/Jersey way of doing this?

Note that the programmatic way would be most useful, I'm not using an xml file to configure the server.

akagixxer
  • 1,767
  • 3
  • 21
  • 35
  • 1
    If your JAX-RS resources are singletons also, then that answer is fine. Otherwise, on top of that answer, you have to deal with multithreading – Paul Samsotha Apr 18 '20 at 15:03
  • Thanks @Paul. Could you elaborate on the threading issue? Do you just mean that multiple requests will be serviced by the same resource object concurrently? – akagixxer Apr 19 '20 at 01:49
  • Yes (15 chars). – Paul Samsotha Apr 19 '20 at 04:41
  • 1
    If the resource is not a singleton, then a new one is created for each request, but there is only one instance of your injected service. Beware of any concurrency traps. If all methods are independent of each other, then you should be fine. That's all I'm saying – Paul Samsotha Apr 19 '20 at 17:18

2 Answers2

1

If your platform supports EJB, you could use the @Singleton EJB (javax.ejb package, not javax.inject), and inject it on your resources with the @EJB annotation. Singleton EJB have also outofthebox concurrency access control.

On plain Jersey, you can use CDI application context. Declare the service class with an @ApplicationScoped annotation and inject it on your resources with @Inject. CDI will only instantiate one bean.

If you cannot annotate the service class, you can create a method that provides your service implementation an annotate it with @Produces and @ApplicationScoped.

@Produces
@ApplicationScoped
public MyService produceService() {
    // instantiate your service client
}

And then use it on your resources, with:

@Inject
private MyService
areus
  • 2,880
  • 2
  • 7
  • 17
0

Answer credit goes to @areus the answer provided here.
However, I'm providing my own answer so that I can share the code.

The Service Bean

@Singleton
public final class MyServiceBean
{
  private static final AtomicInteger INSTANCES = new AtomicInteger();
  private final AtomicInteger calls = new AtomicInteger();

  public MyServiceBean()
  {
    INSTANCES.incrementAndGet();
  }

  public String getMessage()
  {
    return String.format("MyServiceBean{INSTANCES=%d, CALLED=%d}", INSTANCES.get(), calls.incrementAndGet());
  }
}

The Resource Class

@Path("/messages")
public final class MyResource
{
  @Inject
  private MyServiceBean bean;

  @GET
  @Produces(MediaType.TEXT_PLAIN)
  public Response handle()
  {
    return Response.ok(this.bean.getMessage())
      .type(MediaType.TEXT_PLAIN_TYPE)
      .build();
  }
}

HK2 Binder

public final class MyServiceBeanBinder extends AbstractBinder
{
  @Override
  protected void configure()
  {
    bind(MyServiceBean.class).to(MyServiceBean.class).in(Singleton.class);
  }
}

Then just register the binder and the resource like so:

final ResourceConfig config = new ResourceConfig();
config.register(MyResource.class);
config.register(new MyServiceBeanBinder());

Starting the server and hitting the resource multiple times yields:

MyServiceBean{INSTANCES=1, CALLED=1}   
MyServiceBean{INSTANCES=1, CALLED=2}   
MyServiceBean{INSTANCES=1, CALLED=3}   
MyServiceBean{INSTANCES=1, CALLED=4}   
MyServiceBean{INSTANCES=1, CALLED=5}
akagixxer
  • 1,767
  • 3
  • 21
  • 35
  • will this still work if i remove the singleton annotation from MyserviceBean? cause as far as i know the explicit singleton mention in the chained '.in()' method in Binder actually creates and uses a single object across requests? – JayD Jan 01 '21 at 12:32
  • 1
    Yes, it is technically redundant (although I sometimes like that it documents intent of the class). The HK2 binder call to "in(Singleton.class)" has priority over the annotation. – akagixxer Jan 04 '21 at 16:52