Spring MVC usage of form:radiobuttons to bind data (whole object not only one value) - forms

I have a problem with sending data to backend by POST from my JSP using construction:
<form:radiobuttons path="pvClCategory" items="${categoryList}" itemLabel="clcaCategory"/>
After sumbit my Jboss (version 5) displays:
HTTP Status 400 - description: The request sent by the client was syntactically incorrect ().
I don't know how to see sent request. When I am using construction below everything works fine but I do not want to bind only one value from object, but whole object:
<form:radiobuttons path="pvClCategory.clcaCategory" items="${categoryList}" itemValue="clcaCategory" itemLabel="clcaCategory"/>
So, the first form construction (with binding whole object) cause me a problem. Form is initialised properly it means that radio options are correctly displayed and correct value is selected on jsp site. But problem appears when I want to submit the form, i receive error (HTTP Status 400 request sent by client was syntactically incorrect). Any idea why? What I am doing wrong?
My code:
I have one entity class Violation with field named pvClCategory type of ClCategory
#Entity
#Table(name="PV_VIOLATIONS")
public class Violation implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="VIOL_ID")
private Long id;
#ManyToOne
#JoinColumn(name="VIOL_CLCA_ID")
private ClCategory pvClCategory;
/* others fields and getters/setters */
}
My ClCategory entity class which is needed to bind it to Violation.pvClCategory field:
#Entity
#Table(name="PV_CL_CATEGORIES")
#Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class ClCategory implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="CLCA_ID")
private long clcaId;
#Column(name="CLCA_CATEGORY")
private String clcaCategory;
#OneToMany(mappedBy="pvClCategory")
private List<Violation> pvViolations;
public ClCategory() {
}
public long getClcaId() {
return this.clcaId;
}
public void setClcaId(long clcaId) {
this.clcaId = clcaId;
}
public String getClcaCategory() {
return this.clcaCategory;
}
public void setClcaCategory(String clcaCategory) {
this.clcaCategory = clcaCategory;
}
public List<Violation> getPvViolations() {
return this.pvViolations;
}
public void setPvViolations(List<Violation> pvViolations) {
this.pvViolations = pvViolations;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (clcaId ^ (clcaId >>> 32));
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ClCategory other = (ClCategory) obj;
if (clcaId != other.clcaId)
return false;
return true;
}
}
This Violation class is used in my form in commandName property as follows (file name: v.jsp):
<form:form action="../update" method="post" commandName="violation">
<form:radiobuttons path="pvClCategory" items="${categoryList}" itemLabel="clcaCategory"/>
<!-- other fields -->
<button type="submit" value="Apply Changes" class="button-default" >Apply</button>
</form:form>
My controller:
#Controller
#RequestMapping("/viol")
public class ViolationController {
#RequestMapping("edit/{violationId}")
public String edit(#PathVariable("violationId") long id, Map<String, Object> map) {
Violation violation = violationService.getViolation(id);
map.put("violation",violation);
map.put("categoryList", adminService.listCategories());
return "v";
}
}
As I understand in the construction form:radiobuttons path="" items="" the path is property for data binding so whole object from delivered list in items should be binded to it. I put in items List of my categories which are objects of type ClCategory. After submit error appears.
When i use form:radiobuttons path="pvClCategory.clcaCategory" items="${categoryList}" itemValue="clcaCategory" itemLabel="clcaCategory" to bind only String value from object in items (in both cases is used the same object list of type ClCategory) then the form is correctly submited but I do not want to bind only one value of objecte but whole object. Could you help me what am I doing wrong?

Related

JPA2.0 property access in spring rest data -- some getters not being called

