Tomcat JSF Session BEan #Predestroy "Session already invalidated" - session

So I try to access an attribute of my HttpSession on my #PreDestroy method on a #SessionScoped JSF managed bean using
session.getAttribute("myAttribute");
But I get a
java.lang.IllegalStateException: getAttribute: Session has already been invalidated
Why?
I need to access the list of connections to external services opened by that session before one of my session beans is destroyed, and they are of course stored on a session attribute object.
How can I do that?

Explicitly accessing a session attribute in a session scoped managed bean doesn't make sense. Just make that attribute a property of the session scoped managed bean itself.
#SessionScoped
public class YourSessionScopedBean implements Serializable {
private Object yourAttribute; // It becomes a session attribute already.
#PreDestroy
public void destroy() {
// Just access yourAttribute directly, no need to do it the hard way.
}
}
The exception you faced occurred because the session was explicitly invalidated via a HttpSession#invalidate() call instead of "just" expired.

Related

Why is injecting a Stateful Session bean in JSF/ CDI managed beans is considered bad?

Pascal Thivent mentions here that -
If you are using SFSB, then you must avoid injecting them into classes
that are multithreaded in nature, such as Servlets and JSF managed
beans (you don't want it to be shared by all clients).
Moving swiftly on, BalusC also put forwards the same thing here-, but indirectly.
....use SFSB only when you want a real stateful session bean
Consider a Session Scoped Managed bean-
#SessionScoped
public class Bean{
#EJB
EjbBean ejbBean;
}
with
#Stateful
public class EjbBean{
}
But the above SessionScoped bean will be linked to one client only and as such will have state/ instance variables different from other session scoped bean. Subsequently, any stateful EJB bean will not be shared by other clients.
Please suggest on what the author implies when he says-
you don't want it to be shared by all clients
I do perfectly understand the difference b/w HttpSession & the session word in Stateless Session Bean.

What are the techniques to manage "session" or invocation context for Stateless EJBs during Remote calls?

I am writing an application that uses RMI to invoke EJBs. The EJBs are Stateless; the business requirements do not require conversational state with the client.
One of the parameters to the EJB method calls is a "User" object used to determine if the user associated with the call has permission to perform the action. We are not using container-managed auth-auth: the User object is just a POJO provided by the remote client.
I would like to make this User object globally available/injectable within the "session" or invocation context. I am aware that Stateless EJBs do not have a "session" in the EJB sense; what I mean by "session" is "the current invocation". For example, say we have only one remote EJB with two methods:
myStatelessEjb.add(Thing, User)
myStatelessEjb.update(Thing, User)
These methods call many more methods: there are other EJBs involved, Bean Validators, etc. Rather than passing-around the User object everywhere, I would like to make the User object globally available/injectable with the context of the current remote EJB invocation.
I could of course just pass around the User object or encapsulate it with "Thing", but I thought maybe it would be a better design to not "pollute" my objects and APIs with User since it's a cross-cutting concern.
Notes (for emphasis):
I am using RMI.
There is no HTTP session, because I am using RMI.
I am not using container-managed auth-auth.
I am using container-managed transactions.
Is there a standard technique suitable for this problem? e.g. Perhaps the remote client should invoke a Stateful EJB which will hold the User, or maybe ThreadLocal is appropriate, or maybe I can hook onto the container-managed transaction, or maybe there's already an applicable session/context I'm unaware of.
Easiest way would be to store the user in a #RequestScoped CDI bean and inject that as required:
#RequestScoped
public class RequestUser {
private User user;
//getter and setter for user
}
#Remote
#Statless
public class MyRemoteInterface {
#Inject
private RequestUser requestUser;
...
public void foo(User user, Bar bar) {
request.setUser(user);
...
}
}
#Stateless
public class OtherEJB() {
#Inject
private RequestUser user;
public void doBar(Bar bar) {
User user = user.getUser();
...
}
}
While #SessionScoped is useful for HTTP sessions only, #RequestScoped has broader applicability:
public #interface RequestScoped
Specifies that a bean is request scoped.
The request scope is active:
during the service() method of any servlet in the web application, during the doFilter() method of any servlet filter and when the
container calls any ServletRequestListener or AsyncListener,
during any Java EE web service invocation,
during any remote method invocation of any EJB, during any asynchronous method invocation of any EJB, during any call to an EJB
timeout method and during message delivery to any EJB message-driven
bean, and
during any message delivery to a MessageListener for a JMS topic or queue obtained from the Java EE component environment.

Accessing a SessionScoped bean from another SessionScoped bean more than once in same session creates a new instance

I am experiencing a behavior in my application I do not understand. I have a servlet (Servlet1) that accepts a request, invalidates the current session, creates a new session, and then redirects on the request to a JSF page which references a #Named #SessionScoped bean (Bean1). Both the servlet and the bean #Inject another #SessionScoped bean (SharedBean). The first time I visit my servlet URL everything works correctly. However, the second time I visit the URL the #Named #SessionScoped bean is creating a new instance of sharedbean.
So what I would like to happen is this:
Servlet1 creates a new session
Servlet1 initializes SharedBean
Bean1 access SharedBean
This is happening correctly the first time I access my servlet URL but what is happening on the second invocation is this:
Servlet1 creates a new session
Servlet1 initializes SharedBean
Bean1 creates a new SharedBean
This doesn't seem like the correct behavior to me. I know I am missing something here and I would appreciate someone explaining it to me. Code is below:
Servlet1:
#WebServlet
public class Servlet1 extends HttpServlet
{
#Inject
private SharedBean sbean;
public doGet(HttpServeltRequest request, HttpServletResponse response)
{
HttpSession session = request.getSession();
session.invalidate();
session = request.getSession(true);
this.sbean.initialize();
response.sendRedirect(newURL);
}
}
Bean1:
#Named
#SessionScoped
public class Bean1 implements Serializable
{
#Inject
private SharedBean sbean;
public void actionMethod()
{
this.sbean.execute(); // New instance being created here on 2nd access!
}
}
SharedBean:
#SessionScoped
public class SharedBean implements Serializable
{
public void initialize() { /* do some work */ }
public void exeucte() { /* do some work */ }
}
Because each time the servlet is requested you invalidate the session. So the container creates a new bean or provides another instance. You have to invalidate the session after a certain period of time of inactivity of the user or some other endpoint.
SessionScoped means the bean instance is valid for multiple requests.
But you have to decide what a session means for your application.
In most cases a session is associated to the time a user logs in and logs out. So it is comprised of multiple requests.
So a session is valid for multiple requests and is the same for different servlets.
The session is managed by the servlet container (e.g Tomcat) and an instance of the session object is provided via a ServletContext instance.
You create a new session everytime your servlet is requested. That means the container just creates a ServletContext for this single request and always binds a new bean instance of your SharedBean to that context instance.
UPDATE due to comment.
In this case i would not suggest to let the container manage the bean injection.
Because you never know at what time the bean instance is created and associated with
a SharedBean instance. I think the reason is the issue that you create a new session within you servlet request method. The bean instances of the SharedBean can't be the same in this case, because the container creates an new instance of SharedBean and invalidates the shared bean instance bound to the servlet.
It would be better in this case to create the SharedBean
within the servlet, pass it to the session context as a parameter and use the
SessionContext within the bean to fetch the SharedBean instance.
UPDATE due to COMMENT
The injected beans are managed by the container. Means the container is responsible for creating and destroying the bean. If you create a new instance of the bean the reference (address) or bean instance is decoupled from the servlet container and not managed anymore by the container. So the shared bean reference is not available for your managed bean Bean1
UPDATE due to COMMENT
The answer to the problem is that the instance of the SharedBean which you create within the doGet method of the Servlet method is not communicated to the servlet container and therefor not available for the managed bean Bean1.

How does #SessionScoped work with EJB? Is CDI only for web-tier?

How is the session defined in #SessionScoped CDI bean?
Is this annotation valid only when called from Servlet container, where the session is well defined in form of HttpSession?
If not, than how an EJB with #Inject #SessionScoped MyBean myBean can know what the session really is? I mean, methods of this EJB could have been invoked by a standalone client, RESTful WS or by some other view.
What should happen in such case? Should the annotation have no meaning, should it inject fresh MyBean instance for each request or maybe it should retain the same instance across all requests?
Taken from the #SessionScoped specification
The session scope is active:
during the service() method of any servlet in the web application,
during the doFilter() method of any servlet filter and when the
container calls any HttpSessionListener, AsyncListener or
ServletRequestListener.
So in short, yes. It is bound to the HttpSession. Also:
The session context is shared between all servlet requests that occur
in the same HTTP session. The session context is destroyed when the
HTTPSession times out, after all HttpSessionListeners have been
called, and at the very end of any request in which invalidate() was
called, after all filters and ServletRequestListeners have been
called.

Listening to when the user session is ended in a JSF managed bean

Is it possible to do something like this: When a user session starts I read a certain integral attribute from the database. As the user performs certain activities in this session, I update that variable(stored in session) & when the session ends, then I finally store that value to the DB.
My question is how do I identify using the JSF framework if the user session has ended & I should then store the value back to DB?
Apart from the HttpSessionListener, you can use a session scoped managed bean for this. You use #PostConstruct (or just the bean's constructor) and #PreDestroy annotations to hook on session creation and destroy
#ManagedBean
#SessionScoped
public class SessionManager {
#PostConstruct
public void sessionInitialized() {
// ...
}
#PreDestroy
public void sessionDestroyed() {
// ...
}
}
The only requirement is that this bean is referenced in a JSF page or as #ManagedProperty of any request scoped bean. Otherwise it won't get created. But in your case this should be no problem as you're apparently already using a session scoped managed bean, just adding a #PreDestroy method ought to be sufficient.
My question is how do I identify using
the JSF framework if the user session
has ended & I should then store the
value back to DB?
The JSF framework does not have a separate concept of a session; it uses the underlying session management features of the Servlet specification.
You would have to create a HttpSessionListener that provides hooks for you to capture the session creation and destruction events, where you can read the value and store it back into the DB.
HttpSessionListener, or if you need Dependency Injection for that save, you might use #PostConstruct & #PreDestroy. Remember that the session is destroyed when you call invalidate() or after session timeout, not when the user closes the browser. Why do you use Session Scope anyway, Conversation Scope might fit you better.

Resources