I have an EJB application that consists of two beans, ServiceEJB
(web tier) and BusinessEJB
(business tier), where BusinessEJB
is injected in ServiceEJB
.
ServiceEJB
receives HTTP requests from the browser, calls a method in BusinessEJB
, gets the result, and sends the HTTP response.
Also, ServiceEJB
has access to the HttpSession
object, where the userId
of the user that logged in is stored. BusinessEJB
does NOT have access to the HttpSession
object.
The application needs to log messages (using sl4j/logback, for example). It could log the message in ServiceEJB
or BusinessEJB
methods, and when it logs a message, it has to include the userId
of the session in the log entry.
Since BusinessEJB
doesn't have the userId
, it needs to get it from ServiceEJB
. The question is what is the best way to achieve that. What I DON'T want to do is to add a userId
field to each method in BusinessEJB
as a parameter, as there are many ServiceEJB
s and BusinessEJB
s in the application (and other beans called by BusinessEJB
that also generate log entries), and I don't want to pollute the application with the userId
field. Instead, I could have a userId
field at the EJB level, but how to populate them? Is there a way to achieve this with annotations? Any suggestions will be welcome.
@Produces({MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_JSON})
@Stateless
public class ServiceEJB {
@Context
HttpServletRequest httpRequest;
@Inject
private BusinessEJB bean;
private String userId;
@Path("someurl")
public Response someMethod1() {
final HttpSession session = httpRequest.getSession();
// get the userId from the session
String s = bean.someMethod2();
// return Response
}
}
@Stateless
public class BusinessEJB {
private String userId;
public String someMethod2() {
// .... log an entry with userId
return "something";
}
}
A few pointers/comments:
If you integrate with application server security, then the user name is available at any component. EJBs can get it by calling getCallerPrincipal()
on the injected variant of the EJBContext
, here the javax.ejb.SessionContext
:
@Resource
private SessionContext sessionCtx;
Servlets can retrieve the principal from the HttpServletRequest.getUserPrincipal()
. JAX-RS components (the ServiceEJB
) can retrieve it from the javax.ws.rs.core.SecurityContext.getUserPrincipal()
.
Is there any reason why you are NOT integrating with the application server security?
If you have a good reason NOT to integrate with application server security, I would propose a variation of the solution from the previous answer. The variation is to set the user data from a filter applied to all resources (either servlet filter or JAX-RS ContainerRequestFilter
), so that you do not have to worry about setting it in multiple places.
If you ONLY NEED THE USER ID FOR LOGGING, I'd suggest you take a look at the concept of Mapped Diagnostic Contexts (MDC) in slf4j. With it you can set the user id early at the beginning of the request and make it available to all logging statements thereafter.