I am still somewhat of a novice with Spring Boot and Spring Data Rest and hope someone out there with experience in Accessing by Property. Since I cannot change a database which stores types for Letters in an unnormalized fashion (delimited string in a varchar), I thought that I could leverage some logic in properties to overcome this. However I notice that when using property access, some of my getters are never called.
My Model code:
package ...
import ...
#Entity
#Table(name="letters", catalog="clovisdb")
#Access(AccessType.PROPERTY)
public class Letter {
public enum PhoneticType {
VOWEL, SHORT, LONG, COMMON;
public static boolean contains(String s) { ... }
}
public enum PositionType {
ALL, INITIAL, MEDIAL, FINAL;
public static boolean contains(String s) { ... }
}
public enum CaseType {
ALL, LOWER, UPPER;
public static boolean contains(String s) { ... }
}
private int id;
private String name;
private String translit;
private String present;
private List<PhoneticType> phoneticTypes;
private CaseType caseType;
private PositionType positionType;
#Id
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getTranslit() { return translit; }
public void setTranslit(String translit) { this.translit = translit; }
public String getPresent() { return present; }
public void setPresent(String present) { this.present = present; }
public String getTypes() {
StringBuilder sb = new StringBuilder(); //
if (phoneticTypes!=null) for (PhoneticType type : phoneticTypes) sb.append(" ").append(type.name());
if (caseType!=null) sb.append(" ").append(caseType.name());
if (positionType!=null) sb.append(" ").append(positionType.name());
return sb.substring( sb.length()>0?1:0 );
}
public void setTypes(String types) {
List<PhoneticType> phoneticTypes = new ArrayList<PhoneticType>();
CaseType caseType = null;
PositionType positionType = null;
for (String val : Arrays.asList(types.split(" "))) {
String canonicalVal = val.toUpperCase();
if (PhoneticType.contains(canonicalVal)) phoneticTypes.add(PhoneticType.valueOf(canonicalVal));
else if (CaseType.contains(canonicalVal)) caseType = CaseType.valueOf(canonicalVal);
else if (PositionType.contains(canonicalVal)) positionType = PositionType.valueOf(canonicalVal);
}
this.phoneticTypes = phoneticTypes;
this.caseType = (caseType==null)? CaseType.ALL : caseType;
this.positionType = (positionType==null)? PositionType.ALL : positionType;
}
#Override
public String toString() { .... }
}
My Repository/DAO code:
package ...
import ...
#RepositoryRestResource
public interface LetterRepository extends CrudRepository<Letter, Integer> {
List<Letter> findByTypesLike(#Param("types") String types);
}
Hitting this URI: http://mytestserver.com:8080/greekLetters/6
and setting breakpoints on all the getters and setters, I can see that the properties are called in this order:
setId
setName
setPresent
setTranslit
setTypes
(getId not called)
getName
getTranslit
getPresent
(getTypes not called !!)
The json returned for the URI above reflects all the getters called, and there are no errors
{
"name" : "alpha",
"translit" : "`A/",
"present" : "Ἄ",
"_links" : {
"self" : {
"href" : "http://mytestserver.com:8080/letters/6"
}
}
}
But why is my getTypes() not being called and my JSON object missing the “types” attribute? I note that the setter is called, which makes it even stranger to me.
Any help would be appreciated!
Thanks in advance
That's probably because you don't have a field types, so getTypes() isn't a proper getter. Try adding this to your entity
#Transient
private String types;
I don't know how the inner works, but it's possible that the class is first scanned for its fields, and then a getter is called for each field. And since you don't have types field, the getter isn't called. Setter getting called could be a feature but I wouldn't be surprised if it is a bug, because findByTypesLike should translate to find Letters whose types field is like <parameter>, and types is not a field.
Another thing you can try, is to annotate that getter with #JsonInclude. Jackson 2 annotations are supported in Spring versions 3.2+ (also backported to 3.1.2).

POJO information lost during RPC call (GWT)

I am having issues with RPC calls and GWT. Essentially, I have a Person class (common code between client and server) that is created in the client side web code, sent to the server code via an RPC call, and then saved to a DB (OrientDB). I have verified that the following work:
RPC call - I am able to send info to the server and retrieve info from the server
save to DB - have verified that a Person object is saved to the DB
Where I am having issues is the transfer of the POJO from the client to the server. I have verified that the POJO's properties are intact right before it is sent to the server, however, the object passed to the server contains null values for all properties. Essentially, the class is transferred but the information is not. It then saves to the DB, but obviously without any relevant information contained within it.
I will copy what I feel is relevant below, please let me know what else I can provide to make this problem easier to identify. Note these are still in a testing state, so mind the comments :)
Any idea why my POJO's information is being lost in translation?
Person object, followed by the abstract class it inherits from:
public class Person extends org.matesweb.shared.AbsPerson implements Serializable
{
#Id
private String id; // DON'T CREATE GETTER/SETTER FOR IT TO PREVENT THE CHANGING BY THE USER APPLICATION,
// UNLESS IT'S NEEDED
//sets new user details
public void setPerson(String fIrstName, String mIdInit, String lAstName, String email, String password)
{
firstName = fIrstName;
middleInitial = mIdInit;
lastName = lAstName;
}
/*getter and setter methods - required for every
* field due to restrictions imposed by OrientDB*/
public Object getId()
{
String tmp;
tmp = id.toString();
return tmp;
}
//end class
}
public class AbsPerson implements Serializable
{
String firstName;
String middleInitial;
String lastName;
// public sys.Login login;
public org.matesweb.shared.Group[] groups;
private org.matesweb.shared.Purchase[] purchases;
/*this method adds a new purchase to the purchases variable*/
/* public void addPurchase(float price, String description)
{
people.Purchase newPurchase = new people.Purchase(login, price, description);
}
*/
/*adds a person to a group by comparing the passed in group ID and PWD*/
public void addGroup(String groupID, String groupPWD)
{
//compare group ID with group PWD to add a user to the group
}
/*getter and setter methods - required for every
* field due to restrictions imposed by OrientDB*/
public String getFirstName()
{
return firstName;
}
public void setFirstName(String name)
{
firstName = name;
}
public String getMiddleInitial()
{
return middleInitial;
}
public void setMiddleInitial(String midInit)
{
middleInitial = midInit;
}
public String getLastName()
{
return lastName;
}
public void setLastName(String ln)
{
lastName = ln;
}
/*
public sys.Login getLogin()
{
return login;
}
public void setLogin(sys.Login log)
{
login = log;
}
*/
public org.matesweb.shared.Group[] getGroups()
{
return groups;
}
public void setGroups(org.matesweb.shared.Group[] gro)
{
groups = gro;
}
public org.matesweb.shared.Purchase[] getPurchases()
{
return purchases;
}
public void setPurchases(org.matesweb.shared.Purchase[] purch)
{
purchases = purch;
}
}
Service
package org.matesweb.client;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
import org.matesweb.shared.Person;
#RemoteServiceRelativePath("peopleService")
public interface PeopleService extends RemoteService {
//test services
String stringTest(String outgoingString);
Person getPerson(String persId);
//production services
String savePerson(Person p);
}
ServiceAsync
import com.google.gwt.user.client.rpc.AsyncCallback;
import org.matesweb.shared.Person;
public interface PeopleServiceAsync
{
//tests
void stringTest(String outgoingString, AsyncCallback<String> incomingString);
void getPerson(String persId, AsyncCallback<Person> retPerson);
//production services
void savePerson(Person p , AsyncCallback<String> st);
}
ServiceImpl call for this particular method:
//production calls
#Override
public String savePerson(Person p) {
String st = ioObj.saveObj(p);
if(st.equals("Success")){
return "Your information has been saved successfully!";
} else{
return "Something has gone wrong on our end... Sorry! Error:<br /> " + st;
}
}
and finally, the call itself
private static void savePerson(Person p)
{
// Initialize the service proxy.
if (peopleSvc == null) {
peopleSvc = GWT.create(PeopleService.class);
}
//resets status
st="";
// Set up the callback object.
AsyncCallback<String> callback = new AsyncCallback<String>() {
#Override
public void onFailure(Throwable caught) {
st = caught.getMessage();
Label stLabel= new Label(st);
personTable.setWidget(3,1,stLabel);
}
#Override
public void onSuccess(String result) {
st = result;
HTML stLabel= new HTML(st);
joinPanel.add(stLabel);
}
};
// Make the call to the people service.
peopleSvc.savePerson(p, callback);
}
I was able to fix this issue by implementing GWT's IsSerializable interface. I also removed the Serializable interface from the Person class and let it inherit IsSerializable from the abstract class it inherits from.

