Struts2 ActionContext and ValueStack? - struts2

My questions are:
In Struts2, does every action object have its own corresponding ActionContext and ValueStack?
In other words, for every new request a new action object is created. Does this mean every time a new action object is created, a new ActionContext and ValueStack also get created?
Consider this scenario:
Action1------1st req------->view.jsp------2nd req--------->action2.
So when a request comes for action1 a new object of action1 and corresponding ActionContext and ValueStack will get created.
From view.jsp (upon clicking hyperlink) a new request goes for action2.
Does this mean that previous ActionContext and ValueStack (related to action1) gets destroyed and a new ActionContext and ValueStack (for action2) gets created?
Suppose I store something in ActionContext (of action1) in view.jsp and then click on hyperlink for action2 (from view.jsp), will that data along with the ActionContext (of action1) get lost?

Yes
Yes after action execution clean up will be done.
//SourceCode from StrutsPrepareAndExecuteFilter.
//Cleans up a request of thread locals
public void cleanupRequest(HttpServletRequest request) {
Integer counterVal = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
if (counterVal != null) {
counterVal -= 1;
request.setAttribute(CLEANUP_RECURSION_COUNTER, counterVal);
if (counterVal > 0 ) {
if (log.isDebugEnabled()) {
log.debug("skipping cleanup counter="+counterVal);
}
return;
}
}
// always clean up the thread request, even if an action hasn't been executed
ActionContext.setContext(null);
Dispatcher.setInstance(null);
}
3.Yes, If you want that data available in the next action use chain(not suggestible).

Q1. There is one ActionContext and there is only one ValueStack.
Q2.
Does this mean that previous ActionContext and ValueStack (related to
action1) gets destroyed and a new ActionContext and ValueStack (for
action2) gets created?
No.
Q3. I don't understand this question. What I think is missing is awareness of ThreadLocal so although there is one ActionContext each thread is able to have its own variables which are local to that thread and thus action scope for the ValueStack is maintained this way.

Related

Principal.Identity.Name in Task ran from Controller Action

In a controller action I'm running
public ActionResult MyAction()
{
Task.Factory.StartNew( () =>
{
myService.ExecuteMethod( someArgs );
} );
}
The service will call other class methods that call others, and somewhere down the line, an IPrincipal is injected into the constructor of a class.
The IPrincipal is created by doing HttpContext.Current.User.
Since the code is in a Task, it runs on a different thread, and the action has returned so HttpContext.Current is null.
If I try doing Thread.CurrentPrincipal, it exists, but is set to something other than what the HttpContext.Current.User was. Also, if I try accessing Thread.CurrentPrincipal.Identity.Name, I get an ObjectDisposedException thrown.
I'm using Ninject for my IoC container. The IPrincipal binding looks like this
Bind<IPrincipal>().ToMethod( x => HttpContext.Current.User );
Is there a way to get the correct or even a usable IPrincipal in the code ran from a Task in a controller action?
the HttpContext.Current.User is basically something like a temporary ThreadLocal. As such, you'll need to retrieve it's value before starting a task which is run in a different thread. There's no way to find out which thread has executed Task.Factory.StartNew and retrieve it's ThreadLocal values.
Consider, that once you do Task.Factory.StartNew the webrequest may end even before the task has actually started.
So what you need to do is something along the lines of:
IPrincipal principal = HttpContext.Current.User;
Task.Factory.StartNew( () =>
{
myService.ExecuteMethod(principal);
} );
Now however if IPrincipal is (ctor-)injected into myService and myService is instantiated per request (.InTransientScope(), .InRequestScope()) your current binding would work, since the controller is instanciated for each request (also see lifecycle of an asp.net mvc5 application).
The problem would only arise when injecting Lazy<IPrincipal> or when doing IResolutionRoot.Get<IPrincipal> (this includes usage of ninject.extensions.factory, like Func<IPrincipal>!), because at that point HttpContext.Current.User may not be valid any more.
I suggest that if you want an answer more precise and more to the point, you need to post a Minimal, Complete, and Verifiable example

Initialize model values on initial request only and not on postback request

