Saturday, October 6, 2007

Wicket Experience

My experience with wicket has been very good. Also helped me brush up on my oop skills.
More entries to come as my experience on wicket continues

Wicket And the Command Pattern - Part 2

Another benefit I see from using the command pattern in wicket is through the use of buttons.
This time I use another command abstract class
This is the button command. This class will be used by anonymous classes to encapsulate the calls that I would need.


/**
* Button Command - implements the command design pattern.
* Should replace all button instances.
* @author carloc
*
*/
public abstract class ButtonCommand implements Serializable {
public abstract void submit();

}



I then create a custom button which executes the command.
One benefit of this is that I would no longer have to catch exceptions in the other parts of my code.
All of the exceptions would be caught in one place. And a change of the type of button would only be change in one part of my code.


public class CCTIButton extends IndicatingAjaxButton {

/**
*
*/
private static final long serialVersionUID = 1L;

private ButtonCommand buttonCommand;

public CCTIButton(String id, CCTIForm form, ButtonCommand buttonCommand) {
super(id, form);
this.buttonCommand = buttonCommand;
}

@Override
protected void onSubmit(AjaxRequestTarget target, Form form) {
// TODO Auto-generated method stub
target.addComponent(getFeedback());
try {
buttonCommand.submit();
} catch(DataIntegrityViolationException e) {
error("Unique constraint violated.");
} catch(StaleObjectStateException e) {
error("Row(s) was updated or deleted by another transaction.");
} catch(VersionMismatchException e) {
error("Row(s) was updated or deleted by another transaction.");
} catch(CCTIException e) {
error(e.getMessage());
}catch(RuntimeException e) {
error("Application Error Occurred");
e.printStackTrace();
}catch(Exception e) {
error("Application Error Occurred");
e.printStackTrace();
}
}

@Override
protected void onError(AjaxRequestTarget target, Form form) {
target.addComponent(getFeedback());
super.onError(target, form);
}
}


A simple implementation of this would look like this


ButtonCommand buttonCommand = new ButtonCommand() {
@Override
public void submit() {
// TODO Auto-generated method stub
Corporation corporation = (Corporation) SaveCorporationForm.this.getModelObject();
corporation.setCreateDt(new Date());
corporation.setCreateBy(WebUtils.getUser(SaveCorporationForm.this).getId());

final CorporateUser corporateAdminOne = CorporateUser.createCorporateAdmin();
populatecorporateAdmin(corporation, corporateAdminOne);

final CorporateUser corporateAdminTwo = CorporateUser.createCorporateAdmin();
populateCorporateAdminTwo(corporation, corporateAdminTwo);
corporationService.save(corporation,corporateAdminOne,corporateAdminTwo,getFunctionsList());
setResponsePage(new AckPage(getMenu(), new Ack("Corporation has been saved with ref no. " + corporation.getRefNo() + ". Administrator password set to '"+ corporateAdminOne.getConfPswd() +"'", Transaction.CREATE_CORPORATION), new SaveCorporation()));
}
};
return buttonCommand;


As you can see I don't catch my exceptions here anymore.
I would then add the button to my form.


add(new CCTIButton("id", form, buttonCommand)


Again I'm saving a lot of code space

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());

Antonino

My eyes were opened but my ears were not.
Maybe I'm in a search for a world where I can actually be part.
Realizing that I am but a small speck in the world, I want to make a difference.