Accessing Spring Session scoped Proxy Beans - java

I'm developing a web-app using Struts 2 with a Spring 3 backend. I'm using Spring aop:proxy beans to handle my session beans rather than the Struts 2 SessionAware interface. Everything was working fine until I have an Action that is running under the Struts ExecAndWait interceptor. Because this interceptor in effect runs my action under a seperate thread, when I come to try and access my proxied session bean, I get a BeanCreationException/IllegalStateException. Is there another "spring way" that I can get hold of my session beans in this scenario?
Regards

From Execute and Wait Interceptor documentation
Important: Because the action will be running in a seperate thread, you can't use ActionContext because it is a ThreadLocal. This means if you need to access, for example, session data, you need to implement SessionAware rather than calling ActionContext.getSession().
The problem with session scoped-beans is that they depend on thread-local attributes set by RequestContextListener or RequestContextFilter. But the latter allows you to set very interesting threadContextInheritable flag...
If your ExecAndWait interceptor creates new thread per every request it serves, inheritable thread local should propagate session scoped beans to child threads. However if Struts uses thread pool (more likely, thou I haven't used Struts2 for ages) to serve this requests, this will have very unexpected and dangerous results. You might experiment with this flag, maybe it will do the trick.

You can Implement your own ExecAndWait interceptor using Spring. You can also delegate the management/creation of this action to Spring. For the later the details are in the S2 spring plugin documentation.

You can use ,RequestContextHolder(Holder class to expose the web request in the form of a thread-bound RequestAttributes object.) to make session scoped proxy beans available to child threads.
Define a custom ExecuteAndWait Interceptor and in doIntercept method use the following static method from RequestContextHolder
public static void setRequestAttributes(RequestAttributes attributes,boolean inheritable)
Bind the given RequestAttributes to the current thread.
Parameters:
attributes - the RequestAttributes to expose, or null to reset the thread-bound context
inheritable - whether to expose the RequestAttributes as inheritable for child threads (using an InheritableThreadLocal)
Sample Code
public class CustomExecuteAndWaitInterceptor extends ExecuteAndWaitInterceptor {
#Override
protected String doIntercept(ActionInvocation actionInvocation) throws Exception {
RequestAttributes requestAtteiAttributes = RequestContextHolder.getRequestAttributes(); //Return the RequestAttributes currently bound to the thread.
RequestContextHolder.setRequestAttributes(requestAtteiAttributes, true);
//do something else if you want ..
return super.doIntercept(actionInvocation);
}
}

Related

Request scoped bean is always available when running code outside of request scope in Spring 4

Stuck on this thing in Spring 4, probably the same will be for 5.
So, what I have:
Spring Boot 1.5 web app
Request scoped bean:
#RequestScope
#Component
public class APIAction { ... }
Code which accesses this component from threads related or not related to webrequest:
private final ObjectProvider<APIAction> apiAction;
apiAction.getIfAvailable()...
When it runs in Thread bounded to web request everything is fine. But when I invoke it from daemon thread I expect to get null, exception or something else. Instead I'm receiving some proxy object which can't be tested for null, or any kind of state indicating that bean is really available. If I'll try to invoke any bean method, I'll get exception finally saying accessing to bean outside of thread bounded to web request.
So the question is, am I using it wrong? Right now, I'm checking request scope before accessing to bean by invoking this: RequestContextHolder.getRequestAttributes() != null, which is really ugly, and I need all the time to tell people why they should use it like this.
And bonus question, is it possible actually to instantiate that bean in threads without request bound?
TL;DR: You can't use ObjectProvider.getIfAvailable() to check if in request scope.
Use if (RequestContextHolder.getRequestAttributes() != null) instead.
As the javadoc of ObjectProvider says:
A variant of ObjectFactory designed specifically for injection points, allowing for programmatic optionality and lenient not-unique handling.
For singleton beans, ObjectProvider<APIAction> is an alternative to #Autowired(required = false) List<APIAction> with methods that better represent the purpose.
For prototype beans, it allows the on-demand creation of the prototype, including optional constructor arguments.
However, it's all about the existence of the bean, i.e. about whether the bean has been registered (and how many). Any #Component (or other) annotated class is registered by the component scanning, regardless of the bean scope.
The #RequestScope bean exists, so the code could be changed to #Autowired private final APIAction apiAction;, and it would always be non-null.
The fact that the object referred to by apiAction is a proxy that will apply method calls to different instances depending the the request context is besides the point.
When you call an APIAction method, you will get an IllegalStateException saying:
No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
Internally, a #RequestScope annotated class has a Scope of type RequestScope, and the javadoc says:
Relies on a thread-bound RequestAttributes instance.
It does this by calling RequestContextHolder.currentRequestAttributes(), which throws the above exception.
Solution: To check if you are in a request context, call RequestContextHolder.getRequestAttributes() and check for null return value.

Why use session bean at all if ultimately its the HttpSession that holds the state

I am new to EJB so please don't mind anything silly in the question.
I have a doubt that someone might be able to solve hopefully.
I have the following Stateful Bean:
#Stateful
public class SessionBean implements SessionBeanRemote {
private int count = 0;
#Override
public int getCount(){
count++;
return count;
}
}
And this is the client that invokes the Bean (Servlet)
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
InitialContext ctx;
HttpSession session = null;
SessionBeanRemote obj;
try {
if (session.getAttribute("myBean") == null) {
ctx = new InitialContext();
obj = (SessionBeanRemote) ctx.lookup("SessionBean/remote");
session.setAttribute("myBean", obj);
} else {
obj = (SessionBeanRemote) session.getAttribute("myBean");
}
System.out.println(obj.getCount());
} catch (NamingException ex) {
Logger.getLogger(TestServlet.class.getName()).log(Level.SEVERE, null, ex);
}
}
Now I was wondering if it is ultimately HttpSession that has to hold the session bean then why use EJB at all, why not just store whatever we want to in the session directly rather than having it in the session bean first and then storing that bean in the session.
And also I was wondering if lets say I change my #Stateful annotation to #Stateless and then execute the same code on the client side and store the bean in the session then also I can extract the same bean from the session then what is the difference between stateless and stateful, I know that when new lookup is done there is a chance that same stateless bean might be returned to me where as with stateful bean it is always new when we do a lookup. But is that it?
P.S. As I mentioned earlier, I am new to EJB and all the doubts are based on what I have understood from a few tutorials online and some questions on SO. I have also tried running it locally but unfortunately I am not able to deploy the application on the GlassFish because of following error "Exception while loading the app : EJB Container initialization error". I am trying to look into it.
They're two unrelated concepts.
If you seperate concerns, and you should. Then the HTTP session and EJB session(s) operate at logically distinct layers. The http session is for holding an individual web browser and user's state. An EJB session is used for holding transactional, scalable and fault-tolerant and "transparently" (and possibly remote) reference(s) within the context of an Enterprise application client.
The fact that you're using EJB(s) to serve web content, does not mean you cannot also use those same EJB(s) to serve JFC/Swing (or JavaFX) clients.
I'll try to give you a simplified answer.
Yes, you have to store reference to the SFSB somewhere, in case of web application in http session, but as Elliott wrote you may have different client types also.
Some benefits of Session bean vs POJO:
container transaction management
container security enforcement
possible remote access via remote interface
allows to be deployed as separate module (EJB) for potentially better reusability.
If your session bean relies on state, then it is more logical to hold the state in the bean rather than pass all state info in each method call.
Your example is extremely simple, you don't use transactions, persistence, security, so there is no point of using SFSB or even EJB at all.
SFSB are considered to be rather heavyweight, in general should rather be avoided and I'd say majority of web applications don't use them (depends on application requirements really). So you should design your services to be stateless and rather use stateless beans than stateful, then you will have better performance and easier reusability.
So if you plan to use some of the features I provided, you may benefit from EJBs, otherwise you maybe happy with just Http session and business logic in POJOs.
EJB Sessions
A session in EJB is maintained using the SessionBeans over the server's JVM. You design beans that can contain business logic or calculations or dynamic pages, and that can be used by the clients. You have two different session beans: Stateful and Stateless.
Stateful: is somehow connected with a single client (each bean's object). It maintains the state for that client, can be used only by that client and when the client "dies" then the session bean is "lost". Stateful bean's life-cycle is bind with the client. (very useful in role-based applications/systems)
Stateless: A Stateless Session Bean doesn't maintain any state and there is no guarantee that the same client will use the same stateless bean, even for two calls one after the other. The lifecycle of a Stateless Session EJB is slightly different from the one of a Stateful Session EJB. Is EJB Container's responsability to take care of knowing exactly how to track each session and redirect the request from a client to the correct instance of a Session Bean and to same job for every one.
Where:
HTTPSession: It is acquired through the request object. You cannot really instantiate a new HttpSession object, and it doesn't contains any business logic or calculation, but is more of a place where to store objects (for client as output or server as input), and for communication of two or more system over the network.

Injecting using request scope and threads

I have some bussines classes that get injected some dependencies that are provided using servlet request scope.
The problem is that I want to use that bussines classes in some threads that outlive the servlet request.
Whats the most transparent way to do that?
If you are using Spring (which, by the terms you use to describe your problem, you seem to be using), you can use an AOP scoped-proxy for your request-scoped object, and inject this proxy into the objects that outlive the servlet request. The scoped-proxy will take care of using the right instance everytime you access it.
Well,
I'm not sure if I get your problem. I think there is problem with architecture itself, but however this could help you:
Guice module
bind(Bussines.class).annotatedWith(Names.named("request")).to(Bussines.class).in(RequestScoped.class);
bind(Bussines.class).annotatedWith(Names.named("session")).to(Bussines.class).in(SessionScoped.class);
bind(Bussines.class).annotatedWith(Names.named("application")).to(Bussines.class).asEagerSingleton();
Usage
#Inject #Named("request")
private Bussines bussines; //inject a new bussines class every request
#Inject #Named("session")
private Bussines bussines; //inject a new bussines class each session
//This is little bit tricky, cuz Bussines is stored in session. In Stage.PRODUCTION are all injection created eagerly and there is no session at injection time. Session binding should be done in lazy way - inject provider and call bussinesProvider.get() when em is needed;
#Inject #Named("application")
private Bussines bussines; //inject singleton
Also you can use Private modules to bind different scoped objects to a class. Don't forget to expose it.
I see 3 options:
You could add the information you need to an object that has a larger scope like application or session scope
You could persist the information in a file or the database and look it up later
You could save the information on the thread or use a thread scope: http://code.google.com/p/google-guice/issues/detail?id=114
I may not suggest you directly use or inject HttpServletRequest at business bean which is not in request scope . because this will break up app layers. if you want the value from the request or request header, then you can pass value to object to pass to the business layer, because otherwise, it is not secure, and generally, app will apply some security interceptor at that request scope , but if you directly inject to other layer , then it jump over and the interceptor may be skipped... this way also break the encapsulation and obvious is anti-pattern.

Correct usage of Stateful Beans with Servlets

We currently have a Stateful bean that is injected into a Servlet. The problem is that sometimes we get a Caused by: javax.ejb.ConcurrentAccessException: SessionBean is executing another request. [session-key: 7d90c02200a81f-752fe1cd-1] when executing a method on the stateful bean.
public class NewServlet extends HttpServlet {
#EJB
private ReportLocal reportBean;
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
String[] parameters = fetchParameters(request);
out.write(reportBean.constructReport(parameters));
} finally {
out.close();
}
}
}
In the above code, constructReport will check if it needs to open a new connection to the database specified in the Report after which a Report in HTML is constructed from a query which is built from the parameters specified.
The reason why we chose to use a stateful bean over a stateless bean was because we need to open a database connection to an unknown database and perform queries on it. With a stateless bean it seems terribly inefficient to repeatedly open and close database connections with each injected instance of the bean.
A few more details regarding the ConcurrentAccessException: as per the EJB spec, access to SLSB is synchronized by the app. server. However, this is not the case with SFSB. The burden of making sure that the SFSB is not accessed concurrently is on the application developer's shoulders.
Why? Well, synchronization of SLSB is only necessary at the instance-level. That is, each particular instance of the SLSB is synchronized, but you may have multiple instances in a pool or on different node in a cluster, and concurrent requests on different instances is not a problem. This is unfortunately not so easy with SFSB because of passivation/activation of instances and replication across the cluster. This is why the spec doesn't enforce this. Have a look at this dicussion if you are interested in the topic.
This means that using SFSB from servlet is complicated. A user with multiple windows from the same session, or reloading page before the rendering finished can lead to concurrent access. Each access to the EJB that is done in a servlet needs theoretically to be synchronized on the bean itself. What I did was to to create an InvocationHandler to synchronize all invocations on the particular EJB instance:
public class SynchronizationHandler implements InvocationHandler {
private Object target; // the EJB
public SynchronizationHandler( Object bean )
{
target = bean;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
synchronized( target )
{
// invoke method to the target EJB
}
}
}
Then, right after you obtain the remote reference to the EJB, you wrap it with the SynchronizationHandler. This way you are sure that this particular instance will not be accessed concurrently from your app (as long as it runs in only one JVM). You can also write a regular wrapper class which synchronizes all the methods of the bean.
My conclusion is nevertheless: use SLSB whenever possible.
EDIT:
This answer reflects the EJB 3.0 specs (section 4.3.13):
Clients are not allowed to make concurrent calls to a stateful session
object. If a client-invoked business method is in progress on an
instance when another client-invoked call, from the same or different
client, arrives at the same instance of a stateful session bean class,
if the second client is a client of the bean’s business interface, the
concurrent invocation may result in the second client receiving the
javax.ejb.ConcurrentAccessException
Such restrictions have been removed in EJB 3.1 (section 4.3.13):
By default, clients are allowed to make concurrent calls to a stateful
session object and the container is required to serialize such
concurrent requests.
[...]
The Bean Developer may optionally specify that concurrent client
requests to a stateful session bean are prohibited. This is done using
the #AccessTimeout annotation or access-timeout deployment descriptor
element with a value of 0. In this case, if a client-invoked business
method is in progress on an instance when another client-invoked call,
from the same or different client, arrives at the same instance of a
stateful session bean, if the second client is a client of the bean’s
business interface or no-interface view, the concurrent invocation
must result in the second client receiving a
javax.ejb.ConcurrentAccessException
This is not what stateful session beans (SFSB) are intended to be used for. They are designed to hold conversation state, and are to be bound to the user's http session to hold that state, like a heavyweight alternative to storing state in the session directly.
If you want to hold things like database connections, then there are better ways to go about it.
Best option is to use a connection pool. You should always use a connection pool, and if you're running inside an application server (which, if you're using EJBs, then you are), then you can easily use your appserver's datasource configuration to create a connection pool, and use that inside your stateless session bean (SLSB).
Until you provide some code and the stacktrace, I'd suggest that you consider using a connection pool.
If by "unknown database" you mean a database whose parameters are supplied by the end user, and hence no preconfigured connection pool is possible, you can still use the connection pool concept, rather than opening a new connection each time.
Also, theck this thread.
Session beans cannot be used concurrently, like skaffman said they were meant to handle state corresponding to the client session and are typically stored in the session object per client.
Refactoring to use a database pool to handle concurrent requests to your resources is the way to go.
In the meantime, if all you need is this trivial use, you could synchronise the call to constructReport as in:
synchronised (reportBean) {
out.write(reportBean.constructReport(parameters));
}
Note that this is no solution if constructReport takes a significant amount of time relative to your number of clients.
you should never syncronize servlet or ejb access since this cause requests queue and if you have N concurrently users the last one will wait for a long time and often get a timeout response!!! Syncronize method is not intended for this reason!!!

