Struts2 validator - java

i m facing to a struts validator problem.
I has an object like this:
public class Reconstitution {
private List<FormuleReconstitution> formuleList;
...
...some other attributes and methods...
}
and :
public class FormuleReconstitution{
private Map<Long, ElementFormuleReconstitution> serieMap;
...
...some other attributes and methods...
}
and
public class ElementFormuleReconstitution {
private Double coefficient;
}
i would like to add a validator on coefficient in MyAction-validation.xml but i don't know how to do this. I know how to add a simple validator but not when i have sublist with submap :(
my generated html code look like this :
<input type="text" class="texte" id="reconstitutionformuleList0listeElementFormuleReconstitution0coefficient" tabindex="0" value="" name="reconstitution.formuleList[0].listeElementFormuleReconstitution[0].coefficient"/>
how can i add a validator on the field Coefficient ?

To validate an object inside a List, you need to use the Visitor Validator.
Then, to validate a double with min/max range, you need:
<field name="coefficient">
<field-validator type="double">
<param name="minInclusive">0.0</param>
<param name="maxInclusive">1000.0</param>
<message>
The coefficient must be between ${minInclusive} and ${maxInclusive}
</message>
</field-validator>
</field>
Finally, I suggest you to read how the INPUT result works (for both Validation and Conversion errors)

Related

MyBatis returns a List and doesn't want to return an object

