If 'searching of tabular list' is present in a number of pages in the application, a reusable search component can be used. Search component contains, a combo box, listing items, populated from the DTO class fields, using reflection.
To restrict, which fields to be listed, and to show, custom labels for
each filed, a custom annotation class is created, and that annotation is
applied in the DTO class.
1. Need to add only the "tiles:insertTemplate" on all pages, where the search component is required, and pass the className as an attribute and its fields will be listed in the Search by item combo.
<tiles:insertTemplate template="/WEB-INF/jsp/searchComponent.jsp">
<tiles:putAttribute name="className" type="string" cascade="true" value="com.rocky.dto.Customer"></tiles:putAttribute>
</tiles:insertTemplate>
2. Create a reusable search component jsp
searchComponent.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles"%>
<tiles:useAttribute id="className" name="className" classname="java.lang.String" />
<jsp:useBean id = "searcItemsPopulatorBean" class="com.rocky.common.SearcItemsPopulatorBean" >
<jsp:setProperty name="searcItemsPopulatorBean" property="entityClassName" value="${className}"/>
</jsp:useBean>
<table width="80%" >
<tr>
<td width="15%">Search by item:</td>
<td align="left" width="20%">
<select name="searchByItem" >
<c:forEach items="${searcItemsPopulatorBean.searchItems}" var="item">
<option value="${item.key}">
<c:out value="${item.value}"/>
</option>
</c:forEach>
</select>
</td>
<td width="15%">Search criteria:</td>
<td width="20%" align="left">
<select name="searchCriteria">
<option value="startsWith">Starts with</option>
<option value="endsWith">Ends with</option>
<option value="contains">Contains</option>
<option value="exact">Exact</option>
</select>
</td>
<td width="15%">
<input type="text" name="searchText"/>
</td>
<td width="15%">
<input type="submit" value="Search"/>
</td>
</tr>
</table>
3. Define a Bean class to include in the searchComponent.jsp
SearchItemsPopulatorBean.java
package com.rocky.common;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class SearcItemsPopulatorBean {
private String entityClassName;
private Map<String, String> searchItems;
public SearcItemsPopulatorBean() {
this.searchItems = new HashMap<String, String>();
}
public String getEntityClassName() {
return entityClassName;
}
public void setEntityClassName(String entityClassName) {
this.entityClassName = entityClassName;
Field fieldArray[] = null;
try {
fieldArray = Class.forName(entityClassName).getDeclaredFields();
} catch (SecurityException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if (fieldArray != null) {
for (int i = 0; i < fieldArray.length; i++) {
ColumnHeaderSpecifier columnHeaderSpecifier = fieldArray[i]
.getAnnotation(ColumnHeaderSpecifier.class);
if (columnHeaderSpecifier.isSearchable()) {
searchItems.put(fieldArray[i].getName(),
columnHeaderSpecifier.columnHeader());
}
}
}
}
public Map<String, String> getSearchItems() {
return searchItems;
}
}
4. Define a custom annotation class ColumnHeaderSpecifier.java
package com.rocky.common;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ColumnHeaderSpecifier {
boolean isSearchable() default true;
String columnHeader() default "";
}
5. Define DTO class with annotation to configure, which field to be listed in the search combo and the label to be displayed for each field.
Customer.java
package com.rocky.dto;
import java.io.Serializable;
import com.rocky.common.ColumnHeaderSpecifier;
public class Customer implements Serializable{
@ColumnHeaderSpecifier(isSearchable = false)
private static final long serialVersionUID = -5235355839338180539L;
@ColumnHeaderSpecifier(columnHeader = "ID")
int id;
@ColumnHeaderSpecifier(columnHeader = "Customer Name")
String customerName;
@ColumnHeaderSpecifier(columnHeader = "State")
String state;
public Customer(int id, String name, String state) {
super();
this.id = id;
this.customerName = name;
this.state = state;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}