akagixxer
akagixxer

Reputation: 1859

What is the best way to inject a singleton service into a JAX-RS/Jersey resource?

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.

Upvotes: 0

Views: 2447

Answers (2)

akagixxer
akagixxer

Reputation: 1859

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}

Upvotes: 0

areus
areus

Reputation: 2947

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

Upvotes: 1

Related Questions