October 4, 2007

Seam Page Parameter with cutom Validators and Converters

I recently wrote about User Friendly Paging with Seam in which I used the @RequestParameter annotation. However, Gavin King has suggest that Seam page parameter be used rather than @RequestParameter. One advantage of using page parameter is that Seam set the values after performing appropriate type conversion. More important, "the <param> declaration is bidirectional." I would suggest reading Seam Documentation for more information.

The Components

Now that page parameters to be used, there is no need for the @RequestParameter and @out annotations, however, you need to define setter methods instead.


@Local
public interface MemberListing {
void setMemberList();
void setStatus(String status);
void setPageNumber(Integer pn);
void setPageSize(Integer ps);
}
@Stateless
@Name("memberListingService")
public class MemberListingService implements MemberListing{

private String status;
private String pn = null;
private String ps = null;

public void setStatus(String st){
status = st;
}

public void setPageNumber(Integer pageNumber){
pn = pageNumber.toString();
}

public void setPageSize(Integer pageSize){
ps = pageSize.toString();
}
}


Declaring the Page Parameters

For each page parameter, you need to declare a param tag with name and value attributes. Optionally, you can specify converter and validator as well.


<page view-id="/members.xhtml" action="#{memberListingService.setMemberList}">
<param name="st" value="#{memberListingService.status}"/>
<param name="pn" value="#{memberListingService.pageNumber}"
validator="#{pageNumberValidator}"/>
<param name="ps" value="#{memberListingService.pageSize}"
converter="#{pageSizeConverter}"/>
</page>


Custom Validator and Converter

Seams provides the @Validator and @Converter annotations to define components as custom JSF validators and converters. What is amazing about Seam is that you do not need to register these custom validators and converters with JSF. Seam takes care of that for you :)

The reason I am using these components here is because I want to display a custom error message for each parameter. If the standard converters/validators were used then the same error message would be displayed for parameters with same type.


@Name("pageNumberValidator")
@BypassInterceptors
@Validator
public class PageNumberValidator implements javax.faces.validator.Validator {

@Logger
private Log log;
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
log.info("PageNumberValidator.validate called");
try {
Integer pageNumber = Integer.parseInt(value.toString());
if (pageNumber < 1) {
FacesMessage message = new FacesMessage();
message.setDetail("Invalid page number [less than 1]");
message.setSummary("The page number cannot be "+pageNumber);
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
} catch (NumberFormatException e) {
e.printStackTrace();
FacesMessage message = new FacesMessage();
message.setDetail("Invalid or missing page number");
message.setSummary("The page number has to be a valid number");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ConverterException(message);
}
}
}


The validator here is annotated with Seam @Validator annotation and at the same time implements the Validator interface from the JSF API. The name use specify in the @Name annotation is the one used in the param tag above. To have a custom error message you define an instance of FacesMessage and set its properties accordingly. Then you need to pass it as a parameter to the newly thrown instance of ValidatorException. I know you are saying, this is too much for a custom error message but this is the only way I was able to find.

It would have been much simpler if the param tag had validatorMessage and convertorMessage attributes. I don't know but it might not be possible due to limitations in the JSF API.

Here is the code for the converter:

@Name("pageSizeConverter")
@BypassInterceptors
@Converter
public class PageSizeConverter implements javax.faces.convert.Converter {

@Logger
private Log log;

public Object getAsObject(FacesContext context, UIComponent component,
String value) throws ConverterException {
log.info("PageSizeConverter.getAsObject called");
if(value == null || value.trim().length() == 0)
return null;
Integer i = null;
try {
i = new Integer(value);
} catch (NumberFormatException e) {
e.printStackTrace();
FacesMessage message = new FacesMessage();
message.setDetail("Invalid page size [String specified]");
message.setSummary("The page size has to be a valid number");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ConverterException(message);
}
return i.intValue();
}

public String getAsString(FacesContext context, UIComponent component,
Object value) throws ConverterException {
log.info("PageSizeConverter.getAsString called");
return value + "";
}
}


I totally forgot the screenshots


6 comments:

Anonymous said...

When using this method to create a list of objects what is the best or preferred method of making the items selectable, for example editing or viewing detail information on a given item?

Am I correct you would need to pass in some identifier to the next method or could you use an actionlistener / action to populate and setup the edit backing objects?

Anonymous said...

Hi dude,

I tried to implement ur paging example but facing some problem while customization. So, can u please send a code to me on shirin@promactinfo.co.in

Thanks in advance.

Shirin said...

Hi dude,

And in more is there any way that we can combine the sorting and paging and make to customize that one can easily configure it. And also it will auto bind with h:dataTable using some templete or custom control..

If we can do that thn it will more helpful to Seam Developer..

Thanks
Shirin Joshi..

Anonymous said...

i get an error using converters and validators

it's related with the class 'org.jboss.seam.navigation.Param'
in the method 'validateConvertedValue';

When i add one validator or a converter in the pages.xml file, it breaks in the line with the following code:

if ( invalidValues.length>0 ) {
...
}

invalidValues is an array; maybe seam should check if this array is not null in the condition.

Tomasz said...

Makes me wonder, how did you use @BypassInterceptors with @Logger? Seems impossible..

Anonymous said...

I Tried using the example ,But if I remove @RequestParameter annotation from the action level the parameter is not included and not visible in URl.Help me out for that as I cannot chk its validation now.