Saturday, October 6, 2007

Wicket And The Command Pattern

I found out that Wicket benefits a lot from the command design pattern. By encapsulating most of the queries I create with command objects, I am able to make more reusable components. For example I created the QueryCommand object which looks something like this.
The Query Command is used to encapsulate method calls to your service layer.

/**
* This class is an implementation of the command pattern to encapsulate commands
* to the service layer. Class is meant to be used as an abstract class in web pages.
* Meant to be used as an abstract class implementation.
*
*
*
* @author Carlo M. Camerino
*
*/
public abstract class QueryCommand implements Serializable {
/**
* Query Parameter Implementation
* which must be set during construction time
* which is to be passed into the business layer.
*/
private QueryParam queryParam;

/**
* @param queryParam
*/
public QueryCommand(QueryParam queryParam) {
this.queryParam = queryParam;
}

/* (non-Javadoc)
* @see com.ccti.web.query.ISearchable#execute()
*/
public abstract Iterator execute();
/* (non-Javadoc)
* @see com.ccti.web.query.ISearchable#queryCount()
*/
public abstract int queryCount();


public void setPageIndex(int pageIndex) {
this.queryParam.setPageIndex(pageIndex);
}

public void setPageSize(int pageSize) {
this.queryParam.setPageSize(pageSize);
}

public QueryParam getQueryParam() {
return queryParam;
}
}


Instead of using the normal sortable data provider, you use a querydataprovider. Use the query dataprovider
to pass to your DataTables or DataView

public class QueryDataProvider extends SortableDataProvider implements ISortStateLocator {
/**
*
*/
private QueryCommand queryCommand;
private BaseService baseService;

public QueryDataProvider(QueryCommand queryCommand, BaseService baseService) {
this.queryCommand = queryCommand;
this.baseService = baseService;
}

/* (non-Javadoc)
* @see org.apache.wicket.markup.repeater.data.IDataProvider#iterator(int, int)
*/
public Iterator iterator(int first, int count) {
setQueryLimits(first, count);
return queryCommand.execute();
}

/**
* @param first
* @param count
*/
private void setQueryLimits(int first, int count) {
queryCommand.setPageIndex(first);
queryCommand.setPageSize(count);
}

public IModel model(Object object) {
return new QueryDetachableModel((IListeable) object, baseService);
}

public int size() {
return queryCommand.queryCount();
}
}


The last piece in the code is the use of the querydetachable model.
It is just like any detachable model.

public class QueryDetachableModel extends LoadableDetachableModel {
private BaseService baseService;
private Serializable id;
private Integer version;

public QueryDetachableModel(IListeable listable, BaseService baseService) {
this.baseService = baseService;
this.id = listable.getPrimaryKey();
this.version = listable.getVersion();
}

@Override
protected Object load() {
IListeable listeable = (IListeable) baseService.loadObjectById(id);
if (version == null) {
version = listeable.getVersion();
}
if(version != null) {
if (!(listeable.getVersion() == version)) {
throw new VersionMismatchException();
}
}
// TODO Auto-generated method stub
return listeable;
}
}


The QueryParam is just a holder for the page index, page size and the normal things that hibernate would need.


public class QueryParam implements Serializable {

private int pageIndex;
private int pageSize;

private List clauseList = new ArrayList();
private List orderList = new ArrayList();

private Class classType;

public Class getClassType() {
return classType;
}

public void setClassType(Class classType) {
this.classType = classType;
}

public QueryParam(Class classType) {
this.classType = classType;
}

public QueryParam(String propertyName, RestrictionType restrictionType,
Object... values) {
addNewClause(propertyName, restrictionType, values);
}

public void addNewClause(String propertyName, RestrictionType restrictionType, Object... values) {
clauseList.add(RestrictionBuilder.addRestriction(propertyName, restrictionType, values));
}

public void addNewOrder(String propertyName, boolean isAscending) {
orderList.add(OrderBuilder.addOrder(propertyName, isAscending));
}

public int getPageSize() {
return pageSize;
}

public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}


public int getPageIndex() {
return pageIndex;
}


public void setPageIndex(int pageIndex) {
this.pageIndex = pageIndex;
}

public List getClauseList() {
return clauseList;
}


public List getOrderList() {
return orderList;
}


}


I call it in my code using this.

Instead of creating different providers each time you would just use an anonymous command class.
This eliminates a lot of repetive code and makes your queries more dynamic.

QueryCommand queryCommand = new QueryCommand(queryParam){

@Override
public Iterator execute() {
// TODO Auto-generated method stub
return userGroupService.findAllUserGroups(this.getQueryParam()).iterator();
}

@Override
public int queryCount() {
// TODO Auto-generated method stub
return userGroupService.countAllUserGroups(this.getQueryParam());
}

};

QueryDataProvider userGroupProvider = new QueryDataProvider(queryCommand,
userGroupService);

AjaxFallbackDefaultDataTable dataTable =
new AjaxFallbackDefaultDataTable("sample", columnList, userGroupProvider,
ListUserGroup.this.getSysParPageSize());

No comments: