I'm building a Struts2 application, where data is sent to my application in a request header. I have a custom interceptor that grabs this data and uses it to retrieve some data from the database and store it in the session. I then use this session data in the resultant JSP. One problem: the session variable is null until I refresh the page.
Here is my stack:
<interceptor-stack name="myStack">
<interceptor-ref name="myInterceptor"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
Here is my intercept method in myInterceptor:
ActionContext context = invocation.getInvocationContext();
HttpServletRequest request = (HttpServletRequest) context.get(StrutsStatics.HTTP_REQUEST);
HttpSession session = request.getSession(true);
MyObject obj = new MyObject();
String header = request.getHeader("HEADER_VALUE");
if(header != null) {
obj.loadByHeader(header);
session.setAttribute("value", obj);
}
return invocation.invoke();
And here is the JSP code:
<s:if test="#session.value == null">
...
</s:if>
Like I said, this works when I either refresh or go to another page using this value. Am I doing something wrong? Or can I not get the value until the next time around? If it's the latter, is there an alternative way to get that data on first pass?
You should be able to do set it and use it in one shot! I don't see any issue in what you've provided, my guess would be that you are rendering your view the first time without going though your interceptor.
Two ways this could happen: 1, you have more than one package. One of the packages has your custom interceptor stack defined and the other does not. The one that does not renders the view the first time and then subsequent renderings do pass through your interceptor, 2) it is also possible if you have more than one action which will render the view and you have interceptors being applied at the action level.
Throw print/logging message into the interceptor and make sure it is being executed when you think it is.
Edit: reverse order of defaultStack and myInterceptor since the params interceptor will not have run before myInterceptor.
Related
In my app, before upgrading to jsf 2, when doing a custom redirect I used to manually put a request parameter with a specific value in external context like this:
FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap()
.put(CmwNavigationControllerBean.PARAM_DISPLAY_TARGET_POPUP, "true");
Now this line, throws an exception because it seems that this map is no longer allowed to be modified:
at java.util.Collections$UnmodifiableMap.put(Unknown Source) [rt.jar:1.7.0]
Isn't really no other way to bypass this exception? I'm doing refactoring because of upgrade and I try to keep the changes at minimal level.
You can either use a view parameter or use the flash scope for that. A view parameter is in practice a GET parameter which you can pass when you request the page you want to redirect to. For your case, you should redirect to it with the parameter appended.
Return the navigation case with the parameter appended:
//Will be reflected in browser's address bar as /context/myDestinationView.xhtml?displayTargetPopUp=true
return "myDestinationView?displayTargetPopUp=true&faces-redirect=true&includeViewParams=true";
Catch it from your destination view:
<f:viewParam name="displayTargetPopUp" value="#{displayTargetPopUp}" />
Another way if you want to avoid including it in your GET request, is to use flash scope, which is supposed to be fixed for Mojarra 2.1.27 and 2.2.5 versions. Flash scoped values are designed to support a redirection, while the request ones are not.
See also:
Understand Flash Scope in JSF2
How do you pass view parameters when navigating from an action in JSF2?
Rather than getRequestParameterMap() (which is read-only) you should invoke getRequestMap() on the ExternalContext.
For example:
FacesContext.getCurrentInstance()
.getExternalContext()
.getRequestMap()
.put(CmwNavigationControllerBean.PARAM_DISPLAY_TARGET_POPUP, "true");
I am using a RequestContextHolder in a filter to record a piece of data, and want to access it in a POJO (wired up via Spring) later. I'm getting an exception which suggests I'm doing something wrong here, would appreciate any guidance on what that is.
Filter code (in a doFilter() method, whose logging confirms it's being called):
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
if (attrs == null)
{
logger.info("Creating new ServletRequestAttributes");
attrs = new ServletRequestAttributes(servletRequest);
}
attrs.setAttribute("my_attr", "hello there!", RequestAttributes.SCOPE_REQUEST);
RequestContextHolder.setRequestAttributes(attrs);
POJO code:
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
if (attrs != null && attrs.getAttribute("my_attr", RequestAttributes.SCOPE_REQUEST) != null)
{
String myAttr = (String) attrs.getAttribute("my_attr", RequestAttributes.SCOPE_REQUEST);
logger.debug("Got it: ", myAttr);
}
I am seeing this exception coming from Tomcat though:
java.lang.IllegalStateException: The request object has been recycled and is no longer associated with this facade
at org.apache.catalina.connector.RequestFacade.getAttribute(RequestFacade.java:259)
at org.springframework.web.context.request.ServletRequestAttributes.getAttribute(ServletRequestAttributes.java:98)
at com.mycompany.MyClass(MyClass.java:50)
I do wonder if having the "set data" in a filter, and "get data" via the real work of the request could be in play here, but not sure how best to accommodate that, if it even is relevant?
The error is likely caused by the fact that you're making Spring maintain a thread-local handle to a request object that is no longer valid. The details probably don't matter all that much since a different approach is in order.
One of the following will take care of automatically setting and clearing thread-local state properly: DispatcherServlet, RequestContextListener or RequestContextFilter. You need to figure out which one makes the most sense for how Spring is used with your app. Your filter and POJO code shouldn't need to make use of classes like RequestContextHolder directly. What should happen is that you declare a proxied, request-scoped bean for the attribute you want to access:
<bean id="myAttr" scope="request">
<aop:scoped-proxy/>
</bean>
Then, declare the bean for your POJO along with its dependency on the request-scoped bean:
<bean id="myPojo">
<property name="myAttr" ref="myAttr"/>
</bean>
See: Request, session, and global session scopes
Spring should take care of all the details...
I'm new to Struts2, coming from a PHP background, where I'd often have the same file handling GET and POST requests, and processing a form if the request is a POST request.
I currently have the following in struts.xml:
<action name="ProcessData" class="ProcessDataAction">
<result name="*">processdata.jsp</result>
</action>
<action name="ProcessDataUpload" class="ProcessDataAction" method="upload">
<result name="*">processsdata.jsp</result>
</action>
Which works fine, but it bothers me that the URL that handles POST is different, since now if the user reloads the page, they get an error rather than simply seeing the contents of the GET page.
So my question is, is there any way to tell struts2 to call one method if it's a GET request, and another method if it's a POST request?
Struts2 doesn't offer what you described out of the box. If you want to enforce that a particular action method is invokable only by certain HTTP methods, then you'd need to create a custom interceptor and probably a few custom annotations.
If you just want the same action to handle displaying the form and processing it, then you can do the following:
public class MyAction {
public String execute() {
return INPUT;
}
public void validate() {
// perform any form validation needed
}
public String submit() {
// process the form and then redirect
}
}
In your form, you would submit to ProcessData!submit. The ! separates the action from the action method name. It provides what you have already, but you don't need to explicitly map each method in the struts.xml.
But it bothers me that the URL that handles POST is different, since now if the user reloads the page, they get an error rather than simply seeing the contents of the GET page.
Redirecting the user after a successful post completely nullifies this point. Look at the "Redirect After Post" or "Post/Redirect/Get" pattern).
Not by default, no. IMO the cleanest solution is to tweak the method name via an interceptor that looks at the request type. For example, I had a simple one that looked for executeGet and executePost methods.
Whether or not it's a great idea... different issue.
In my JSF application, I need to redirect the user from Page A to page B if a session attribute such as userRole has a value of "contributor", and to a page C, for example, if it is "author".
I'm told that I have to implement a page listener, or a session listener perhaps. Although I understand writing a listener class is quite straightforward and standard, I don't know how to set it up on the JSF page itself (to listen to the session).
Anybody?
A session listener (HttpSessionListener) is unsuitable since it doesn't have a reference to the current HTTP request/response, which are mandatory in order to change the request/response destination.
Use a filter. To learn more about filters, check our servlet-filters tag info page. Note that session scoped JSF managed beans are by itself stored as HttpSession attribute with the managed bean name as key. You could access them in doFilter() method as follows:
Bean bean = (Bean) ((HttpServletRequest) request).getSession().getAttribute("bean");
Or when it's to be determined based on a POST action, just return a different outcome in the managed bean action method. Then just make use of (implicit) JSF navigation. Pseudo:
public String submit() {
if (user is contributor) return "pageB";
if (user is author) return "pageC";
return "pageA";
}
This question already has answers here:
How do servlets work? Instantiation, sessions, shared variables and multithreading
(8 answers)
Closed 5 years ago.
So far I understand Httpsession concepts in Java.
HttpSession ses = req.getSession(true);
will create a session object, according to the request.
setAttribute("String", object);
will, bind the 'String', and value with the Session object.
getAttribute("String");
will return an object associated with the string, specified.
What I am not able to understand is: I am creating a session object like
HttpSession ses = req.getSession(true);
and setting a name for it by calling setAttribute("String", object);.
Here, This code resides inside the server. For every person, when he tries to login the same code in the server will be executed. setAttribute("String", object); in this method the string value is a constant one. So, each session object created will be binded by the same string which I have provided. When I try to retrieve the string to validate his session or while logout action taken the getAttribute("String"); ll return the same constant string value(Am I right!!?? Actually I don't know, I'm just thinking of its logic of execution). Then, how can I be able to invalidate.
I saw this type of illustration in all of the tutorials on the WEB. Is it the actual way to set that attribute? Or, real application developers will give a variable in the "String" field to set it dynamically
(ie. session.setAttribut(userName, userName); //Setting the String Dynamically.. I dono is it right or not.)
And my final question is
WebContext ctx = WebContextFactory.get();
request = ctx.getHttpServletRequest();
What do the two lines above do? What will be stored in ctx & request?
HttpSession ses = req.getSession(true); will creates new session means. What value stored in ses.
Some [random] precisions:
You don't need login/logout mechanisms in order to have sessions.
In java servlets, HTTP sessions are tracked using two mechanisms, HTTP cookie (the most commonly used) or URL rewriting (to support browsers without cookies or with cookies disabled). Using only cookies is simple, you don't have to do anything special. For URL re-writing, you need to modify all URLs pointing back to your servlets/filters.
Each time you call request.getSession(true), the HttpRequest object will be inspected in order to find a session ID encoded either in a cookie OR/AND in the URL path parameter (what's following a semi-colon). If the session ID cannot be found, a new session will be created by the servlet container (i.e. the server).
The session ID is added to the response as a Cookie. If you want to support URL re-writing also, the links in your HTML documents should be modified using the response.encodeURL() method. Calling request.getSession(false) or simply request.getSession() will return null in the event the session ID is not found or the session ID refers to an invalid session.
There is a single HTTP session by visit, as Java session cookies are not stored permanently in the browser. So sessions object are not shared between clients. Each user has his own private session.
Sessions are destroyed automatically if not used for a given time. The time-out value can be configured in the web.xml file.
A given session can be explicitly invalidated using the invalidate() method.
When people are talking about JSESSIONID, they are referring to the standard name of the HTTP cookie used to do session-tracking in Java.
I suggest you read a tutorial on Java sessions. Each user gets a different HttpSession object, based on a JSESSIONID request/response parameter that the Java web server sends to the browser. So every user can have an attribute with the same name, and the value stored for this attribute will be different for all users.
Also, WebContextFactory and WebContext are DWR classes that provide an easy way to get the servlet parameters.
As I understand it, your concerns are about separation of the different users when storing things in the HttpSession.
The servlet container (for example Tomcat) takes care of this utilizing its JSESSIONID.
The story goes like this :
User first logs onto website.
Servlet container sets a COOKIE on
the user's browser, storing a UNIQUE
jsessionId.
Every time the user hits the
website, the JSESSIONID cookie is
sent back.
The servlet container uses this to
keep track of who is who.
Likewise, this is how it keeps track
of the separation of data. Every
user has their own bucket of
objects uniquely identified by the
JSESSIONID.
Hopefully that (at least partially) answers your question.
Cheers
Your basic servlet is going to look like
public class MyServlet{
public doGet(HttpServletRequest req, HttpServletResponse res){
//Parameter true:
// create session if one does not exist. session should never be null
//Parameter false:
// return null if there is no session, used on pages where you want to
// force a user to already have a session or be logged in
//only need to use one of the two getSession() options here.
//Just showing both for this test
HttpSession sess = req.getSession(true);
HttpSession sess2 = req.getSession(false);
//set an Attribute in the request. This can be used to pass new values
//to a forward or to a JSP
req.setAttribute("myVar", "Hello World");
}
}
There is no need to set any attribute names for your session that is already done. As others have suggested in other answers, use cookies or URL re-writing to store the sessionID for you.
When you are dealing with the DWR WebContext, it is simply doing the same thing as above, just normally the Request object isn't passed into the method, so you use the WebContext to get that request for you
public class DWRClass {
public doSomething(){
WebContext ctx = WebContextFactory.get();
HttpServletRequest req = ctx.getHttpServletRequest();
HttpSession sess = req.getSession(); //no parameter is the same as passing true
//Lets set another attribute for a forward or JSP to use
ArrayList<Boolean> flags = new ArrayList<Boolean>();
req.setAttribute("listOfNames", flags);
}
}