The default stack has the parms interceptor who binds the parameters to the setters found in the action who is being invoked. That works fine when you know the name of the parameter. For example if a request parameter is "employee.name" that could be binded to a property of my action which has a property "employee" with a property "name". In order to get it to work I need to know which name the request parameter name would be and put a setter in my action called setEmployee() of an object type Employee or it could be a Map too.
What if I want to let the action bind that param to another property that I don't know which one will be. Let's say that the action receive as a parameter the name on which the request parameter will be set.
<s:action name="showEmployee" executeResult="true">
<s:param name="employeePrefix">xyz.wz.empl</s:param>
</s:action>
That would mean to the action to bind all the parameters of the employee to xyz.wz.empl.
For example let's say the request parameter has the following:
xyz.wz.empl.name=Alfredo
xyz.wz.empl.lastName=Osorio
I would like to bind that to a property of my action, let's say a Map employee, but that won't work because the request parameter is xyz.wz.empl. How can I bind that dynamic parameter to the invoked action using the parameter that was sent to the action (employeePrefix).
I could ask for the request parameters
ActionContext.getContext().getParameters()
and do the conversion myself but I think there must be another way to explicitly call something from the Struts 2 framework to the conversion, in the way that com.opensymphony.xwork2.interceptor.ParametersInterceptor does.
Thank You.
This is not an ideal solution in my opinion but it will do what you need.
Your Struts 2 Action will need to implement SessionAware and ParameterAware interfaces.
Create a private field that is a map called 'parameters' and create a getter/setter for it.
Now when you call getParameters() you will have a map of your name/value pairs.
Now for this next portion you will need to use reflection.
So for example you will need to do this:
Map myParams = getParameters()
Employee e = new Employee()
Class clas = null;
try {
clas = Class.forName("Employee");
Field fieldlist[] = clas.getDeclaredFields();
for (int i = 0; i < fieldlist.length; i++) {
Field fld = fieldlist[i];
if(myParams.containsKey(fld.getName()))
fld.set(e, myParams.get(fld.getName())
}
} catch (ClassNotFoundException ex) {
// handle exception case
}
This should map all parameters in that map to a field in the employee object if it exists in the map. This can be of course ported out to a separate method that would belong in something like a Util class where you pass in the class you want to reflect on.
Related
I'm creating a web application with Struts 2. In a JSP file, I created a form like this :
<s:form action="actionclassname!%{methodToCall}" method="post">
...
</s:form>
In the action class, I created two methods which will be called depending on the value of the variable methodToCall. That works well.
My problem is that the two methods of the action class need some validations. So I used annotations to validate the form if the first method is called :
#Validations(....)
public String actionMethod1(){
...
}
#Validations(...)
public String actionMehod2(){
...
}
The first method works well with validations. My problem is on the second method. It seems like when I define validations (also using annotations) for this second method, the validations I defined on first method are executed again before those I defined for the second method.
How to make validations on the second method to run when it's this method which is called by the JSP?
In the action configuration via annotation you should use a parameter validateAnnotatedMethodOnly for validation interceptor, like in this example
#Action(value="actionclassname", results = {
#Result(name="input", location = "/actionclassname.jsp")
},interceptorRefs = #InterceptorRef(value="defaultStack", params = {"validation.validateAnnotatedMethodOnly", "true"}))
I have Struts 2 actions with different (HTML and JSON ) result types. They use common interceptor.
If needed to intercept the request, how to return a result based on given action result type?
For example, my Action.ERROR forwards to JSP page. If action is JSON type I want to forward JSON error instead.
In Struts2 the action has not a type. This means that you cannot configure the type of the action. Instead you can configure result types in the xml configuration. In the xml configuration file this is defined as the result-type tag. When you configure the result using result tag you specify type attribute that will be used to determine the corresponding result type. Say name="success" or name="error" are results of the dispatcher result type.
When the action is intercepted you could get the results
Map<String, ResultConfig> results = actionInvocation.getProxy().getConfig().getResults();
In the ResultConfig there's className attribute that could be used to determine the type of the result.
I have Struts2 actions with different (HTML and JSON ) result types. They use common interceptor. If need to intercept the request, how to return result based on given action result type?
For example, my Action.ERROR forwards to JSP page. If action is JSON type I want to forward JSON error instead. Please advice.
Although is true that an Action has not a type, it's also true that, if an Action is called in an AJAX way, like an action returning JSON, all of its results should have the same result type (JSON in this case), unless you are using a single Action to perform different logical actions (ajax and non-ajax operations, that is an anti-pattern);
That said, if you want to return the proper GLOBAL error result, from inside an Interceptor that is used by all of your Actions (each one with its result type), based on their other result type (let's say: SUCCESS, assuming every Action has a SUCCESS result), this is the way to do it:
public String intercept(ActionInvocation invocation) throws Exception {
// Get the action configuration defined in struts.xml
ActionConfig config = invocation.getProxy().getConfig();
// Get the SUCCESS result configured for that Action
ResultConfig success = config.getResults().get("success");
// Get the class of the SUCCESS result
Object clazz = success.getClass();
/* .... oops, some error occurred !!
We now need to redirect to the right global error result .... */
if (clazz instanceof org.apache.struts2.dispatcher.ServletDispatcherResult) {
log.debug("Struts2 Result type: CLASSIC");
return "error";
} else if (clazz instanceof org.apache.struts2.json.JSONResult) {
log.debug("Struts2 Result type: JSON");
return "jsonError";
} else {
log.debug("Struts2 Result type: SOMETHING ELSE, returning default ");
return "error";
}
}
Although this is technically possible, i would discourage it because... there is no real reason to do it;
For your purpose, remember that each global result is scoped in its <package>;
Since you could (/should) have two different packages for Classic Actions (a <package> extending struts-default) and JSON Actions (a <package> extending json-default), you can simply define two different global error result for each package with the same name but different result type; this way the Interceptor will call the one relative to the package of the current Action, outputting the desired kind of result.
I'm trying to create a simple crud form to insert data into a database with hibernate, without knowing what the object type is. The ultimate goal is to only have one insert form for every table in the database. So far i get the methods that the current object has, check to see if it has any set methods and create a text input for every field that has a set.
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
HtmlPanelGrid hpg = (HtmlPanelGrid) viewRoot.findComponent("panel");
for (Method method : declaredFields) {
String name = method.getName();
if (name.contains("set")) {
HtmlOutputText hot = new HtmlOutputText();
HtmlInputText hit = new HtmlInputText();
hot.setValue(name.substring(3));
try {
hit.setValue(newObject.getClass().getMethod(name, String.class));
} catch (Exception ex) {
Logger.getLogger(ReflectController.class.getName()).log(Level.SEVERE, null, ex);
}
hpg.getChildren().add(hot);
hpg.getChildren().add(hit);
}
}
Here newObject is the object that is going to be inserted into the database later with hibernate. My problem is this:
How do assign a certain field from that object to the text input that is being created at the moment. So far if I put the method in the value like I'm doing above, it will just print out the method in the value attribute for that input. what i want is that when this form is submited, for to assign the value in that text box to the property with that name.
I can give you a partial answer - You need to create a ValueExpression dynamically
Application app = FacesContext.getCurrentInstance().getApplication();
hit.setValueExpression("value", app.getExpressionFactory().createValueExpression(FacesContext.getCurrentInstance().getELContext(), "#{bean.item}", Item.class));
The hard part will be creating the valueExpression that will actually map to a field within your object's value. That requires a great deal more thought but you will for sure need the dynamic valueExpression. As written, this will result in the execution of your bean's setItem();method with a parameter of type Item. You will require something a little more complex.
In JSF, binding input components to properties is accomplished with EL-expressions. You can create one programmatically as Steve shows, but that syntax is really ugly. On a related note, programmatic manipulation of the component tree is a rather unorthodox way of using JSF. The orthodox way to tackle your requirement would be something like:
<ui:repeat var="prop" value="#{genericEditorBean.propertyNames}">
<h:outputLabel value="#{prop}" for="input"/>
<h:inputText id="input" value="#{genericEditorBean.object[prop]}"/>
</ui:repeat>
where
public List<String> getPropertyNames() {
List<String> propertyNames = new ArrayList<>();
BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
propertyNames.add(pd.getName());
}
return propertyNames;
}
(There really is no reason to reimplement scanning for Java Bean properties when the Java API offers a class for that very purpose. Unlike your home-grown version, this will also handle properties inherited from a super class ...)
I once used an open-source library named MetaWidget to do this.
It was a few years ago, but it worked well and was easy to set up.
It looks like the project is still active:
http://metawidget.sourceforge.net/index.php
I'm working on converting a legacy project to Spring (trying to adjust little as possible for now) and I'm running into a small issue with mapping/translating legacy parameters to a model attribute object. I may be completely wrong in thinking about this problem but it appears to me that to translate a parameter to a specific model attribute setter is to pass in the request parameter through a method for creating a model attribute and manually call the correct setter:
#ModelAttribute("form")
public MyForm createMyForm(#RequestParameter("legacy-param") legacy) {
MyForm myForm = new MyForm();
myForm.setNewParam(legacy);
return myForm;
}
I don't necessarily want to change the request parameter name yet since some javascript and JSPs are depending on it being named that way but is there any way to do something like this? Or is there a different way to map/translate request parameters to model attributes?
public class MyForm {
#ParameterName("legacy-param")
private String newParam;
public void setNewParam(String value) { ... }
public String getNewParam() { ... }
}
#Controller
public class MyController {
#RequestMapping("/a/url")
public String myMethod(#ModelAttribute("form") MyForm myForm, BindingResult result) { ... }
}
The way you've written that model attribute method is indeed odd. I'm not entirely clear what you're actually trying to do.Assuming there are many parameters, you're going to end up with an awful lot of instances of MyForm in your ModelMap. A more 'normal' way to create model attribute would be like this:
#ModelAttribute("legacyParamNotCamel")
public MyForm createMyForm(#RequestParameter("legacy-param-not-camel") String legacy) {
return legacy;
}
Then in the JSP you can refer to it directly in expression language. e.g.,
<c:out value="${legacyParamNotCamel}"/>
If you want to put them onto a form backing object, you need to do it all in a single method that creates the object, not make new copies of it in each method. (assuming your form has more than a single parameter associated with it.)
--
It seems like what you're really trying to do though is translate the parameter names in the request before the web data binder gets ahold of it, so that you can bind oddly named parameters onto a java bean? For that you'll need to use an interceptor that translates the names before the binding process begins, or make your own subclass of the databinder than can take a property name translation map.
You placed the #ModelAttribute at the Method Level but the intention seems to be more of a formBackingObject hence we should be dealing at the Method Parameter Level
There's a difference.
I put up an explanation here on my blog along examples at Spring 3 MVC: Using #ModelAttribute in Your JSPs at http://krams915.blogspot.com/2010/12/spring-3-mvc-using-modelattribute-in.html
In one of my Struts action I've got the following code in a method:
...
List<Object> retrievedListOfObjects = c.getListOfObjects();
return mapping.findForward("view");
}
fw_view leads to a new Struts action with another Struts form. Let's say this form has got among others the following field
List<Object> listOfObjects;
I now want to pass the retrievedListOfObjects from within the first Struts action to the form of the following Struts action.
Is this possible without storing it in the session?
you can store it as a request attribute.
request.setAttribute("listOfObjects", listOfObjects);
and then in the Action that is forwarded to
List<Object> listOfObjects = (List<Object>)request.getAttribute("listOfObjects");
Given that when setting request attributes you can give them meaningful names, you should consider setting many attributes rather than setting one big list of objects.
Correction of krock code.
Setting object to request:
request.setAttribute("listOfObjects", listOfObjects);
Getting the object in an other action.
List<Object> listOfObjects = (List<Object>)request.getAttribute("listOfObjects");