If my action class is as per below:
<!-- language: lang-java -->
package org.tutorial.struts2.action;
import java.util.Map;
import org.apache.struts2.interceptor.RequestAware;
import org.tutorial.struts2.service.TutorialFinder;
import com.opensymphony.xwork2.Action;
public class TutorialAction implements Action, RequestAware {
private String language;
private String bestTutorialSite;
public String execute() {
System.out.println(language);
setBestTutorialSite(new TutorialFinder().getBestTutorialSite(language));
System.out.println(bestTutorialSite);
if (getBestTutorialSite().contains("Java"))
return SUCCESS;
else
return ERROR;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public String getBestTutorialSite() {
return bestTutorialSite;
}
public void setBestTutorialSite(String bestTutorialSite) {
this.bestTutorialSite = bestTutorialSite;
}
#Override
public void setRequest(Map<String, Object> requestObj) {
System.out.println(bestTutorialSite);
requestObj.put("message", bestTutorialSite);
}
}
When this action is invoked prior to the execute method, the language is already populated by Struts2 framework. In the execute method the setBestTutorialSite method is to populate the private field bestTutorialSite.
Now I thought of setting this private field bestTutorialSite into the request attributes (in the setRequest method). However I notice that this method is invoked first before any private field (like the language) is populated. Hence in the setRequest method, the system print of bestTutorialSite is always null.
I thought I was able to set this attribute with the bestTutorialSite (captured from the execute method) prior to calling the JSP page.
I don't think i fully grasp the understanding of Struts2 flow - obviously! :OP
Please help.
Thanks.
I assume you're using defaultStack which looks like this:
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="debugging"/>
<interceptor-ref name="profiling"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
</interceptor-stack>
as you can see, servletConfig interceptor is before params interceptor which means first Request will be set on your action (with servletConfig) and then your action will be populated with request parameters (with params).
What you want to achieve is to change the order of the interceptors which can be harmful when used in wrong way.
I think request interceptor will execute first,After that the execute() method will execute.
It might be the problem.
Related
Suppose I configure a "per method" validation into my action, using the Struts2 validation framework (in my example with annotations, but also with xml it is the same).
Suppose I have three public exposed methods in my action:
public class MyAction extends ActionSupport {
#Override
public String execute() throws Exception {
//some code here...
}
#Validations(
customValidators = {#CustomValidator(type="myCostomValidator", fieldName="myFieldName", shortCircuit=true, message="")}
)
public String info() {
//some code here...
}
#Validations(
customValidators = {#CustomValidator(type="myCostomValidator", fieldName="anotherFieldName", shortCircuit=true, message="")},
visitorFields = {#VisitorFieldValidator(context="update_context", fieldName="anObjectField", message="", shortCircuit=true)}
)
public String update() {
//some code here...
}
//getters, setters and other...
}
Now, each of the three methods can be invoked and has a different validation. If the validation fails the framework set the result "input" that must be configured into the struts.xml:
<action name="myAction_*" method="{1}" class="com.test.gui.action.test.MyAction">
<result name="success">result1.jsp</result>
<result name="edit">result2.jsp</result>
<result name="input">result3.jsp</result>
</action>
How can I have different "input" results for each action method? For example, I'd like to reach one page if the validation fails for the info() method and another page if the validation fails for the update() method.
Thank you.
I've found this solution: the use of #InputConfig annotation.
With this annotation you can set a 'per method' result name for validation errors. So in the struts.xml you can configure for each result name the page that you want to reach.
Examples:
#InputConfig(resultName="input_update")
public String update() {
// perform action
return SUCCESS;
}
and in struts.xml: <result name="input_update">jsp/input_update.jsp</result>
or:
#InputConfig(methodName="inputMethod")
public String update() {
// perform action
return SUCCESS;
}
public String inputMethod() {
// perform some data filling
return INPUT;
}
struts.xml: <result name="input">jsp/myInput.jsp</result>
See documentation at struts.apache.org/docs/inputconfig-annotation.html
It can also be set the input result name in the struts.xml by setting a parameter for the 'workflow' interceptor, but it affects all methods in the action:
<interceptor-ref name="workflow">
<param name="inputResultName">custom_input_result</param>
</interceptor-ref>
see doc here struts.apache.org/docs/default-workflow-interceptor.html
You don't have to modify returned result name. You are using wildcard mapping for your action, so use it for JSP file name as well.
<action name="myAction_*" method="{1}" class="com.test.gui.action.test.MyAction">
<result name="success">success.jsp</result>
<result name="input">result_{1}.jsp</result>
</action>
I am developing one java project using struts2 and hibernate
i have one super action class name it as ProjAction where in that project management every event will come across the main Super Class.
i have one child action class name it as saveDataAction after saving the data it goes to the super action class and gives the result.
So upto to the Super action class i can able to display the action message using the store interceptor in struts2 config file for its respective action class results. i have used the operationMode as 'AUTOMATIC'
NOW to avoid the refresh issue i have one action class name it as ProjRedirectAction. So in this action class i am not able to show/retrieve the action message after saving the data
can anyone tell me how to access the action message in ProjRedirectAction class using store interceptor. or else is there any way to show the action message to multiple action classes of struts2.
public class SuperAction extends ActionSupport {
public String getData() {
//implementation for showing the data in the jsp page
//the result is always redirect to some default.jsp
return SUCCESS;
}
//private method which accessed by its child class for redirecting to superclass
public String nextCall() {
//few implementation done after that it calls the getData() method
return getData();
}
}
//Child Class implementaion for ex saving records in db
public class ChildAction extends SuperAction {
public String saveData() {
//saving data to db and then calling the return nextCall method of superACtion class
return nextCall();
}
}
//earlier i had this implementation, the reason being adding one more action class in my project is to avoid refresh issue(on refresh the same value was inserting to DB) so i have added one more action class with name RedirectAction
//RedirectACtion class implementation
public class RedirectAction {
public String execute() {
//this method is having same implementation as the method getData() of SuperAction class includes
return SUCCESS;
}
}
//struts.xml config
<package name="default" extends="struts-default" namespace="/">
//SuperACtion class result
<action name="*Super" method="{1}" class="com.action.SuperAction">
<interceptor-ref name="store">
<param name="operationMode">AUTOMATIC</param>
</interceptor-ref>
<result name="SUCCESS" type="redirectAction">executeRedirect</result>
</action>
//ChildACtion class result
<action name="*Child" method="{1}" class="com.action.ChildAction">
<interceptor-ref name="store">
<param name="operationMode">AUTOMATIC</param>
</interceptor-ref>
<result name="SUCCESS" type="redirectAction">executeRedirect</result>
</action>
//RedirectACtion class result
<action name="*Redirect" method="{1}" class="com.action.RedirectAction">
<interceptor-ref name="store">
<param name="operationMode">AUTOMATIC</param>
</interceptor-ref>
<result name="SUCCESS" type="tiles">Default.jsp</result>
</action>
this issue i am not finding any solution , any help is really appriciated
thank you
I want to have a form that creates one of several subclasses in a type hierarchy. Say it's AbstractPerson with the subclasses Employee and Visitor.
Can I do that with a single Action / REST-Controller Bean?
Usually I use the form-ids smart, so it assigns values directly to the setters of my Action.
So if I have a member like
AbstractPerson member;
I would try to use a form with an input field called "member.name".
However, struts must create an instance of AbstractPerson first - and it can't because it's abstract! It would be very cool if I could give struts2 a hint that it should actually create a Empolyee or Visitor object (depending on the form content). Is that or sth similar possible?
Cheers!
This is similar to something I've done recently to access Entity classes via a small set of crud actions. That is a handful of crud actions allow you to operate on all the entity classes within a certain package. You should be able to apply this strategy to your Employee and Visitor classes.
How it works in a nutshell:
1) You need to specify in either the namespace or the action name the name of the class that should be created.
2) You use struts2s prepareable interface to create a model. (Reflectively create the class determined from step 1.
3) Use the model driven interface, which returns the object defined in step 2. This way that object is at the top of the stack and you can simply say "name" and know that it is the name attribute of the Class determined in step 1. You could avoid this step but it isn't as pretty.
Now there is a small glitch in doing this, you'll find that to perform the above three steps you'll need a custom stack, a "staticParams-prepare-params" stack.
First an example and then a definition of that stack to make this work, please let me know if you have any questions:
package com.quaternion.demo.action.crud;
import com.quaternion.demo.orm.ActionValidateable;
import com.quaternion.demo.service.CrudService;
import com.quaternion.demo.util.ActionUtils;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;
import org.springframework.beans.factory.annotation.Autowired;
// Adds a new record to the database
#ParentPackage("staticParams-prepare-parms")
#Namespace("/crud/{entityName}")
#Result(type = "kjson") //TODO: could rid of this line by setting the result as the default for the package
public class AddAction extends ActionSupport implements Preparable, ModelDriven {
private static final Logger log = Logger.getLogger(AddAction.class.getName());
#Autowired
private CrudService crudService;
private String entityName;
private Object entityModel;
private Map jsonModel = new HashMap(); //for output, return the newly created object
private Class clazz;
#Override
public String execute() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
log.log(Level.INFO, "In execute entityName is set with {0}", entityName);
//If an id is passed in it will merge the object with that id, null will be used for unset attributes
String status = SUCCESS;
boolean error = false;
Object entity = null;
try {
entity = crudService.create(clazz, entityModel);
} catch (Exception e) {
error = true;
status = ERROR;
jsonModel.put("message", e.getMessage());
}
if (error == false) {
jsonModel.put("entity", entity);
}
jsonModel.put("status", status);
return SUCCESS;
}
public Object getEntityModel() {
return entityModel;
}
public void setEntityModel(Object entityModel) {
this.entityModel = entityModel;
}
public Object getJsonModel() {
return jsonModel;
}
#Override
public Object getModel() {
return this.entityModel;
}
#Override
public void prepare() throws Exception {
log.log(Level.INFO, "In prepare entityName is set with {0}", entityName);
clazz = ActionUtils.initClazz(entityName);
entityModel = clazz.newInstance();
}
public String getEntityName() {
return entityName;
}
public void setEntityName(String entityName) {
this.entityName = entityName;
}
//TODO: validation would be a good idea can't implement in this class need to delegate
//if entity implements a validate method, this validate should
//call that validate
#Override
public void validate(){
if (entityModel instanceof ActionValidateable){
((ActionValidateable)entityModel).validate(this);
}
}
}
Here is a definition for the stack:
<package name="staticParams-prepare-parms" extends="struts-default">
<result-types>
<result-type name="kjson" default="true" class="com.quaternion.demo.result.Kjson"/>
</result-types>
<interceptors>
<interceptor-stack name="staticParamsPrepareParamsStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="staticParamsPrepareParamsStack"/>
</package>
You might wonder what the kjson result type is. I was feeling challenged by the struts2-json plugin on several other actions. I created a generic paging and read actions, "flexjson" does not serialize collections by default, which prevents lazy loading issue (well in the case the collections were not loaded which will always be the case with these simple services) so kjson is just a result type which returns json using flexjson.
This is my struts.xml file. As you can see an action class and an interceptor are configured.
My question is that, the interceptor is being called, but not the action class.
(Where as in tutorials, I read that after interceptor is executed it calls the action class method)
Please see my code:
<struts>
<package name="test" extends="struts-default">
<interceptors>
<interceptor name="loginkiran" class="vaannila.MyLoginInterCeptor" />
</interceptors>
<action name="HelloWorld" class="vaannila.HelloWorld" method="kiran">
<interceptor-ref name="loginkiran" />
<result name="SUCCESS">/success.jsp</result>
</action>
</package>
</struts>
This is my Action class's kiran method
public class HelloWorld {
public String kiran() {
System.out.println("Hi inside the Kiran Method");
return "SUCCESS";
}
}
========================
This is my Interceptor class
public class MyLoginInterCeptor implements Interceptor {
#Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("The server Port is"+str);
return invocation.invoke();
}
}
You haven't defined the INPUT result. Define it within struts.xml and direct to a page with
<s:actionerror/>
tag and you'll figure out what was going wrong. If that doesn't work, try this
<action name="HelloWorld" class="vaannila.HelloWorld" method="kiran">
<interceptor-ref name="loginkiran" />
<interceptor-ref name="basicStack" />
<result name="SUCCESS">/success.jsp</result>
<result name="input">/x.jsp</result>
</action>
You missed the defaultStack.
Add <interceptor-ref name="defaultStack"></interceptor-ref> , below your mentioned interceptor reference
Please write System.out.println(invocation.invoke()); and check what it returns. Add try/ catch block to your code it will give you an idea. I think it is failing in invoke() method.
I am using <s:form action="someAction">
my struts.xml contains
<action name="someAction"
class="com.test.testaction.getValue"
method="getValuedemo">
<result name="success" type="redirectAction">demo</result>
</action>
while my Action contains
public class getValue extends ActionSupport{
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getValuedemo() {
userName = "tmpUser";
}
}
My question is how to get userName property in demo.action????? Please help
You can pass userName as a parameter
<action name="someAction" class="com.test.testaction.getValue" method="getValuedemo">
<result name="success" type="redirectAction">
<param name="actionName">demo</param>
<param name="userName">${userName}</param>
</result>
</action>
Also add userName getter/setter in the demo action
Values associated with a specific action are per-request. If you set values in an action and then redirect, those values will be lost. If getValue is just redirecting to demo, what is the purpose of the getValue action? Why not just have the userName getter and setter on DemoAction?
Please revise your question to provide more details on what you are trying to do.
Additionally, your action name does not adhere to Java naming conventions for a class, which should start with a capital letter. Additionally, you might want to come up with a better name for the action than GetValue.
First use the chain result type...
<result name="success" type="chain">demo.action</result>
Then read about interceptors so you can avoid using chain, redirect, redirectAction.