Spring StringTrimmerEditor trim all fields except password field - spring-mvc

In registered a StringTrimmerEditor in a Spring controller.
Of course when I now enter a password starting or ending with whitespace it is trimmed as well. But an user doesn't recognize this trimming.
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String.class, new StringTrimmerEditor(false));
}
So is it possible to 'exclude' the password field from trimming.
Should I register a StringTrimmerEditor for each field or just declare password as char[]?

Although this is an old question, if you still get tripped on this then here is how I solved this.
Say we have this form src/main/webapp/view/greet-form.html (I am using Thymeleaf)
<form action="/something" th:attr="action=#{/greet}" method="post" th:object="${student}">
<p><strong>Enter your first name</strong></p>
<p><input type="text" th:field="*{firstName}" th:value="*{firstName}"> <span class="error">*</span><br />
<span class="error_message" th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}"></span></p>
<p><strong>Enter your secret code</strong></p>
<p><input type="text" th:field="*{secretCode}" th:value="*{secretCode}"> <span class="error">*</span><br />
<span class="error_message" th:if="${#fields.hasErrors('secretCode')}" th:errors="*{secretCode}"></span></p>
<p><input type="submit" value="Submit"></p>
</form>
When the form is submitted, we want Spring to trim the value of firstName but leave secretCode as it is.
This is our form backing class, defined in src/main/java/Student.java. The trick is not to use String type for secretCode but some other custom type. This way, StringTrimmerEditor will not be used for the secretCode and the data will not be trimmed.
public class Student {
private String firstName;
private SecretString secretCode;
public Student() {
//
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public SecretString getSecretCode() {
return secretCode;
}
public void setSecretCode(SecretString secretCode) {
this.secretCode = secretCode;
}
}
Here is the definition of SecretString in src/main/java/SecretString.java
public class SecretString {
private String secret;
public SecretString() {
secret = "";
}
public SecretString(String secret) {
//mandatory null check
secret = (secret == null)? "" : secret;
this.secret = secret;
}
#Override
public String toString() {
return secret;
}
}
But now Spring will complain about not being able to convert String to SecretString. This can be solved with a custom property editor or a custom converter (if you are using Spring 3+). I used a custom converter like this.
First define the code in src/main/java/SecretStringConverter.java
import org.springframework.core.convert.converter.Converter;
public class SecretStringConverter implements Converter<String, SecretString> {
#Override
public SecretString convert(String source) {
return new SecretString(source);
}
}
Then register our converter class with a conversion-service factory bean (I am using src/main/webapp/WEB-INF/spring-mvc-demo-servlet.xml)
<context:component-scan base-package="your package" />
<mvc:annotation-driven conversion-service="app_conversion_service"/>
<bean id="app_conversion_service" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="SecretStringConverter"></bean>
</list>
</property>
</bean>

Set password as disallowed field
binder.setDisallowedFields("password");

Related

Getting null values in Spring MVC controller when submitting data from the jsp

I have a jsp form with an input box, a domain object with get/set methods, and a controller. When I submit the form I get null values in the controller. The "set" method is never being called in the domain object when i submit the form but the object itself is being called.
Order.jsp
<portlet:defineObjects />
<portlet:actionURL portletMode="view" var="createNewOrderURL">
<portlet:param name="action" value="createNewOrder" />
</portlet:actionURL>
<div>
<form:form name="form" method="post" commandName="refOrder" action="${createNewOrderURL}" id="createOrder">
TestName : ${refOrder.name}<br/> <!-- here I get the correct value to display -->
<form:input path="referenceNum" />
<input type="submit" value="Submit" />
</form:form>
</div>
Order.java
public class Order {
private String name = "Name1";
private String referenceNum;
public Order(){
System.out.println("Inside Order.java");
System.out.println(getReferenceNum());
}
public Order(String name, String referenceNum) {
this.name = name;
this.referenceNum = referenceNum;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getReferenceNum() {
return referenceNum;
}
public void setReferenceNum(String referenceNum) {
this.referenceNum = referenceNum;
}
SalesOrderController.java
#RenderMapping
public String defaultRender(RenderRequest request, RenderResponse response, Model model) throws SQLException, NamingException{
model.addAttribute("refOrder",new Order());
return "SalesOrderEntry";
}
#ActionMapping(params={"action=createNewOrder"})
public void addNewOrder(ActionRequest request, ActionResponse response, #ModelAttribute("refOrder") Order order)throws NamingException, SQLException{
System.out.println("Inside addNewOrder method");
System.out.println("New Order is --> "+order.toString());
System.out.println("RefNum - "+order.getReferenceNum());
System.out.println("request.getParameter is "+request.getParameter("referenceNum"));
}
I get null for all the print statements in the controller. Have been trying to fix this for two days now and I can't find what's wrong. Would really appreciate if someone can help me get this to work.
Do you have the following in your src/main/webapp/WEB-INF/liferay-portlet.xml descriptor?
<requires-namespaced-parameters>false</requires-namespaced-parameters>
Also, you might want to take a look at the PortletMVC4Spring project, which is the successor to Spring Portlet MVC. The GitHub project contains archetypes that work in Apache Pluto and Liferay Portal. The requires-namespaced-parameters config option is conveniently set in the archetypes.

Spring MVC, Binding, multi-select options following validation

Thanks in advance for any help.
I have a form that is being validated with JSR 303. After validation fails, the controller returns the form, shows validation errors, and renders the form with the original values. This works fine with all types of form elements except the mutli-select element.
The command object:
public class TaskOrder implements Serializable {
private static final long serialVersionUID = 1L;
...
#XmlTransient
#ManyToMany
#<OtherJPAAnnotations...>
private List<Contractor> subcontractors;
...
}
Contractor class:
public class Contractor implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#<OtherJPAAnnotations...>
private Integer id;
#<OtherJPAAnnotations...>
private String name;
}
Controller:
#RequestMapping(value="processingPath", method=RequestMethod.POST)
public String createNewTaskOrder(#Valid #ModelAttribute TaskOrder taskOrder,
BindingResult result,
Model model) {
...
if (!result.hasErrors()) {
//No binding errors, lots of processing...
else {
model.addAllAttributes(result.getModel());
model.addAttribute(taskOrder);
model.addAttribute("subs", myDAOInstance.getSubs());
return this.setupNewTaskOrder(model);
}
}
#RequestMapping("getFormPath")
public String setupNewTaskOrder(Model model) {
if (!model.containsAttribute("taskOrder")) {
TaskOrder taskOrder = new TaskOrder();
taskOrder.setId(0);
model.addAttribute(taskOrder);
}
return "_n/admin/taskOrder/new";
}
The form:
<form:form commandName="taskOrder" action="processPath">
...
<form:select path="subcontractors">
<form:options items="${subs}" itemValue="id" itemLabel="name"/>
</form:select>
...
</form>
When I open an existing "TaskOrder" with the same form, the values are selected in the "subcontractors" multi-select.
However, trying to add a new "TaskOrder", when it returns from validation the values aren't selected. I know that the selected values are being attached to the model and returned, but just not being selected in the select element.
Thanks for any help!
Thanks for the help. Biju...you were correct!
For those who may stumble across this issue, I added the "EqualsUtil" class described here to my project:
http://www.javapractices.com/topic/TopicAction.do?Id=17
Then added the following method to my Contractor class:
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Contractor)) return false;
Contractor c = (Contractor) o;
//Here I ignore the other properties since "id" and "name" are what
//I'm primarily concerned with...
return EqualsUtil.areEqual(this.name, c.name) &&
EqualsUtil.areEqual(this.id, c.id);
}