Struts 2 ModelDriven Action suporting both a list and individual items

I have inherited some struts2 REST-plugin based code, and the following construct puzzles me:
#Namespace("/merchants/{id}")
public class MerchantAction extends ActionSupport implements ModelDriven<Object> {
private Merchant merchant = new Merchant(); // A Model
private Iterable<Merchant> merchants; // A list of models
....
public HttpHeaders index() {
merchants = merchantService.findAllMerchants();
return new DefaultHttpHeaders("index");
}
#Override
public Object getModel() {
return (merchant != null ? merchant : merchants);
}
public void setId(String id) {
merchant = merchantService.findMerchant(id));
}
In other words, it seems to be toggling between returning a list and returning an individual item in the getModel() call. Is this kosher ? Looks a bit strange to me
I've considered your approach, but finally gave it up. IMO, it lost the advantage of strong typed action.
My solution is, creating a ViewModel for each action. In the view models, there can be the single model, the list of the model, and other items for pages usage, such as items for drop down list or radio buttons.
So the UserViewModel is like:
public class UserViewModel implements IViewModel<User> {
private User model;
private List<User> list;
public void setModel(User user) {
this.model = user;
}
public User getModel() {
return model;
}
public void setList(List<User> list) {
this.list = list;
}
public List<User> getList() {
return list;
}
}
And the actions are like:
public class UserController implements ModelDriven<UserViewModel> {
private int id;
private UserViewModel model = new UserViewModel();
public String index() {
return "success";
}
public String show() {
return "success";
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return this.id;
}
#Override
public UserViewModel getModel() {
return model;
}
}
But in this way, I still lose the shortcut way in jsp files. I should write long model.userName instead of short userName.
I'm still finding the best solution of it.

