I'm using a custom interceptor which creates a new db connection, and sets this connection onto the current action before executing the action. After that, the interceptor closes the connection.
I'm looking for a convenient way to share this db connection with other classes / static methods (such as Models) that are used by the action. E.g so I can call static method like User.get( id ) or User.getEmail( id ) without having to pass the db connection to each method separately.
I could set the db connection onto the ActionContext from the interceptor, by doing:
ServletActionContext.getActionContext().put("db", db );
And then I could access this db connection from a static method, such as:
public class User implements Model
{
public static String getEmail(int id)
{
Connection db =
(Connection) ServletActionContext.getActionContext().get("db");
//...
}
}
My question is, would a new ActionContext be generated for every given request, so I can be sure that a new db connection will be used each time? E.g if I have 500 people visiting mysite.com/fooAction, could I be sure that each of those 500 requests is generating a unique ActionContext, and each call to User.getEmail() would access only the db connection which is unique to the given request?
Thanks.
To answer the question :
My question is, would a new ActionContext be generated for every given
request, so I can be sure that a new db connection will be used each
time?
Is yes. Reference is the java-doc. It similar to the one provided by Alfredo Osorio only it refers to 2.3.x version.
Can you say what struts2 version is being used?
I was not able to find any version that uses
ServletActionContext.getActionContext()
but instead the signature is
ServletActionContext.getActionContext(javax.servlet.http.HttpServletRequest)
To answer the comment regarding thread-local being static and still the ActionContext instance being unique per request its because the doing a
ActionContext.getContext()
internally invokes a get on the thread local instance.
actionContext.get()
You may find the following post helpful in this regard.
However to delve deeper, the method
ServletActionContext.getActionContext(javax.servlet.http.HttpServletRequest)
takes a different route than using the thread-local.
public static ActionContext getActionContext(HttpServletRequest req) {
ValueStack vs = getValueStack(req);
if (vs != null) {
return new ActionContext(vs.getContext());
} else {
return null;
}
}
public static ValueStack getValueStack(HttpServletRequest req) {
return (ValueStack) req.getAttribute(STRUTS_VALUESTACK_KEY);
}
getActionContext
getValueStack
Below are some additional references (source code).
ValueStack
OgnlValueStack
ActionContext
The following posts may also be helpful.
will-a-new-actioncontext-and-valuestack-be-created-for-every-new-action-object
struts2-actioncontext-and-valuestack
Update 2 :
Wanted to add as mentioned here (Link 1 above) that in case of ActionChaining being involved, the action is invoked with its own interceptor stack and result.
The thread in which its executed, however is the same.
The value-stack and parameters are copied over. See - ActionChainResult#execute(ActionInvocation).
Once the chain-invocation is complete, the state of the action-context is reset. (See DefaultActionProxy#execute() ).
Partial Information : Although the action-invocation is set in DefaultActionInvocation#init(ActionProxy) I was not able to determine if or where it is reset.
Sources :
DefaultActionInvocation
DefaultActionProxy
DefaultActionProxyFactory
ActionChainResult
FilterDispatcher(Deprecated)
My question is, would a new ActionContext be generated for every given
request, so I can be sure that a new db connection will be used each
time?
Since ActionContext uses ThreadLocal it is thread safe. Struts 2 creates an ActionContext for each request, and each request has its own thread. So yes, if you create a new connection and store it in the ActionContext every thread will have its own connection. But I don't recommend you to store the connection in the ActionContext because this couple you to Struts 2 which is not a good thing, also your services shouldn't be calling web specific classes because it also couple them.
From Struts 2 Javadoc:
The ActionContext is the context in which an Action is executed. Each
context is basically a container of objects an action needs for
execution like the session, parameters, locale, etc.
The ActionContext is thread local which means that values stored in
the ActionContext are unique per thread. See the
ActionContext.ActionContextThreadLocal class for more information. The
benefit of this is you don't need to worry about a user specific
action context, you just get it:
ActionContext context = ActionContext.getContext(); Finally, because
of the thread local usage you don't need to worry about making your
actions thread safe.
ActionContext excerpt:
public class ActionContext implements Serializable {
static ThreadLocal<ActionContext> actionContext = new ThreadLocal<ActionContext>();
....
}
Related
I implement AWS Lambda function with Java and face with the question - how to release used resources correctly? In my function I make different calls of some resources: execute queries to DB, make REST-calls to third-party services (send StatsD metrics, invoke Slack webhooks, etc), interact with Kinesys stream.
Not going into details, my function looks like this:
public class RequestHandler {
private StatisticsService statsService; //Collect StatsD metrics
private SlackNotificationService slackService; //Send Slack notifications
private SearchService searchService; //Interact with DB
//Simplified version of constructor
public RequestHandler() {
this.statsService = new StatisticsService();
this.slackService = new SlackNotificationService();
this.searchService = new SearchService();
}
public LambdaResponse handleRequest(LambdaRequest request, Context context) {
/**
* Main method of function
* where business-logic is executed
* and all mentioned services are invoked
*/
}
}
And my main question is - where is more correctly release resources which are used in my services, in the end of handleRequest() method (in such case I'll need to open them all again in each next invocation of Lambda-function) or in finalize() method of RequestHandler class?
According to Lambda best practices you should :
Keep alive and reuse connections (HTTP, database, etc.) that were
established during a previous invocation.
So your current code is right.
Regarding the finalize() function, I don't think it is relevant. Lambda execution context will be deleted at some point freeing automatically every open resources.
https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html#function-code
I had the problem, that every time i retrieved a collection from the gwt request factory, there was the "findEntity()"-method called for every entity in that collection. And this "findEntity()"-method calls the SQL-Database.
I found out that this happens because request factory checks the "liveness" of every entity in the "ServiceLayerDecorator.isLive()"-method (also described here: requestfactory and findEntity method in GWT)
So i provided my own RequestFactoryServlet:
public class MyCustomRequestFactoryServlet extends RequestFactoryServlet {
public MyCustomRequestFactoryServlet() {
super(new DefaultExceptionHandler(), new MyCustomServiceLayerDecorator());
}
}
And my own ServiceLayerDecorator:
public class MyCustomServiceLayerDecorator extends ServiceLayerDecorator {
/**
* This check does normally a lookup against the db for every element in a collection
* -> Therefore overridden
*/
#Override
public boolean isLive(Object domainObject) {
return true;
}
}
This works so far and I don't get this massive amount of queries against the database.
Now I am wondering if I will get some other issues with that? Or is there a better way to solve this?
RequestFactory expects a session-per-request pattern with the session guaranteeing a single instance per entity (i.e. using a cache).
The proper fix is to have isLive hit that cache, not the database. If you use JPA or JDO, they should do that for you for free. What matters is what "the request" thinks about it (if you issued a delete request, isLive should return false), not really what's exactly stored in the DB, taking into account what other users could have done concurrently.
That being said, isLive is only used for driving EntityProxyChange events on the client side, so if you don't use them, it shouldn't cause any problem unconditionally returning true like you do.
Background
The Apache Action class is not thread-safe. However, this was only realized after implementing a base class, upon which all other classes in the system depend. The base class uses a number of instance variables:
private HttpServletRequest request;
private ArrayList inputParams = new ArrayList();
private Connection connection;
private String outputParameter;
private boolean exportEnabled;
Fortunately, all usages of those variables are accomplished through accessor methods, exclusively. For example:
public boolean getExportEnabled() {
return this.exportEnabled;
}
public void setExportEnabled( boolean exportEnabled ) {
this.exportEnabled = exportEnabled;
}
Problem
The base class is running in a multi-threaded Servlet environment.
Solution #1
To resolve this issue, I was thinking about using a HashMap keyed to the session. However, this would require re-writing all of the methods, and dependent code:
private static HashMap sessionVariables = new HashMap();
public boolean getExportEnabled( HttpSession session ) {
return getSessionVariables().get( session.getId() + '.exportEnabled' );
}
public void setExportEnabled( boolean exportEnabled, HttpSession session ) {
getSessionVariables().put( session.getId() + '.exportEnabled', exportEnabled );
}
That is a lot of work, and would likely introduce bugs.
Solution #2
It might be possible to change the base class to an "empty" class. This empty class would have a single method:
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response )
throws Exception {
// Instantiate "this" and forward the request?
}
But it would have to know the appropriate base class to instantiate, or perhaps instantiate a new version of itself to handle the call.
Update #1
I believe the Struts architecture does the following:
Create an instance of the Action subclass.
Re-use that same instance for every request.
Obtain a thread (from a thread pool) when receiving a new connection.
Call execute on the Action subclass from the thread.
Handle multiple new connections using different threads.
The same execute method will be called on the same instance of the object, resulting in unsafe behaviour because the subclass has instance variables.
Update #2
The following solution seems to solve the issue:
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response ) throws Exception {
((MyClass)clone()).executeClone( mapping, form, request, response );
}
public ActionForward executeClone(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response ) throws Exception {
// Former "execute" method code goes here.
// ...
}
The original execute method was renamed to executeClone. The new execute implementation creates a clone of the current class and subsequently calls executeClone. This minimally invasive technique avoids introducing new bugs while making the class thread-safe.
Question
What would be the most reliable way to make the code thread-safe while minimizing the risk of introducing bugs?
Thank you!
Solution #1 is dangerous because it assumes that the session is thread-safe, which is not necessarily the case. Someone could be making two simultaneous requests with the same session.
Solution #2 could be easily implemented by making your base class implement Cloneable. Then it can clone itself, set the clone's instance variables, and then call super.execute(). If you think it's too hard to change the design to make your base class be properly thread-safe, this might be an easy way out.
What would be the most reliable way to make the code thread-safe while minimizing the risk of introducing bugs?
There is no general answer to this. What you need to do to make a class thread-safe depends on what the class does, its API design ... and what level of thread-safety you require. In some cases, it is not even practical to make something thread-safe; e.g. read the javadoc for Collections.synchonizedList and look about how it handles the iterator() method.
I have written a web-service application that has in a main class generated random value per request (for logging).
I cannot set it as a static field because next request will override it.
I also cannot pass it to the every class that I use in the main one (as an argument or with setter).
Is it possible to create some semi-static field - visible for one request but not for every other that go to the web-service ?
You can safely assume that, in the Java EE model, each single request is served by a single thread and that there is no contention by concurrent requests.
Having said that, you can employ a Singleton using a ThreadLocal, let the Servlet populate the value and have the underlying classes access the sigleton without having notion of the threads or the HTTP request context:
public class RandomValueHolder {
private static ThreadLocal<Long> randomValue;
public static Long getRandomValue() {
return randomValue.get();
}
public static void setRandomValue(Long value) {
randomValue = new ThreadLocal<Long>();
randomValue.set(value);
}
}
Why not use HttpRequest and store the value as attribute
Save the data in the request itself with Request.setAttribute() and use the corresponding Request.getAttribute() to retrieve it.
I am using GWT 2.4 with the editor and request factory frameworks. I have a model, Trip, which has an Address 'origin' and an Address 'destination'. When creating a Trip via the UI, the two addresses are created automatically and assigned to the Trip. User fills out details and saves. For some reason, I am getting the 'autobean frozen error' when trying to persist to the server. This code worked in GWT 2.3 and I cant switch back. I am hoping its not a bug in GWT 2.4. Here is some sample code of what I am doing:
RequestContext request = requestFactory.request();
TripProxy trip = request.create(TripProxy.class);
trip.setOrigin(request.create(AddressProxy.class));
trip.setDestination(request.create(AddressProxy.class));
driver.edit(trip, request);
this.trip = trip;
// … on save button clicked (different method)
RequestContext request = driver.flush();
request.save(trip).with(driver.getPaths()).fire(someReceiverImpl);
Results in:
java.lang.IllegalStateException: The AutoBean has been frozen
at com.google.web.bindery.autobean.shared.impl.AbstractAutoBean.checkFrozen(AbstractAutoBean.java:195)
at com.google.web.bindery.autobean.shared.impl.AbstractAutoBean.setProperty(AbstractAutoBean.java:270)
at sun.reflect.GeneratedMethodAccessor53.invoke(Unknown Source)
The call to fire completes successfully but somewhere from within requestfactory, the above error is thrown. Curiously, the entity is saved on the server however, validations are not enforced. When I simplify the model and remove the Address associations, the validation and save works. My main issue is the autobean frozen error; the validation stuff is secondary.
EDIT: On further investigation I found that the entities are making it to the server okay and persisting as expected. Its upon return that the above exception is thrown. AddressProxy is a ValueProxy and it looks like RF doesnt like Trip coming back with these associations. Returning null 'fixes' the problem but this obviously wont work long term.
I know this is a lot more than you're asking for, but these 3 tips have helped me out (from here):
Trying to edit locked entity.
If an entity is frozen ( locked for changes) you cannot:
change its properties
use it in requestContext method calls.
If you try to do it, you will receive the exception : java.lang.IllegalStateException: The AutoBean has been frozen.
When entity may be frozen?
every entity returned as a response is frozen
every entity which has been used in requestContext call will be frozen.
In first situation solution is easy – you just have to unlock given entity. In order to do that you must use instance of your RequestContext class and call edit() method.
StudentRequest req1 = requestFactory.studentRequest();
StudentProxy s2 = req1.edit(s1);
In second situation you should not use given entity any more, It cannot be edited because it has already a requestContext assigned. If you want to change it you must retrieve instance of this entity from server again and follow instructions for point a).
Trying to call requestContext.edit() on entity which already has a requestContext assigned.
If you have retrieved the entity from the server or created a new one, and afterwords you are trying to use ANOTHER RequestContext to edit it e.g. in this way:
StudentRequest req = requestFactory.studentRequest();
s1 = req.create(StudentProxy.class);
// s1 is connected with "req" and one context is just enough for it
StudentRequest reqZZZ = requestFactory.studentRequest();
reqZZZ.edit(s1); // you cannot do it - here exception will be thrown
you will surely recieve an exception:
java.lang.IllegalArgumentException: Attempting to edit an EntityProxy previously edited by another RequestContext
You may run into this problem in situations where you have a bean, but you have no track of request context which has created or edited the bean in some previous method call. In this situation you must save the previous requestContext somewhere, or send it along with the entity to the point of interest. The best solution may be to create some special layer which holds currently used request.
Trying to reuse a Request Context which has already been fired.
You can use a request context to create and edit many different entities (also of a different type). You can also accumulate the methods which should be fired. But what you cannot do is to try to use it twice to fire a request. If you have created a request and call the fire() method on it, you cannot do it again. If you do you will get: java.lang.IllegalStateException:A request is already in progress exception.
The solution is to simply create a new requestContext.
This was caused by not using the same EntityManager on the server.