Anyone already get to work with a AbstractIterationElementProcessor derived class in a spring project which uses Thymeleaf? I am trying use this to implement this kind of tag:
<form:form ...>
...
</form:form>
The element is iterated based on a List which should be generated inside the processor class, based on the array with the list of field of the class from model class passed to the view (named command).
In each iteration, one element of the list is processed by a switch-like structure inside the tag and a inner element is chosen (form:input, form:select, etc).
Right now, I have this code:
public class FormProcessor extends AbstractIterationElementProcessor {
public FormProcessor() {
super("form");
}
#Override
public void processClonedHostIterationElement(Arguments arguments, Element iteratedChild) {
//
}
#Override
public String getIteratedElementName(Arguments arguments, Element element) {
return "form";
}
#Override
public boolean removeHostIterationElement(Arguments arguments, Element element) {
return false;
}
#Override
public AbstractIterationElementProcessor.IterationSpec getIterationSpec(Arguments arguments, Element element) {
return null;
}
#Override
public int getPrecedence() {
return 1000;
}
}
But I am have some trouble to understand what implement in each method. Anyone can give a hint on how to do that?
Related
I often want to refine posted data before use it, for example
public class Song() {
public String[] tags;
public String csvTags;
public void setTagsWithCsv() {
// this one should be more complicated for handling real data
this.tags = csvTags.split(",");
}
}
In this case, I have to call setTagsWithCsv method inside the method of the controller class.
#RequestMapping(value = "/song/create", method = POST)
public String createSong(Song song) {
song.setTagsWithCsv();
songService.create(song); // some code like this will come here
...
}
Is there any way to call the method with an annotation like '#PostConstruct'? The method should be called after a post request.
Maybe you just provided a bad example, but If your Song is in a form of POJO, you do it on a call to setCsvTags
public class Song {
private String[] tags;
private String csvTags;
public void setCsvTags(String csvTags) {
this.csvTags = csvTags;
this.tags = csvTags.split(",");
}
public void setTags(String[] tags) {
this.tags == tags;
String newCsvTags = Arrays.toString(tags);
this.csvTags = newCsvTags.substring(1, newCsvTags.length() - 1); // get rid of []
}
}
or make a method, without keeping explicit tags array
public class Song {
private String csvTags;
public void getTags() {
return csvTags.split(",");
}
}
Otherwise, there is no standard way of doing this, you can play with request interception before reaching your Controller, but I think it would be just a waste of time.
I get Exception
org.springframework.beans.NotReadablePropertyException: Invalid property 'entries[0].reason' of bean class [my.company.data.SDROrder]: Bean property 'entries[0].reason' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
from the following code snippet:
Errors errors = new BeanPropertyBindingResult(new SDROrder(), "sdr");
orderValidator.validate(order, errors);
for validator:
public class OrderValidator implements Validator
{
#Override
public boolean supports(Class<?> clazz)
{
return Order.class.isAssignableFrom(clazz);
}
#Override
public void validate(final Object target, final Errors errors)
{
errors.rejectValue("entries[0].reason", "Wrong Reason");
}
}
where we have such data hierarchy
public class Order
{
private List<AbstractOrderEntry> entries;
public List<AbstractOrderEntry> getEntries()
{
return entries;
}
public void setEntries(List<AbstractOrderEntry> entries)
{
this.entries = entries;
}
}
public class SDROrder extends Order
{
}
public class AbstractOrderEntry
{
}
public class SDROrderEntry extends AbstractOrderEntry
{
private String reason;
public String getReason()
{
return reason;
}
public void setReason(String reason)
{
this.reason = reason;
}
}
Please see working example here: here
Update 1: Just to clarify. The problem is I try to rejectValue on object that has Collection of objects where each element has specific attribute at Runtime but has not it at Compile time. Spring uses Bean's properties to resolve these fields and can't find inherited attribute. The question is: can I explain Spring to resolve inherited fields somehow?
I found the solution here.
The trick is at
org.springframework.validation.Errors.pushNestedPath(String)
and
org.springframework.validation.Errors.popNestedPath()
methods.
The correct validation should be done as follow:
errors.pushNestedPath("entries[0]");
errors.rejectValue("reason", "Wrong Reason");
errors.popNestedPath();
I have two controller classes extending basecontroller. I have a common functionality to be implemented and implementation is in basecontroller.
public class BaseController{
protected void populateWidget(List li, int zipcode){
//implementation for child A
populate only 10 employee records
//implementation specific to child B
populate 100 student records
}
}
public class ChildA extends BaseController{
List<Employees> li = ...
populateWidget(li, 90034)
}
public class ChildB extends BaseController{
List<Students> li = ...
populateWidget(li, 90034)
}
I have written setChildB method and working on it. like below
public class BaseController{
protected boolean childB;
protected void setIsChildB(boolean childB){ this.childB = childB;}
protect boolean isChildB(){ return childB; }
protected void populateWidget(List li, int zipcode){
//implementation for child A
if(!isChildB())
populate only 10 employee records
else
//implementation specific to child B
populate 100 student records
}
}
public class ChildA extends BaseController{
List<Employees> li = ...
populateWidget(li, 90034)
}
public class ChildB extends BaseController{
List<Students> li = ...
setChildB(true); //setting as child B call
populateWidget(li, 90034)
}
Please advice the best way to do it.
I recommend implementing a Template Method Pattern. You could have your base controller like this:
public class BaseController {
protected abstract void getPeople(List li, int zipcode);
protected void populateWidget(List li, int zipcode) {
//Do generic logic here
//Refer to child
getPeople(List li, int zipcode)
//Do some more generic stuff
}
}
And then each of your children like this:
public class ChildA extends BaseController {
#Override
protected void getPeople(List li, int zipcode) {
//Specific Logic here
}
}
The children each override the getPeople() method to do their own thing.
That way if you need a ChildC, you can simply extend the parent without changing it.
Update: Based on the comments, here is an alternative way of doing this.
I would not do it as a setter, rather I would make a constructor argument on the base class that takes the type of child as an argument. This makes sure that the compiler enforces that you will always have a value set
You could use a boolean but I recommend strongly using an Enum, String constant or int constant.
The above two points are mostly because I always anticipate that I might have to do a ChildC when some requirements change comes along and at that point you won't have to do changes to the base class. It also falls into the set of patterns that other programmers I work with would expect and therefore makes the code easier readable and maintainable by others.
Sample base class:
public class BaseController {
private int childType;
protected static final int CHILD_A = 1;
protected static final int CHILD_B = 2;
public BaseController(int aChildType) {
childType = aChildType;
}
protected void populateWidget(List li, int zipcode) {
switch (childType) {
case CHILD_A:
//Handle Child A
case CHILD_B:
//Handle Child B
}
}
}
The child implementation would look like this:
public class ChildA extends BaseController {
public ChildA () {
super(CHILD_A);
}
//The rest of your code goes here...
}
Note, that in the above example I am using integer constants for the sake of brevity. In the 21s century Java creating an Enum would be the preferred way to go.
We are using JPA Entities to get the database rows and then when we transfer that to the external, we want to use disconnected object (DTO) which are simple beans annotated with JAX-B.
We use a mapper and its code looks like this:
public BillDTO map(BillEntity source, BillDTO target) {
BeanUtils.copyProperties(source, target);
return target;
}
But when the code is running we get an error like this:
java.lang.IllegalArgumentException: argument type mismatch
Note this is the Spring implementation of the BeanUtils:
import org.springframework.beans.BeanUtils
And the naming of the properties are identical (with their getter/setter).
Anybody knows why the error happens?
And how to use a fast way instead just copying properties one by one?
This example working well. Here String property is copied to enum property:
Entity:
public class A {
private String valueFrom;
public String getValue() {
return valueFrom;
}
public void setValue(String value) {
this.valueFrom = value;
}
}
DTO (En is enumeration):
public class B {
private En valueTo;
public void setValue(String def) {
this.valueTo = En.valueOf(def);
}
public void setEnumValue(En enumVal) {
this.valueTo = enumVal;
}
}
As for your GitHub example, problem in class B in getter should be:
public String getValue()
Example:
public String getValue() {
return value.toString();
}
i have the object of this structure
public class Cleanse {
private Contacts contact;
private List<Contacts> dnb360;
private String operation;
public String getOperation() {
return operation;
}
public void setOperation(String operation) {
this.operation = operation;
}
public Contacts getContact() {
return contact;
}
public void setContact(Contacts contact) {
this.contact = contact;
}
public List<Contacts> getDnb360() {
return dnb360;
}
public void setDnb360(List<Contacts> dnb360) {
this.dnb360 = dnb360;
}
In jsp i am getting the list of cleanse objects
can anyone tell me how to bind this list and get the submitted value in contoller
Binding in the jsp is not that difficult. If you're using core jstl tags (which you should), you only have to iterate over the lists entries and do with them what ever you want:
<c:forEach items="${dnb360}" var="s">
<b>${s}</b>
</c:forEach>
Since you're talking about 'submitting', I guess you#re trying to set up a form here, which makes it even easier (use spring's form tag here) <%# taglib uri="http://www.springframework.org/tags/form" prefix="form" %>:
<form:checkboxes items="${dnb360}" path="dnb360" />
For retrieving such a bound value and convert back to your object, I'd suggest using the #InitBinder annotation. You annotate a method with that and define how to bind a String value back to your object when retrieving this String through a method which has #ModelAttribute("dnb360") dnb360,...
#InitBinder
public void initBinder(WebDataBinder binder) {
//custom editor to bind Institution by name
binder.registerCustomEditor(Contacts.class, new PropertyEditorSupport() {
#Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(xxx); //most likely obtained through database call
}
});
}
If you need more help, please feel free to ask.