I have created the index.jsp which send action request
<meta http-equiv="REFRESH" content="0;url=./radioButton.action">
After completing this the request is forwarded to radio.jsp. In this one i am showing country, state list etc.. (so i am redirecting for this from index.jsp).
Now i am imposing validations to this form. i have created the .xml correctly for validation.When the validation fail it will be redirected to ./radioButton.action
<result name="input" type="redirectAction">radioButton</result>
I have created one interceptor which extends MethodFilterInterceptor, for this to keep the action errors in session scope which having the follow logic in after() method.
if (fieldErrors != null && fieldErrors.size() > 0)
{
Iterator it = fieldErrors.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pairs = (Map.Entry)it.next();
System.out.println(pairs.getKey() + " = " + pairs.getValue().toString());
}
session.put(FIELD_ERRORS_KEY, fieldErrors);
}
public static final String FIELD_ERRORS_KEY = "RedirectMessageInterceptor_FieldErrors";
i have configured the interceptor in my .xml as below
<interceptors>
<interceptor name="redirectMessage" class="com.daya.message.RedirectMessageInterceptor" />
<interceptor-stack name="sessionStack">
<interceptor-ref name="redirectMessage" />
<interceptor-ref name="defaultStack" />
</interceptor-stack>
</interceptor-stack>
</interceptors>
I am getting the error message on my console befor putting into session scope. The output printed in console is
userName = [User Name is required to login]
password = [Password is required to login]
I am using the below tag to display the field error
<s:fielderror></s:fielderror>
But in the jsp after redirect (when validation failed) messages are not getting display.
Client side validation is working well. I disabled javascript for checking server side validation
As Struts providing a way handle this using MessageStoreInterceptor but i don't know why you are doing it manually.
Related
I am trying to create URLs that look like http://localhost:8080/app/client/shared-namespaces-and-actions:
“/app” would be the context root.
“client” would actually be a variable, replaced by the actual client version/interface of the application, such as
“john” (i.e.:
http://localhost:8080/app/john/namespaces-and-actions).
“/shared-namespaces-and-actions” would be packages/actions
accessible under one or more clients (such as “/about/contact”, or
“/products/73/edit”).
Ideally, a URL could entirely omit the “client” component, and so that for actions where it makes sense some sort of combined view could be displayed. For example “/app/” would display links to the client versions, whereas the client’s “/app/client/” would have what that client wants on their homepage.
This variable “client” component is what I have a question about.
Thus far, I have been using a query string parameter to handle the client selection, rather than part of the URL path. However, this has some obvious issues for things like bookmarks, as I always have to pass the client parameter in the query string (not attractive), else bookmarks will fail.
Is there an easy way to make this change? It seems like what I want is a wildcard in the namespace, as I do with actions, but the documentation doesn't seem to support that as a possibility, nor did experimentation. I already use packages to set namespaces, interceptor stacks, and to group related actions in the struts.xml file, so it would not be trivial to change all actions to be under the "/" namespace package with an additional wildcard for the client.
An option I was looking at was to extend the class DefaultActionMapper, and override the method parseNameAndNamespace(String, ActionMapping, ConfigurationManager), removing the client component of the URI (if it exists), and then passing that modified URI to the parent implementation. However this has a number of issues, such as breaking all links, form targets, and redirects. I expect fixing the links/forms would be annoying to fix, but not impossible, but I'm not sure if the same would be true of redirects.
I am using 2.3.16.3.
struts.xml:
<struts>
<constant name="struts.enable.SlashesInActionNames" value="true" />
<constant name="struts.mapper.alwaysSelectFullNamespace" value="false" />
<constant name="struts.patternMatcher" value="namedVariable"/>
<constant name="struts.action.extension" value="" />
<package
name="poc-default"
extends="struts-default"
strict-method-invocation="true">
<default-interceptor-ref name="defaultStack" />
<default-action-ref name="http404" />
<action
name="http404"
class="poc.DefaultAction">
<result name="success">/WEB-INF/http404.jsp</result>
</action>
</package>
<package
name="root"
namespace="/"
extends="poc-default"
strict-method-invocation="true">
<action
name=""
class="poc.DefaultAction">
<result name="success">/WEB-INF/home.jsp</result>
</action>
<action
name="clients"
class="poc.DefaultAction">
<result name="success">/WEB-INF/client-select.jsp</result>
</action>
</package>
</struts>
Using the struts.xml above, "http://localhost:8080/poc/" goes to the home page, and "http://localhost:8080/poc/clients" goes to the client selection page.
I then replace the package "root" with the following (added named wildcard to namespace):
<package
name="root"
namespace="/{uriClientString}/"
extends="poc-default"
strict-method-invocation="true">
<action
name=""
class="poc.DefaultAction">
<result name="success">/WEB-INF/home.jsp</result>
</action>
<action
name="clients"
class="poc.DefaultAction">
<result name="success">/WEB-INF/client-select.jsp</result>
</action>
</package>
Now, neither action is mapped by the URLs "http://localhost:8080/poc/john/" and "http://localhost:8080/poc/john/clients".
Having given Dave Newton's comment some thought, I believe that he is correct that I was going about solving my problem (optional client selector prefix to URL) is probably not how I should be going about this.
URL rewriting seems like a better (and possible) choice, and instead putting the client into context via an attribute of the request. I have created a filter that appears before the Strut 2 filter in web.xml:
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws ServletException, IOException {
final HttpServletRequest httpRequest = (HttpServletRequest) request;
final String requestServletPath = httpRequest.getServletPath();
// Obtain set of client strings here.
final List<String> clients = Arrays.asList("john");
String newServletPath = null;
for (String client : clients) {
final String toCheck = "/" + client;
if (requestServletPath.equals(toCheck) || requestServletPath.startsWith(toCheck + "/")) {
/*
* Put client into context here, {#link HttpSession#setAttribute(String, Object)} or
* {#link ServletRequest#setAttribute(String, Object)}.
*/
request.setAttribute("uriClientString", toCheck);
newServletPath = requestServletPath.substring(toCheck.length());
break;
}
}
if (newServletPath != null)
request.getRequestDispatcher(newServletPath).forward(request, response);
else
chain.doFilter(request, response);
}
It was also necessary Struts 2's filter mapping to support forwards:
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
Unfortunately, it is necessary to rewrite the generated URLs to include the client prefix if it is being used, since otherwise Struts2 will just start from the actual namespace:
<s:a namespace="%{#request.uriClientString}" action="">Home page</s:a>
Finally, it is also necessary to rewrite the redirects to include the client prefix if it is being used, since, once again, Struts2 will just start from the actual namespace:
<action name="re">
<result type="redirectAction">
<param name="namespace">${#request.uriClientString}</param>
<param name="actionName">clients</param>
</result>
</action>
This is not as easy to implement as I was hoping at the outset, but it does seem like it will meet my needs once everything is updated.
I will accept this in a couple of days if nothing better is suggested.
I am trying to use addActionMessage() and addActionError() to pass messages and error from actions (e.g. in execute()) to the forwarded page.
In the JSP, I use:
<s:if test="hasActionMessages()">
<s:actionmessage/>
</s:if>
to display such messages.
But no message is shown. I am wondering if anyone could give a fix on this problem, or suggest another solution. I am new to Struts and web development, and I am not sure what is a proper pattern for passing messages from actions to pages.
EDIT: action-mapping code and java code
<action name="myAddUser" class="org.any.backend.action.UserAdminAction" method="addUser">
<result name="success" type="redirectAction">myUserAdmin</result>
<result name="input" type="redirectAction">myUserAdmin</result>
</action>
Java code:
public String addUser() throws Exception {
// check duplicate
for (User u : userList)
if (u.getUserName().equals(userName)) {
addActionError("A user with the same user name already exists. Choose another user name. ");
return INPUT;
}
if (userName != null && !userName.isEmpty() && password != null && !password.isEmpty()) {
User newUser = new User();
newUser.setUserName(userName);
newUser.setPassword(password);
userList.add(newUser);
addActionMessage("User " + userName + " added. ");
return SUCCESS;
} else {
addActionError("User name and password cannot be empty");
return INPUT;
}
}
Your code is right.
Maybe you are using a REDIRECT-ACTION result type, or a CHAIN.
They both will lose action messages and errors, forcing you to put them in session (and clean them once displayed) for this page.
EDIT: I'm assuming that you are using the block
<s:if test="hasActionErrors()">
<s:actionerror />
</s:if>
<s:if test="hasActionMessages()">
<s:actionmessage/>
</s:if>
and not only the posted one, or you will never see the errors, only the messages...
Use session.setAttribute(...) in Action Class. and <logic:present .... > tag in jsp.
hope this will help you.
As I have seen you are using redirect action, you can still keep the action error/messages using one of the interceptors.
http://struts.apache.org/2.3.1.2/docs/message-store-interceptor.html
Here's an example in the struts.xml
<action name="submit"
class="com.dummy.SubmitAction">
<interceptor-ref name="store">
<param name="operationMode">STORE</param>
</interceptor-ref>
<interceptor-ref name="defaultStack" />
<result name="success" type="redirectAction">
<param name="actionName">view</param>
<param name="asnid">${id}</param>
</result>
</action>
<action name="view"
class="com.dummy.ViewUserAction">
<interceptor-ref name="store">
<param name="operationMode">RETRIEVE</param>
</interceptor-ref>
<interceptor-ref name="defaultStack" >
<param name="workflow.excludeMethods">execute</param>
</interceptor-ref>
<result name="success">pages/user.jsp</result>
</action>
Im trying to make it so that once i submit this form i cannot hit the back button, but with the current configuration I cannot even get the page/form to load. I can't seem to figure out why "invalid.token" is always being triggered thus redirecting me to index.jsp no matter what I have the token tag in my form like im supposed to. If i use the "excludeMethods" filter and exclude View then my page loads but I can hit the back button freely so it still does not work properly. I have tried moving the interceptor-ref above and below my noLoginStack but it dosen't make a difference. Based on my debugging my actual java class isn't even being hit, so its failing before then. What am I doing wrong?
My action declaration:
<action name="viewAppointmentLetter" class="edu.ucr.c3.rsummer.controller.instructor.ManageAppointmentLetters">
<interceptor-ref name="noLoginStack"/>
<interceptor-ref name="token" />
<result name="invalid.token">/index.jsp</result>
<result name="error" type="redirectAction">index.do</result>
<result name="input">/instructor/assigned_appts.jsp</result>
<result name="view">/instructor/assigned_appts.jsp</result>
<result type="redirectAction">index.do</result>
</action>
My assigned_appts.jsp:
<s:form action="saveAppointmentLetter" onsubmit="return verifySubmit();">
<s:token name="token" />
.....
</s:form>
If its any clue I always get this in my console
WARN org.apache.struts2.util.TokenHelper - Could not find token name in params.
In struts2 the order of interceptor is very important. you should follow this order.
<interceptor-ref name="token"/>
<interceptor-ref name="noLoginStack"/>
USe TokenSession interceptor.Had to handle result by result name="invalid.token" in struts.xml in specific action.
The page from which your action is generated at that page you have to write <s:token> tag in the header
I use Token Session to prevent duplicate form submits, but the first time I make a request to server, I always get error page
<action name="show" class="ClientAction">
<interceptor-ref name="tokenSession" />
<interceptor-ref name="basicStack" />
<result name="invalid.token">/WEB-INF/error.jsp</result>
result type="tiles" name="success">page.view</result>
</action>
"<s:token />" was added to may success page between <s:form> and </s:form>, but it doesn't run correctly.
plz help me to solve them, is there another way prevent duplicate form submits. I wait for suggestion, thank u very much. : )
It seems that you are not using proper interceptor name. If you want to use the session token, it is token-session.
try using token-session instead of tokenSession.
Hope that helps.
tag <s:token /> must be inserted into form which is double-submitted, not into success form. If token tag is missing, interceptor resolve the submitted request as invalid even if it's the first attempt.
On default I want my struts2 app to forward to an action:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<constant name="struts.devMode" value="false" />
<package name="myApp" namespace="/myApp" extends="struts-default">
<action name="Login_*" method="{1}" class="myApp.SessionManager">
<result name="input">/myApp/Login.jsp</result>
<result type="redirectAction">Menu</result>
</action>
</package>
<package name="default" namespace="/" extends="struts-default">
<default-action-ref name="index" />
<action name="index">
<result type="redirectAction">
<param name="actionName">Login_input.action</param>
<param name="namespace">/myApp</param>
</result>
</action>
</package>
</struts>
I'm looking for the application to call SessionManager.input(), but instead it calls SessionManager.execute().
You don't want typically a public input() method. The most basic and typical scenario for an Action is:
The Action is intended to "do something" (one thing), and this action needs some user input.
The Action does than something in the method execute()
If the Action did succesfully that something, it returs SUCCESS. This triggers a result informative JSP page.
If the Action didn't get the user input (because there wasn't any, or because it was insufficient or wrong) it returns INPUT. This triggers a "input form" JSP so that the user can fill the data and (re)try the action.
Now, this basic scenario can be coded in several ways, among them:
1) Two different mappings, one for the input form, other for the execution
<!-- default action class: execute does nothing, returns SUCCES -->
<action name="ActionXXXShowForm">
<result>/myApp/XXXForm.jsp</result>
</action>
<action name="ActionXXX" class="myApp.XXXAction">
<result name="input">/myApp/XXXForm.jsp</result>
<result>/myApp/XXXDone.jsp</result>
</action>
2) Just one mapping
<action name="ActionXXX" class="myApp.XXXAction">
<result name="input">/myApp/XXXForm.jsp</result>
<result>/myApp/XXXDone.jsp</result>
</action>
Both are very similar (specially if your are doing programmatic validation).
In both cases, we have only a "struts" method in the action (execute), which is good practice as our action only "does one thing".
But in the second case, we need to deal with the case in which no data is post, and dont emit an error message for that case.
Example: In case 1:
public class XXXAction extends ActionSupport {
...
public String execute() throws Exception {
if(! inputOk()) return INPUT;
// .. do your stuff
return SUCCESS;
}
private boolean inputOk() {
// ... check your inputs - sort of programatic validation
if( surname == null ) addActionError("no surname");
// .... more checks
return ! hasActionErrors());
}
}
In case 2 you modify it slightly:
public class XXXAction extends ActionSupport {
....
public String execute() throws Exception {
if( emptyInput() ) return INPUT; // no error messages in this case
if(! inputOk()) return INPUT;
// .. do your stuff
return SUCCESS;
}
private boolean inputOk() {
// same as before
}
private boolean emptyInput() {
// detect no input was sent. do not emit errors herer
}
}
When you call an actions from jsp, the default method is execute(). If you want to call another method, you can spec by the attribute method="".
<s:url action="SessionManager" method="input"/>
The attribute method is common on other tags.
If you are using a <s:a... tag, then you will not be able to call the intended method apart from the default execute() method. Instead of <s:a... tag, you need to define the action using an <s:url... tag with an id and call the same using normal html anchor tag ie.,
<a href=${<<id>>} but you may need to specify the method in your struts.xml or any other xml configuration file, which you have included in your struts.xml file with the method attribute in your <action... method="..." clause.