Issue
I am trying to inject a client depended object inside a statless object. I did manage to do it, but I am not really sure how and why it works because I don't fully understand JavaEE dependency injection . I have 3 relevant classes
- BookmarkRepository - fetching the data from the db
- RequestData - all the relevant info for the user request that I might need
- BookmarkEndpoint - REST endpoint
import java.util.UUID;
import javax.enterprise.context.RequestScoped;
@RequestScoped
public class RequestData {
private final UUID correlationId;
public RequestData() {
correlationId = UUID.randomUUID();
}
public UUID getCorrelationId() {
return correlationId;
}
}
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Stateless
public class BookmarkRepository {
@PersistenceContext
private EntityManager em;
@Inject
RequestData requestData;
@Override
public String test() {
StringBuilder sb = new StringBuilder();
String objectid = Integer.toString(System.identityHashCode(this));
String correlationId = requestData.getCorrelationId().toString();
sb.append(correlationId);
sb.append(" OBJECT ID: " + objectid);
return sb.toString();
}
}
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("bookmarks")
public class BookmarkEndpoint {
@Inject
private BookmarkRepository bookmarkRepository;
@Inject
private RequestData requestData;
@GET
@Path("/test")
@Produces(MediaType.TEXT_PLAIN)
public Response testEndpoint() {
String outerCorelationId = requestData.getCorrelationId().toString();
sb.append(outerCorelationId);
String innerCorelationId1 = bookmarkRepository.test();
sb.append("\n" + innerCorelationId1);
String innerCorelationId2 = bookmarkRepository.test();
sb.append("\n" + innerCorelationId2);
return ResponseBuilder.createOkResponse(Response.Status.OK, finalMessage);
}
}
Output when I call the program with 2 HTTP calls
FIRST HTTP CALL
ba4ac8b9-8e3b-4632-9c5e-89fe9b6f2cc5
ba4ac8b9-8e3b-4632-9c5e-89fe9b6f2cc5 OBJECT ID: 717717270
ba4ac8b9-8e3b-4632-9c5e-89fe9b6f2cc5 OBJECT ID: 717717270
SECOND HTTP CALL
9f563047-3d8f-48f5-849d-9a0bf1df46ae
9f563047-3d8f-48f5-849d-9a0bf1df46ae OBJECT ID: 717717270
9f563047-3d8f-48f5-849d-9a0bf1df46ae OBJECT ID: 717717270
My questions:
- Does @Inject trigger each time a method from BookmarkEndpoint is called? I though @Inject is only triggered when a object is first being created, but as we can see in the output it seems that we are dealing with the same object and each time it's method is called it is being injected with a new RequestData object
- Is it possible that two HTTP calls get the same @Statless object during the same time and then the RequestData of one client will be squashed because of the other client RequestData
- When is @Statless created and when it is destoyed? I have seen many topic where it is said "when it is needed it is created" and "when it is not needed it is destroyed". What does that really mean. If we trust my output then my @Statless object is always in memory
- Should BookmarkRepository really be @Statless because I obviously have a state that is unique to each client
Solution
On very important point your questions are missing:
CDI Injects a proxy, which is like a router. It does not inject the actual object. If you put a breakpoint in getCorrelationId()
and take a look at the stack trace, you'll see there's a stack frame where a CDI Proxy is called at some point.
Why is this important?
This allows for the creating programs where a RequestScoped bean can be injected with a SessionScoped bean, and vice-versa. The container routes you to the correct instance of the class when the function on the the proxy is called. It literally looks it up and sends the function call to the right place!
So to get to your questions:
Does @Inject trigger each time a method from BookmarkEndpoint is called?
Neither. @Inject
is just an annotation. BookmarkEndpoint
is injected with a proxy of BookmarkRepository
that is a sublcass of BookmarkRepository
, and the CDI Framework defines this subclass at runtime. BookmarkRepository
is a stateless EJB, so it will have a pool of instances. The proxy will pick an instance out of the pool and invoke the method on it. Since you don't have a CDI scope on BookmarkEndpoint
, I believe it will be re-created every request (don't quote me on that, use the debugger). We generally slap @ApplicationScoped
on all of our JAX-RS beans as an optimization.
Is it possible that two HTTP calls get the same @Statless object during the same time and then the RequestData of one client will be squashed because of the other client RequestData
No. Instead, Stateless beans are pooled. If there is a bean available in the pool, two requests will be handled at once. If there is no bean available, the request blocks until an instance becomes available. Pool sizes and timeouts are tunable and are a container specific setting.
When is @Statless created and when it is destoyed?
Short story: The pool of beans is created at boot time and destroyed at shutdown, unless you throw an exception out of the bean, than that instance is destroyed. See here for the full explanation: https://docs.oracle.com/javaee/6/tutorial/doc/giplj.html
Should BookmarkRepository really be @Statless
@Stateless
will absolutely work, but it's not necessary: you don't really need a pool for anything. @Singleton @Lock(LockType.Read)
would be more appropriate. What would be best is @ApplicationScoped @Transactional(TxType.Required)
since that avoids EJBs altogether
because I obviously have a state that is unique to each client
You do... but not in the way you're thinking! Remember the following is a proxy:
@Inject
RequestData requestData;
Its going to route you to an instance of the bean that is bound to the current request. Your "state" is being held by the container.
Answered By - Jonathan S. Fisher
Answer Checked By - Dawn Plyler (JavaFixing Volunteer)