Struts2 Model object is not populating to form field - struts2

Form field is not automatically getting populated for below scenario
public class EmployeeAction extends ActionSupport implements ModelDriven{
private Employee employee=new Employee();
public Object getModel() {
return employee;
}
public String execute() throws Exception {
employee=employeeService.findById(employee.getId());
return super.execute();
}
}
the problem is new employee object but form is expecting old object reference , so manual mapping is working fine,
public String execute() throws Exception {
BeanUtils.copyProperties(employee,employeeService.findById(employee.getId()));
return super.execute();
}
how to avoid this object(new/old) reference problem
Employee.jsp
<s:form action="saveemployee" method="post">
<s:hidden name="id"></s:hidden>
<s:textfield name="name" label="Name" />
<s:textfield name="age" label="Age"></s:textfield>
<s:radio name="gender" label="Gender" list="%{staticMasterMap.gender}" listKey="key" listValue="value" ></s:radio>
<s:label value="DOB"></s:label><fw:datepicker name="dob" id="dob" changeMonth="true" changeYear="true" format="dd/mm/yy" yearRange="1900:2010"></fw:datepicker>
<s:textarea name="address" label="Address"></s:textarea>
<s:submit label="Save"></s:submit>
<s:reset label="Reset"></s:reset>
</s:form>
Struts.xml
<package name="fw" extends="struts-default" namespace="/">
<action name="saveemployee" class="com.example.employee.action.EmployeeAction"
method="save">
<result name="input" type="tiles">employee</result>
<result name="success" type="tiles">employee</result>
</action>
<action name="findemployee" class="com.example.employee.action.EmployeeAction" method="findById">
<result name="success" type="tiles">employee</result>
<result name="input" type="tiles">employee</result>
</action>
</package>

I have not much worked with Struts2 Model Driven Interface and can not recommend what best can be done in your case, but if i am right this is one of the pitfalls of using the Model Driven Interface.
In your case by the time the execute() method has been invoked S2 already has obtained a reference to your Model object which it will use for this particular request cycle. This means you're changing the reference inside your execute method using
employee=employeeService.findById(9l);
but still the framework has reference to the old model object. Since S2 gets the reference using the getter method it has no information what you are doing inside execute method and which is the cause of your data inconsistency.
Honestly i am not sure about any solution for this use-case and will go for a simple Object backed property approach.
Hope some one can provide a workaround if my inputs are correct.

I think the problem is simpler. By default, Struts 2 actions are instanced in each request (also written as having a scope of a request).
When you find a certain Employee an instance of EmployeeAction is created and used to populate the form.
When you press the "Save" button, a new instance of EmployeeAction is created with a new (empty) Employee instance.
You could verify if this is the case by having a constructor log something or using a debugger.
Solving this could be done by saving information to the Session (the employee id, for example) and verifying this data to create a new Employee instead.
Other option is to change the ObjectFactory of Struts 2 to something like Spring.
References: I've used Struts2 with and without Spring and tried to find some official reference. Struts 2 documentation is not clear about the instance creation in the request cycle but the documentation for the Spring plugin of Struts 2 says
Spring 2's bean scope feature can be used to scope an Action instance
to the session, application, or a custom scope, providing advanced
customization above the default per-request scoping.

Related

Action mapping in struts

I found in a struts project this action mapping:
<action name="action" class="MyClass" method="add">
<result name="success">/jsp/test.jsp</result>
</action>
And in MyClass there is no method name ="add" but there is a method "onAdd"
I wanna know if struts know the name of the method in this case or its an error ?
because I found this in much actionmapping in this project;
Thanks for your help;
Server will throw exception there is no method MyClass.add().
For more details you can look struts2 documentation Action Method
Because when you are mapping action into the staruts.xml then you have to specify the particular class and method and if you are not specifying that method then by Default it will call Execute() method. so now try this and that will call the particular class.
<action name="actionname" class="package.class" method="methodname">
<result name="success">/folder/xyz.jsp</result>
</action>
that way it will work.

Configuration of the beans when using <f:param> <f:viewParam>

I'm trying to pass a parameter between a JSF page to another, from a bean to another. I know it is a common question, infact I've tried several approaches before writing it down.
To do that I have put both the beans in session scope and added in the first bean the following:
<p:commandButton value="Submit" type="submit"
actionListener="#{sourceBean.save}" action="success">
<f:setPropertyActionListener
target="#{targetBean.foo}" value="#{sourceBean.foo}" />
</p:commandButton>
The problem is that I don't want these beans to be in session scope but in view scope.
So I tried to put in my first page:
<p:commandButton value="Submit" type="submit"
actionListener="#{sourceBean.save}" action="success">
<f:param name="foo" value="#{sourceBean.foo}"/>
</p:commandButton>
And in the second page:
<f:metadata>
<f:viewParam id="foo" name="foo" value="#{targetBean.foo}"
/>
</f:metadata>
The problem is that the passed String is null so, obviously, I get an error form the Converter.
I think I'm missing something in the configuration of my managed beans. Do I have to link target and source bean in someway?
At this moment I have this configuration:
<managed-bean>
<managed-bean-name>targetBean</managed-bean-name>
<managed-bean-class>guiBeans.TargetBean</managed-bean-class>
<managed-bean-scope>view</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>sourceBean</managed-bean-name>
<managed-bean-class>guiBeans.SourceBean</managed-bean-class>
<managed-bean-scope>view</managed-bean-scope>
</managed-bean>
Another question: In my app, the value foo.id, that I use during the conversion, is set autonatically by the database when I save the object so when I call:
actionListener="#{sourceBean.save}"
The converter gets the id and turns it into a String (and viceversa, if needed).
So, I wanted to know if in JSF is first called the actionListener or the function that sets the parameters .
Could be this the reason why I get a null String? Thanks a lot.
The <f:param> is evaluated during rendering of the form, not during submitting of the form. Your problem suggests that the #{sourceBean.foo} value is only been set during submitting the form and thus not available during rendering of the form.
You'd basically need to replace action="success" by action="#{bean.action}" with
public String action() {
return "success?foo=" + foo.getId();
}
Or, if you're using navigation cases
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/some.xhtml</to-view-id>
<redirect>
<view-param>
<name>foo</name>
<value>#{sourceBean.foo.id}</value>
</view-param>
</redirect>
</navigation-case>
Unrelated to the concrete problem, the <f:param> doesn't support the converter attribute at all. You'd have to access the desired property directly (which is id in the above example).

