Reputation: 1859
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
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
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