Spring List Binding

Thanks in advance for any help.
I have the following object association in my model:
public class Contract {
private Integer id;
private String name;
//getters/setters...
}
public class User {
....
private List<Contract> contracts;
....
}
Controller:
#RequestMapping(....)
public String getUser(#PathVariable Integer userId, Model model) {
....
model.addAttribute(userDao.findUser(userId));
model.addAttribute("contractsList", contractDao.findAllContracts());
....
}
#RequestMapping(....)
public String processUser(#ModelAttribute User user, Model model) {
....
//Create a copy of the user to update...
User userToUpdate = userDao.findUser(user.getId);
....
userToUpdate.setContracts(user.getContracts());
//set other properties...
userDao.updateUser(userToUpdate);
return "someSuccessView";
}
#InitBinder
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
binder.registerCustomEditor(Contract.class, new UserContractsPropertyEditor());
}
My PropertyEditor:
public class UserContractsPropertyEditor extends PropertyEditorSupport {
#Inject ContractDao contractDao;
#Override
public void setAsText(String text) throws IllegalArgumentException {
System.out.println("matching value: " + text);
if (text != "") {
Integer contractId = new Integer(text);
super.setValue(contractDao.findContract(contractId));
}
}
}
My JSP form:
<form:form commandName="user">
<%-- Other fields... --%>
<form:checkboxes items="${contractsList}"
path="contracts"
itemValue="id"
itemLabel="name" />
</form:form>
The form renders correctly. That is, the checkbox list of Contracts is generated and the correct ones are "checked." The problem is when I submit I get:
java.lang.IllegalArgumentException: 'items' must not be null
at org.springframework.util.Assert.notNull(Assert.java:112)
at org.springframework.web.servlet.tags.form.AbstractMultiCheckedElementTag.setItems(AbstractMultiCheckedElementTag.java:83)
at org.apache.jsp.WEB_002dINF.jsp._005fn.forms.user_jsp._jspx_meth_form_005fcheckboxes_005f0(user_jsp.java:1192)
....
The custom property editor seems to be doing its job and there are no null/empty strings being passed.
If the form and controller makes the conversion when viewing the form, why is it having trouble when processing the form? What am I missing here?
You need to ensure that a call to getContract() returns a List instance:
public List<Contract> getContracts() {
if (contracts == null) contracts = new ArrayList<Contract>();
return contracts;
}
Thanks for your response. I guess a fresh set of eyes first thing in the morning does the trick again.
Apparently, my custom property editor had no clue what to do with the id value I was passing in since it couldn't access my DAO/service. So, I had to change the constructor:
public class UserContractsPropertyEditor extends PropertyEditorSupport {
private ContractDao contractDao;
public UserContractsPropertyEditor(ContractDao contractDao) {
this.contractDao = contractDao;
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
Integer contractId = new Integer(text);
Contract contract = contractDao.findContract(contractId);
super.setValue(contract);
}
}
Then, modified the initBinder in my controller:
#Inject ContractDao contractDao;
....
#InitBinder
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
binder.registerCustomEditor(Contract.class, new UserContractsPropertyEditor(this.contractDao));
}
Maybe this will help someone else.