Using beans in servlets

I has a jsp page (index.jsp) with a form with two text fileds username and password like.
<form action="MyClass">
<input type="text" name="username" id="username" />
<input type="password" name="password" id="password" />
<input type="submit" />
</form>
On form submition i am invocking a servlet. I know that we can get the entered username and password values by using request methods,
request.getParameter("username");
request.getParameter("password");
But i don't want to use them , instead i want to store these values in a bean called BeanClass and i want to retrieve values from the bean in the sevlet. How can i get it??
You have to use <jsp:useBean/> action to instantiate the BeanClass with request or session scope in JSP.
Sample - EmpServlet.java
package com.me;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class EmpServlet extends HttpServlet {
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter pw=response.getWriter();
Emp emp=(Emp)request.getAttribute("emp");
pw.print(emp);
}
}
Emp.java : Emp bean
package com.me;
public class Emp {
private int age;
private String name;
public Emp() {
name="";
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean valid()
{
return age!=0 && name.length()!=0;
}
#Override
public String toString() {
return "Emp{" + "age=" + age + ", name=" + name + '}';
}
}
emp.jsp (view)
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<jsp:useBean id="emp" class="com.me.Emp" scope="request">
<jsp:setProperty name="emp" property="*"/>
</jsp:useBean>
<c:if test="${emp.valid()}">
<jsp:forward page="emp"/>
</c:if>
<form method="post" action="emp.jsp">
<br/><input type="text" name="age"/>
<br/><input type="text" name="name"/>
<br/><input type="submit"/>
</form>
web.xml
<servlet>
<servlet-name>EmpServlet</servlet-name>
<servlet-class>com.me.EmpServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>EmpServlet</servlet-name>
<url-pattern>/emp</url-pattern>
</servlet-mapping>
To the point, you're looking for a MVC framework like JSF or Spring MVC. With JSF it'll look something like this:
<h:form>
<h:inputText value="#{bean.username}" required="true" />
<h:inputSecret value="#{bean.password}" required="true" />
<h:commandButton value="submit" action="#{bean.submit}" />
<h:messages />
</h:form>
with
#ManagedBean
#RequestScoped
public class Bean {
private String username;
private String password;
public void submit() {
// Do here your job.
}
// Add/generate getters and setters.
}
That's all. No need for a servlet.
If you really want to do it the low level servlet way, you'd need to populate the bean yourself. This can be convenienced with Apache Commons BeanUtils to save boilerplate code.
Bean bean = new Bean();
BeanUtils.populate(bean, request.getParameterMap());
request.setAttribute("bean", bean);
// ...
The <jsp:useBean> does not allow for the MVC approach, it's more a MV. You have to mingle the conversion/validation into model and control the request/response inside the view, tasks which a controller should do. MVC frameworks offer you a controller which takes all this nasty boilerplate tasks from your hands.

Spring3 - #Autowired

This is my applicationContext.xml
<bean id="JdbcUserDao" class="controller.User.JdbcUserDao">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="org.apache.derby.jdbc.ClientDriver"
p:url="jdbc:derby://localhost:1527/TodoDb"
p:username="root"
p:password="root" />
This is my implDao class :
#Repository
public class JdbcUserDao implements IUserDao {
private JdbcTemplate jt;
#Autowired
private DataSource dataSource;
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
jt = new JdbcTemplate(this.dataSource);
}
public JdbcTemplate getJt() {
return jt;
}
public void setJt(JdbcTemplate jt) {
this.jt = jt;
}
#Override
public List<User> getUsers(final String username, final String password) {
List<User> users = this.jt.query("SELECT username, password FROM USERS",
new RowMapper<User>() {
#Override
public User mapRow(ResultSet rs, int i) throws SQLException {
User user = new User();
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
return user;
}
});
return users;
}
}
Problems:
this.dataSource available when it sets the dataSource through #Autowired like the configs in xml
when I use dataSource in getUsers, it become null ?
Questions:
How can I get this works ?
I'm new to spring3 so I really need your help.
In order to use autowiring, you need to add the following to your xml file configuration.
<context:annotation-config />
If it doesn't help then please add
<context:component-scan base-package="org.springframework.jdbc.datasource" />
Try adding the AutowiredPostProcessor to the config
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor">
</bean>
You could try adding the autowire to the set method instead of the property.
you need to import the class which you are doing autowired without access modifiers in repository class file
com.<your project>.controller.User.JdbcUserDao
and spring annotation
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.repository.CrudRepository;
#Repository
public class JdbcUserDao implements IUserDao {
#Autowired
DataSource dataSource;

spring mvc bind into 2 objects that have identical fields

I submit a form, lets say this form contains
<input name="address" ..>
and
<input name="billingAddress" ..>
i have 2 objects to which i need to bind to:
class Address {
String address;
..
}
class BillingAddress {
String address;
..
}
obviously billingAddress wont bind to address in BillingAddress without some magic.
lets say i have multiple identical fields in both Address and BillingAddress but on the form i prefix the billing inputs with billing, ie billingFirstName, billingLastName etc.
is there some elegant way i can bind to BillingAddress that i can reuse for similar problems?
(or is there a better way to solve this then what i have come up with?)
If you wand to use more than one ModelAttribute, you have to create a wrapper object, which holds an instance of each ModelAttribute. In your case I would create a wrapper object called "FormModel" which holds an instance of Address and an instance of a BillingAddress.
class FormModel {
private Address address;
private BillingAddress billingAddress;
// Getters and Setters
}
Now use FormModel as your ModelAttribute.
In your Form you can define your input-elements like:
<input name="address.address" ..>
<input name="billingAddress.address" ..>
Controller:
#RequestMapping(value = "/save", method = RequestMethod.POST)
public String save(Model model, #ModelAttribute() FormModel formModel) {
// process formModel.getAddress()
// process formModel.getBillingAddress()
return "redirect:home";
}
If you use custom validators for Address and BillingAddress, you also have to create a FormModelValidator that calls the AddressValidator and BillingAddressValidator:
public class FormModelValidator implements Validator {
private final AddressValidator addressValidator;
private final BillingAddressValidator billingAddressValidator;
public FormModelValidator(AddressValidator addressValidator,
BillingAddressValidator billingAddressValidator) {
this.addressValidator = addressValidator;
this.billingAddressValidator = billingAddressValidator;
}
public boolean supports(Class<?> clazz) {
return FormModel.class.equals(clazz);
}
public void validate(Object target, Errors errors) {
FormModel formModel = (FormModel) target;
try {
errors.pushNestedPath("address");
ValidationUtils.invokeValidator(this.addressValidator,
formModel.getAddress(), errors);
} finally {
errors.popNestedPath();
}
try {
errors.pushNestedPath("billingAddress");
ValidationUtils.invokeValidator(this.billingAddressValidator,
formModel.getBillingAddress(), errors);
} finally {
errors.popNestedPath();
}
}
}

Resources