I want to retrieve data from PostgreSQL database using MyBatis 3.
I wrote this mapper inperface:
package datamodel.gis.building;
public interface BuildingMapperBatis
{
// List of objects within rectangular box
public List<BuildingDbDto> getByBBox( #Param("lat1") BigDecimal lat1,
#Param("lon1") BigDecimal lon1,
#Param("lat2") BigDecimal lat2,
#Param("lon2") BigDecimal lon2 );
// Retrieve the object by id
public BuildingDbDto getById( #Param("id") Long id );
/// public List<BuildingDbDto> getById( #Param("id") Long id );
}
The class BuildingDbDto is a trivial DTO object with private fields, public getters, no setters and a constructor initializing all the fields.
A piece of the MyBatis XML configuration is:
<configuration>
<typeAliases>
<typeAlias alias="Building" type="datamodel.gis.building.BuildingDbDto" />
</typeAliases>
<environments default="default">...here is the connection specified...</environments>
<mappers>
<mapper resource="datamodel/gis/building/BuildingMapperBatis.xml" />
</mappers>
</configuration>
The XML configuration of the mapper in the file "/src/main/resources/datamodel/gis/building/BuildingMapperBatis.xml" is:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="datamodel.gis.building.BuildingMapperBatis">
<resultMap id="BuildingMap" type="Building" >
<id column="bld_id" property="id" />
<result column="bld_geo_latitude" property="latitude" />
<result column="bld_geo_longitude" property="longitude" />
<result column="bld_addr_settlement_name" property="addrSettlementName" />
</resultMap>
<select id="getByBBox" resultMap="BuildingMap">
SELECT bld_id, bld_geo_latitude, bld_geo_longitude, bld_addr_settlement_name
FROM get_buildings_in_bbox( #{lat1}, #{lon1}, #{lat2}, #{lon2} )
</select>
<!-- <select id="getById" resultMap="Building"> -->
<select id="getById" resultType="Building">
SELECT bld_id, bld_geo_latitude, bld_geo_longitude, bld_addr_settlement_name
FROM get_buildings_in_bbox( 0,0,90,90)
WHERE bld_id = #{id}
</select>
</mapper>
Then I perform the queries:
BuildingMapperBatis mapper = sessionFactory.openSession().getMapper( BuildingMapperBatis.class );
List<BuildingDbDto> found = mapper.getByBBox( lat1, lon1, lat2, lon2 );
BuildingDbDto dto = mapper.getById( id );
/*///
BuildingDbDto dto = mapper.getById( id ).get(0);
*/
The method getByBBox does work.
The method getById throws the exception at the line ".getById( id );":
"java.lang.IllegalAccessError: tried to access class datamodel.gis.building.BuildingDbDto from class com.sun.proxy.$Proxy0
at com.sun.proxy.$Proxy0.getById(Unknown Source)"
I tried both resultType="Building" and resultMap="Building", and the result is the same.
I tried to replace that lines of code to the commented out ones. I changed the result type of getById to a list and took the 0'th element of the list.
In this variant the code works correctly. But I don't like this conversion, because a search by id always returns only one (or none) object.
How can I make the method to return a single object, not a list?
I have found the solution of my problem, so I answer my qustion.
The cause of exception was that I didn't declare BuildingDbDto as public class. I wanted to hide the DTO from business layer and made it package-protected. My intention was to transform the DTO into a buisness entity (containing data getters and methods for a business-logic) within the same package, and make the buisness entity public.
Unfortunately, the magic inside MyBatis must have an access to returned value classes, so DTOs have to be public.

How to mapping a value to a text?

I query from database and I receive an integer list. Ex: 0, 1, 2
If I display the digits to browser, users will not understand the meaning of number.
So, I would like to map a digit to a string.
Ex: 0: Pending, 1: Active, 2: Inactive, so on.
File display.xhtml will have the source code follow as:
<!--display.xhtml-->
<t:dataTable id="itemTable" value="#{itemBrowser.itemList}" var="item">
<t:column>
<f:facet name="header">
<h:outputText value="Status" />
</f:facet>
<h:outputText value="#{itemStatusListReversedString[item.status]}" />
</t:column>
</t:dataTable>
<!--faces-config.xml-->
<managed-bean>
<managed-bean-name>itemStatusListReversedString</managed-bean-name>
<managed-bean-class>java.util.HashMap</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<map-entries>
<key-class>java.lang.String</key-class>
<map-entry>
<key>0</key>
<value>Inactive</value>
</map-entry>
<map-entry>
<key>1</key>
<value>Active</value>
</map-entry>
<map-entry>
<key>2</key>
<value>Pending</value>
</map-entry>
</map-entries>
</managed-bean>
But, there is nothing to output in browser. So, how can I fix this issue?
Thanks
I think the problem is in this line:
<h:outputText value="#{itemStatusListReversedString[item.status]}" />
You have to do something like this
<h:outputText value="#{item.stringValue}" />
and in the item class add something like this:
public String getStringValue(){
return itemStatusListReversedString.get(this.numberValue);
}
You must change the item class previously in the faces-config to inject a itemStatusListReversedString.
Example:
itemBrowser.itemList is a List of a objects of MyClass:
public class MyClass{
//The necessary stuff
private Integer valueFromDB; //0, 1, 2...
private Map<Integer, String> itemStatusListReversedString; //The map you configured in the faces-config.xml
//More stuff
public String getStringValue(){
return itemStatusListReversedString.get(this.valueFromDB);
}
}
In the faces-config.xml you configure MyClass this way:
<bean id="myClassInstance"
class="package.MyClass" scope="request">
<property name="itemStatusListReversedString" ref="itemStatusListReversedString"></property>
</bean>
When creating new instances of MyClass use this approach instead of creating them using new MyClass():
WebApplicationContext webApplicationContext = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
MyClass bean = (MyClass)webApplicationContext.getBean("myClassInstance");
Use enum then work with ordinal() for the numbers and values() for the text like:
YourEnum.values()[ordinal]

Getting a GET request param into an #ViewScoped bean

I have a (request-scoped) list from which the user may select a "PQ" (list of links). When clicked or otherwise entered into the browser the main page for each PQ shall be displayed. Each PQ's page is of the form
http://localhost:8080/projectname/main.jsf?id=2
Here's the PQ bean first:
#Named
#ViewScoped
public class PqHome implements Serializable
{
#PersistenceContext(unitName="...")
private EntityManager em;
private Integer id;
private PQ instance;
#PostConstruct
public void init()
{
System.out.println("ID is " + id); // ID from URL param
instance = em.find(PQ.class, id);
}
public Integer getId()
{
return id;
}
public void setId(Integer id)
{
this.id = id;
}
public PQ getInstance()
{
return instance;
}
}
Here's the main.xhtml:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
...>
<ui:define name="metadata">
<f:metadata>
<f:viewParam name="id" value="#{pqHome.id}">
<f:convertNumber integerOnly="#{true}" />
</f:viewParam>
<!--f:event type="preRenderView" listener="#{pqHome.init}" /-->
</f:metadata>
</ui:define>
<ui:define name="title">
<h:outputText value="Main" />
</ui:define>
...
</ui:composition>
Any time I select or otherwise refresh the page/URL I get a NullPointerException from the EntityManager:
org.jboss.weld.exceptions.WeldException: WELD-000049 Unable to invoke [method] #PostConstruct public de.mycomp.myproj.beans.PqHome.init() on de.mycomp.myproj.beans.PqHome#4f0ea68f
at org.jboss.weld.bean.AbstractClassBean.defaultPostConstruct(AbstractClassBean.java:595)
...
Caused by: java.lang.IllegalArgumentException: id to load is required for loading
at org.hibernate.event.spi.LoadEvent.<init>(LoadEvent.java:87)
at org.hibernate.event.spi.LoadEvent.<init>(LoadEvent.java:59)
at org.hibernate.internal.SessionImpl.get(SessionImpl.java:961)
at org.hibernate.internal.SessionImpl.get(SessionImpl.java:957)
at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:787)
at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:762)
at org.jboss.as.jpa.container.AbstractEntityManager.find(AbstractEntityManager.java:221)
at de.mycomp.myproj.beans.PqHome.init(PqHome.java:47)
... 56 more
[Line 47 is em.find(...)]
The line
<f:event type="preRenderView" listener="#{pqHome.init}" />
doesn't make things any better. I'm pretty desparate now.
How do you get URL GET request params into an #ViewScoped bean?
Note: I bet it's not a trivial thing to do. Chances are I'm doing something wrong here conceptually, so any tips on how to improve are welcome. I felt that I needed to choose #ViewScoped because there will be more complex AJAX-based GUI on that page which I'd really like to keep accessible via URL GET params.
Thanks
There is a better way to get id from url. Just use it in #PostConstruct init() method to get "id" from url:
FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("id");
You can still use ViewScoped and #PostConstruct.
The #PostConstruct is invoked directly after bean's construction and all dependency injection (such as #PersistenceContext, #EJB, #ManagedProperty, #Inject, etc..etc..).
The <f:viewParam> sets its value during the update model values phase, which is far after (post)construction of the bean. So inside the #PostConstruct the <f:viewParam> value is simply not yet been set. It'll be still null at that point.
You're close with <f:event type="preRenderView">, but you have to remove the #PostConstruct annotation.
So:
<f:viewParam name="pq" value="#{pqHome.id}">
<f:convertNumber integerOnly="#{true}" />
</f:viewParam>
<f:event type="preRenderView" listener="#{pqHome.init}" />
with
private Integer id;
public void init() {
instance = em.find(PQ.class, id);
}
Unrelated to the concrete problem, I'd suggest to use a Converter for this instead. See also Communication in JSF 2.0 - Converting and validating GET request parameters.
Also the combination #Named #ViewScoped won't work as intended. The JSF-specific #ViewScoped works in combination with JSF-specific #ManagedBean only. Your CDI-specific #Named will behave like #RequestScoped this way. Either use #ManagedBean instead of #Named or use CDI-specific #ConversationScoped instead of #ViewScoped.

Can I have the same mapping value with different param in a different Spring controller?

Is there any way to accomplish something like this:
I have a form used for navigation :
<form action="mapping.do">
<input type="submit" value="menuOption01" />
<input type="submit" value="menuOption02" />
</form>
The PageController class is too big and has too many dependancies, I need to add another menu option but don't want to add to the complexity. I'd like to have a method in another controller which handles the new menu option.
Trying this gives me a Spring configutation error (There is already handler mapped):
#Controller
#SessionAttributes(types = { Entity.class })
class PageController {
#RequestMapping(params = "menuOption01", value = "mapping.do")
public String viewPage(#ModelAttribute final Entity entity) {
...
return "view";
}
... // another 5000 lines of code
}
#Controller
class OtherController {
#RequestMapping(params = "menuOption02", value = "mapping.do")
public String viewOtherPage(#ModelAttribute final Entity entity) {
...
return "otherview";
}
}
I faced a similar situation so we made the following default handler for these types of methods:
#RequestMapping(method = RequestMethod.POST, params = SIDE_TAB, value = "sideMenuController.xhtml")
public ModelAndView changeSelectedTab(#RequestParam(SIDE_TAB) String sideTab) {
return new ModelAndView("redirect:/location/" + Utils.toCamelCase(sideTab) + ".xhtml");
}
Our pages then had the following:
<input type='submit' name='side-tab' value='$value' />
This of course meant that we had to have a naming standard for the files themselves, but that was quite easy to ensure happened (i.e. "Event History" would go to eventHistory.xhtml, "Create New Entity" would go to "createNewEntity.xhtml", etc....)
Not directly, but:
You can include that param in the url: value=/mapping/parameter/ and /mapping/otherparameter. (The .do extension is a bit obsolete btw)
Use an if clause - pass the two params with #RequestParam("param", required=false) String param and use if (param != null) viewPage();
You can have one method that takes HttpServletRequest and checks whether a parameter with a given name exists (using request.getParameter("foo") != null)
You can use parameter per method mapping. See my question and answer:
#RequestMapping with "params" on same URL in different classes cause "IllegalStateException: Cannot map handler" in JUnit with SpringJUnit4ClassRunner
https://stackoverflow.com/a/14563228/173149
Just use these classes:
<bean name="handlerMapping"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<bean name="handlerAdapter"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

How to bind a List using #ModelAttribute to controller from a JSP form

I am trying to get a list of selected candidates to my controller using #modelAttribute with their respective id and blurb. I am able to bring one Candidate correctly but i don't know how to bring a list of candidates thru... I tried to add List<> as i have shown below, but i get
ERROR -
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/panel-requests] threw exception [Request processing failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [java.util.List]: Specified class is an interface] with root cause
org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [java.util.List]: Specified class is an interface
JSP -
<form:form modelAttribute="candidateAddAttribute"
action="/panel-requests/requests/${panelRequestForId.id}/empl" method="post">
<c:forEach items="${candidates}" var="candidates">
<select name="employee" id="employee" disabled="disabled">
<option value="default" selected="selected">${candidates.candidateName}</option>
</select>
<textarea rows="3" cols="40" id="candidateBlurb" name="candidateBlurb"
disabled="disabled">${candidates.candidateBlurb}</textarea>
<textarea rows="2" cols="20" id="candidateCV" name="candidateCV"
disabled="disabled">${candidates.candidateCV}</textarea>
</c:forEach>
<div id="candidateDiv" id="candidateDiv">
<select name="employee" id="employee">
<option value="default" selected="selected">Select Employee</option>
<c:forEach items="${employees}" var="employee">
<option value="${employee.id}" id="${employee.id}">
${employee.employeeName}- ${employee.employeeCV}<
/option>
</c:forEach>
</select>
<textarea rows="3" cols="40" id="candidateBlurb"
name="candidateBlurb">BLURB</textarea>
<div id="employeeCv"></div>
<input type="submit" value="Add Candidate" />
</div>
</form:form>
The above form at first displays list of employee and when user selects employee, enters blurb and hits add candidate button, i take data to controller.
Controller:
#RequestMapping(value = "{id}/empl", method = RequestMethod.POST)
public String getEmployeeDetails(
#ModelAttribute("candidateAddAttribute") #Valid List<Candidate> candidates,
BindingResult result, #PathVariable("id") int requestId, Model model) {
//implementation goes here
}
How do I implement List in this case? Thanks in advance.
EDITED PART
I tried sending 2 candidate's details, firebug sends it correctly like -
Parameters
candidateBlurb BLURB sar
candidateBlurb BLURB dann
employee 1
employee 2
so it can be a problem in initBinder that i ma using,
binder.registerCustomEditor(Employee.class,
new PropertyEditorSupport() {
public String getAsText() {
return Long.toString(((Employee) getValue()).getId());
}
public void setAsText(final String text) {
Employee employee = Employee.findById(Integer
.parseInt(text));
setValue(employee);
}
});
which only takes 1 employee detail at a time. Is that a problem here???
Create a POJO class which contains the List as a property, such as
public class EmployeeForm {
private List<Candidate> candidates;
public List<Candidate> getCandidates() { ... }
public void setCandidates(List<Candidates>) { ... }
}
and use this in your #RequestMapping method's signature rather than a List<Candidate> directly.
The error message you get is Spring complaining that it doesn't know how to instantiate a List interface, in order for Spring to bind the request parameters in the form to it. You want to provide a simple form/command class to data-bind the form parameters to, Spring knows how to handle a list from there.
you can try this :
List<Candidate> candidates = ListUtils.lazyList(new ArrayList<Candidate>(),FactoryUtils.instantiateFactory(Candidate.class));
I've run into this same issue, half of the fields are binding correctly and half aren't. I noticed that the half that are not binding correctly (thus giving me NULL in the controller) were wrapped in a DIV.
When I moved these fields outside the DIV, bingo, they were bound properly in the controller.
I see that you have some fields wrapped in a DIV too, I'd recommend moving them out and seeing if they become visible to your controller.

Categories