GWT with JPA and Gilead example

Can someone please provide me an example of GWT + JPA + Gilead, I can't seem to find anything on Google with this topic.
Thanks
Thanks Maksim,
I'm not using this in an EJB server but Tomcat. I understand the step you've pointed out above but not sure on how to do the next step which is to set up PersistentBeanManager and send my object over the wire.
Here is what I have thus far but I haven't got a chance to test if this works yet. If you see a problem with this let me know, thanks.
private HibernateJpaUtil gileadUtil = new HibernateJpaUtil();
private static final EntityManagerFactory factory =
Persistence.createEntityManagerFactory("MyPersistentUnit");
public MyServlet() {
gileadUtil.setEntityManagerFactory(factory);
PersistentBeanManager pbm = new PersistentBeanManager();
pbm.setPersistenceUtil(gileadUtil);
pbm.setProxyStore(new StatelessProxyStore());
setBeanManager(pbm);
Book book = new Book();
Book cloned = (Book) pbm.clone(book);
//send the cloned book over the wire
}
I tried to set up my project very similar and also ran into the hibernate exception. I figured out that when using JPA I need to initialize the HibernateJPAUtil with the EntityManagerFactory. When I did this it worked. This changes your first two lines of code to:
public class MyServiceImpl extends PersistentRemoteService implements MyService {
public MyServiceImpl() {
final EntityManagerFactory emf = Persistence.createEntityManagerFactory("MA");
final PersistentBeanManager persistentBeanManager = new PersistentBeanManager();
persistentBeanManager.setPersistenceUtil(new HibernateJpaUtil(emf)); // <- needs EMF here
persistentBeanManager.setProxyStore(new StatelessProxyStore());
setBeanManager(persistentBeanManager);
}
#Override // from MyService
public Stuff getStuff() {
// no need for clone/merge here, as Gilead's GWT PersistentRemoteService does this for us
...
return stuff;
}
}
Also I used net.sf.gilead.pojo.java5.legacy.LightEntity as base class for all my entities (note the java5.legacy package).
Entity:
//imports
#Entity
public class Book extends LightEntity implements Serializable {
private static final long serialVersionUID = 21L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
#Lob
private String description;
#ManyToMany(cascade = CascadeType.ALL)
private List<Author> author;
// Getters and setters
#Override
public int hashCode() {
int hash = 0;
hash += (getId() != null ? getId().hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Book)) {
return false;
}
Course other = (Book) object;
if ((this.getId() == null && other.getId() != null) || (this.getId() != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
}
The Book object looks same.
Then use it as regular EJB on your server and as regular DTO's on your client.
Don't forget to add Gilead's libraries to your project.
Hope this blog will help you.
http://zawoad.blogspot.com/2010/06/google-app-engine-jdo-and-gxtext-gwt.html
This is not a direct a example of what you want but the approach should be like this. We followed the same approach in our project with GWT+JPA+EJB. to send your object over the wire you need a Data Transfer Object (DTO). Convert this DTO to Entity object and do whatever you want to do.