How can I get HttpServletRequest when in an HttpSessionListener?

How can I access request headers from a SessionListener?
I need to set a timeout on the current session when it is created. The timeout needs to vary based on a header in the HttpServletRequest. I already have a SessionListener (implements HttpSessionListener) that logs the creation and destruction of new sessions, and it seems to be the most logical place to set the timeout.
I've tried the following, but it always sets ctx to null.
FacesContext ctx = FacesContext.getCurrentInstance();
The HttpSessionListener does not have access to the request because it is invoked when no request has been made—to notify of session destruction.
So, a Filter or Servlet would be better places to examine the request and specify the session timeout.
FacesContext ctx = FacesContext.getCurrentInstance();
JSF contexts are per-request and thread-local. So, this method call will probably return null outside the JSF controller invocations (e.g. FacesServlet.service) - so, other threads and any requests that don't pass through the Faces servlet mapping.
It is technically possible to set this time-out using a JSF mechanism - you could use a phase listener to check for a session after RENDER RESPONSE, though you would still have to cast to the servlet API to set the time-out. The advantage of phase listeners is that they can be registered either globally in faces-config (see spec) or for specific views. A global phase listener defined in a JAR with a META-INF/faces-config.xml can be dropped into multiple WARs, allowing you to easily reuse the functionality.
(You could also override how the session is provisioned to JSF, but the amount of work is excessive.)
For a one-off, erickson's suggestion of a Filter is really straightforward.
You can't ( see the API ). The request allows you to access the session, but not the other way around.
You might even have concurrent requests for the same session, so this is not feasible.

Categories

Resources