Pass attribute into struts2 action via struts.xml

How can I pass an attribute into my struts2 java action that tells me whether the action was called from one URL path / action mapping vs another?
I figured I could put something in the struts.xml for two action mappings but use the same java action and just pass in a flag into the action.
You'll want to use the <param/> tag. I do this frequently for actions that handle both adding and editing an entity, as the fields, validations, and whatnot are virtually identical. Here's an example of that.
struts.xml
<action name="users/add" class="AddEditUserAction">
<param name="edit">false</param>
<result name="input">/WEB-INF/jsp/addEditUser.jsp</result>
</action>
<action name="users/{username}/edit" class="AddEditUserAction">
<param name="edit">true</param>
<result name="input">/WEB-INF/jsp/addEditUser.jsp</result>
</action>
The Action
public class AddEditUserAction {
private boolean isEdit;
// this is called by the struts.xml to set the value
public void setEdit(final boolean edit) {
isEdit = edit;
}
}
In order for this to work, you need the static parameters interceptor in your stack (it's included by default).
I question the design.
I'd handle it by specifying a method in the action configuration for one or both mappings.
The method(s) would set a flag in the action and call the "guts" of the action, which would query the flag value, and proceed accordingly.

struts2: default parameter generated by s:url tag

In struts2 we use
<action name="anAction">
<result name="success">xx.jsp</result>
</action>
to define action, and use s:url to generate a link to the action
<s:url action="anAction"></s:url>
The above s:url will output "/anAction.do".
I wonder if it's possible to let s:url generate a default URL parameter (i.e. /anAction.do?p=xxx for all links), without modifying the existing s:url tags (there are many and they are scattered). The goal is to let the parameter appear in the link for SEO purpose.
Available options can be: changing any action config, changing any struts config, even rewrite the s:url generation class.
Edit: I found that adding this to struts.xml
<constant name="struts.url.includeParams" value="get" />
partially solves my problem (as long as the initial page has ?p=xxx, all subsequent links will have it). The short-comings are obvious: the parameter will not follow a redirect action. I am still searching for more sophisticated solution.
I did figure out how this can be done. The steps are:
1) create a class which implements org.apache.struts2.components.UrlRenderer
2) register that class with struts object factory and it will be injected as necessary
Details? Ok.
1) For example, a subclass of ServletUrlRender might look like this:
package com.example;
import java.util.Map;
import org.apache.struts2.components.ServletUrlRenderer;
import org.apache.struts2.components.UrlProvider;
public class ParameterInjectingUrlRenderer extends ServletUrlRenderer {
#Override
protected void mergeRequestParameters(String value, Map<String, Object> parameters, Map<String, Object> contextParameters) {
super.mergeRequestParameters(value, parameters, contextParameters);
parameters.put("myParameter", "secretvalue");
}
}
2) In struts.xml, set this class as the renderer implementation, with a line like this:
<constant name="struts.urlRenderer" value="parameterInjectingUrlRenderer" />
(In this example I'm using a Spring object factory, and value references a spring bean id. If you are not, I believe you put the fully-qualified class name).
That's it!
Global Search and replace (All IDE's should have this feature)
<s:url action="anAction"></s:url>
With
<s:url action="anAction"><s:param name="p" value="'xxx'"/></s:url>
Now every "anAction" will have a parameter p with value xxx.
It generally a good idea to specify the namespace.
<s:url namespace="/" action="anAction"><s:param name="p" value="'xxx'"/></s:url>

Referencing actionerror from httpheader result in struts.xml

In my TestClass action I am setting an action error using addActionError method. I have an action defined in the struts.xml as following
<action name="TestAction" class="TestClass">
<result name="input">/jsp/test.jsp</result>
<result name="error" type="httpheader">
<param name="error">409</param>
<param name="errorMessage">${SOME-EXPRESSION}</param>
</result>
</action>
The intent is to have the error message display whatever was added using addActionError. According to the org.apache.struts2.dispatcher.HttpHeaderResult documentation, I should be able to use Ognl expressions within errorMessage parameter.
So, is it possible to put something in place of ${SOME-EXPRESSION} that will reference actionerror in this scenario.(I tried ${actionerror} but it didn't work)
I know that I can have a workaround by declaring my own field (for example "errorText") in the action class and using that insteda of addActionError referencing it using ${errorText} inside the param tags. But before I go that route, want to make sure that's the only way.
Action errors are stored in a list, so you'll have to show something like ${actionErrors[0]}. But keep in mind this way it will only show your first added error, not all those you have included using addActionError.

Resources