I have a managedbean AddDeviceBean where in instantiating all domain objects used in screen in the constructor
public AddDeviceBean() {
device = new DeviceVO();
deviceacct = new DeviceAccountsVO();
deviceconfig = new DeviceConfigVO();
devicecurr =new DeviceCurrencyVO();
devicelink = new DeviceLinkVO();
devicetran = new DeviceTranVO();
devicecd = new DeviceCDVO();
deviceBlank = new DeviceBlankVO();
comments = new ArrayList<DeviceCommentsVO>();
}
I have a DB2 sequence whose next value has to be set for a property on pageload
I am using #PostConstruct annotation to generate the next value and setting the value.
Problem is I have commandButton on the screen which invokes some method in same bean and #PostConstruct is called twice after submitting and the DB2 next value is called
I need to get next value only once during page load and not during submit
That will indeed happen when your managed bean is request scoped. A request scoped bean is constructed on every single HTTP request. The initial request counts as one request. The form submit (the postback) counts as another request.
If you want to make the bean to live as long as you're interacting with the same view, then you should be making it a view scoped one instead.
#ManagedBean
#ViewScoped
public class AddDeviceBean {
// ...
}
See also:
How to choose the right bean scope?

Struts 2 Session in interceptor

I am trying to access session object from within my interceptor by implementing SessionAware interface (I've implemented the setSession method), but I am not able to get my session object this way.
Then I tried ActionContext.getContext().getSession() and I am able to get the session, but I don't know but its coming out to be empty for only the first time in every browser for every user & then it comes filled when another action is invoked.
I assume there is something wrong going with the session. Why is it giving me an empty session only for the first time? Does it set something only after giving empty session for the first time?
If this is the case then everyone will be shown as guest on their first request & then with a username on their 2nd request & hence forth.
Or am I getting the session in the wrong way?
I saw code to get sessions in interceptors, but this does not work for me as it cannot find the constant HTTP_REQUEST.
final ActionContext context = invocation.getInvocationContext();
HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
HttpSession session = request.getSession(true);
Object user = session.getAttribute(Constants.USER_HANDLE);
Any suggestion on resolving any of the problems?
Something I forgot to mention - my website is a secure site(https), so if the user is not logged in, it would not let him enter the website & if he is logged in, at least his username should be there in the session. Shouldn't it be?
I have an interceptor that also grabs the session as a map. Have you tried something like this? This works for me.
public String intercept( ActionInvocation actionInvocation ) throws Exception {
Map session = actionInvocation.getInvocationContext().getSession();
You can use the following to return the HttpSession -- and create it if it doesn't exist.
HttpSession session = ServletActionContext.getRequest().getSession(true);
If you need the Map instead, then you can just call this right after that last call (since you passed true, the session would have been created and the next call will return a blank session map).
Map<String, Object> session = ActionContext.getContext().getSession();
Alternatively, you can use the CreateSessionInterceptor early in your stack so that the session is created by the time you need it. Then just use the map example above to get it.
FYI: ServletActionContext is a subclass of ActionContext that just has convenience methods for getting the request and response without needing to use the constants like you were trying in your example.
Try this. It worked for me in struts 2.0.14.
public String intercept(ActionInvocation ai) throws Exception {
Map session = ActionContext.getContext().getSession();
You can get the session on Interceptor using ActionContext itself...refer the following code snippet:
SessionMap<String, Object> session = ActionContext.getContext().getSession();
or alternatively you can follow this approach:
The request is available on the ActionContext instance, which is made available via ThreadLocal.
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession(true);
Add the import:
import org.apache.struts2.StrutsStatics;

ASP.NET MVC: ensure user always has a session variable set

Consider an ASP.NET MVC application that requires a session variable be set. It's used throughout the app. It'll be set by either reading a hashed value on the browser cookie, or after having the user login.
In the WebForms + Master Page model, I'd check the Page_Load() of the master page. Perhaps not the ultimate event, but it was an easy one to find.
How would you check and enforce the existence of a session variable in ASP.NET MVC? Consider that this question might not involve user login details, but some other piece of data (first visit time, perhaps).
Solution Attempts
public void Application_BeginRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Session["SomeDateTime"] = DateTime.Now.ToString();
// results in Object reference not set to an instance of an object.
// context.Session is null
}
You have two options.
1.Place logic in base controller's Initialize function
Assuming that all your controllers inherit from a base controller, you can place the logic needed in the override of the Execute() function of the base controller.
public class BaseController : Controller
{
public BaseController()
{
}
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
// check if the user has the value here using the requestContext.HttpContext object
}
{
2. Use the Global.asax void Application_PreRequestHandlerExecute(Object source, EventArgs e) function
public void Application_PreRequestHandlerExecute(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
// use an if statement to make sure the request is not for a static file (js/css/html etc.)
if(context != null && context.Session != null)
{
// use context to work the session
}
}
Note: The second part works with any ASP.NET application, WebForms or MVC.
As for enforcing that they have a certain session variable, its very open really. You can redirect to a certain page for them to fill out a form or select an option or something. Or maybe just have a default value that is set to a certain session key if it is not found.
EDIT
While playing with this, I noticed a big issue with Application_PreRequestHandlerExecute approach. The event handler is being called for any request done to the server, be it .css/.js/.html files. I'm not sure if this is an issue with the way my workstation is setup, or just how ASP.NET/IIS works, so I would make sure that this isn't being called on all requests when implementing the approach above.
It is for the previous reasons I wrapped the work to be done in the session with an if statement.
Not sure I fully understand the question, but I do this by override the OnActionExecuting method of the controller.
In there you do the code to see if the Session Variable exists. If not, create it, if so then use it.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Session != null)
{
//TODO: Get value from session etc.
}
base.OnActionExecuting(filterContext);
}
As another alternative, the ControllerActionInvoker class invokes every action method; it gets assigned to the controller via the controller factory. So you could subclass this action invoker, everytime an action is invoked (by overridding the InvokeAction method) check here for this existence...

enforcing ObjectStateManager entry deletion on page leave

I have a edited RESTful wizard based upon Shoulders of Giants | A RESTful Wizard Using ASP.Net MVC… Perhaps? . This wizard has a CANCEL button which, when pressed, fires the code below.
// If the user cancels, drop out altogether
if (!string.IsNullOrEmpty(CANCEL_BUTTON)) {
Session.Remove(VACANCYWIZARD_SESSION_KEY);
repository._entities.ObjectStateManager.GetObjectStateEntry(inProgressVacancyWizard.Vacancy).Delete();
return this.RedirectToAction("Index", "Home");
}
Now, to be able to call SaveChanges() after the cancel button I have to manually delete the entry from the wizard from my ObjectStateManager. But when you cancel the wizard by just manually returning to the home page it stays in and a next call to _entities.SaveChanges() will throw an exception that it cannot Save the object, from the wizard progress to the database, since it is still in the object state.
Note that in between the steps of the wizard I do not save anything to the database. I keep it in session state retrieving it each step:
NewVacancy inProgressVacancyWizard = Session[VACANCYWIZARD_SESSION_KEY] as NewVacancy;
Somehow, however, the inProgressVacancyWizard.Vacancy does appear in the ObjectStateManager so I have to delete it else I will get errors about incomplete Vacancies models while the _entities.SaveChanges() is called for another object.
Is there a way to cover for this problem?
//edit
After some reading I have found out that the fundaments of my repository are not good. As found here. Currently I'm doubting to implement the option mentioned in "One ObjectContext instance per business transaction" in the same article. Would that be a wise thing? I would like to hear some more about it since it will be a major refactor.
public static Repository Instance
{
get
{
if (instance == null) {
instance = new Repository();
}
return instance;
}
}
#region Constructor: Repository()
/// <summary>
/// Constructor
/// </summary>
private Repository()
{
_entities = new DBModelEntitiesNew2();
}
It seems like you are using a single ObjectContext instance across multiple requests. Don't do that. It will cause you nothing but misery. It makes your web server stateful. Dispose the ObjectContext after the response is rendered (we do it, indirectly, from Controller.Dispose), and new up a new one for the next request.

Resources