Prepare objects before update
Typical problem with web applications is update of (domain) objects. It is done in two steps: the first is presenting HTML form with only editable properties of an object, and, the second, is the actual update when form is submitted. The problem is that on submission in second step we must be aware of what object's properties to update.
The obvious solution is to put values of all non-editable object properties in the form using hidden input fields. Some web framework then would inject all those values into an empty domain object and it becomes ready for actual update.
However, this problem has its disadvantageous: values of all object properties should be presented in the form, hidden or not. Not only that this makes forms bigger and increases amount of posted (and sent) data, slows down the runtime and development time (all hidden values has to be properly encoded, too), but it also reduces the maintainability, since every time a property is changed, added or removed from the object, all forms where object is used have to be manually updated.
Preparable actions
Madvoc offers another approach for this problem: to run some logic to
load an object from the database, so that when parameters are set they
can be set on this object. This may be done by using
PrepareInterceptor
before ServletConfigInterceptor
. During action
preparation object would be loaded from the database and after
parameters will be injected in the loaded object.
First (good) consequence is that usually, there is no need for extra
code in action method for viewing the form. Second (bad) consequence is
that prepare()
needs ID parameters to load object from database.
Hopefully, Madvoc saves from getting IDs from parameters manually:
IdRequestInjectorInterceptor
. It simply injects only parameters that
ends with ".id"!
There is one more (and last) caveat: we have to be sure that empty
request parameters are not ignored. By default, everything is ok, empty
parameters are not ignored. Anyhow, this can be set by setting Madvoc
parameter:
injectorsManager.requestScopeInjector.ignoreEmptyRequestParams
.
Putting all together
It is all about the interceptors. Default interceptors stack:
public class AppInterceptorStack extends ActionInterceptorStack { public AppInterceptorStack() { super( IdRequestInjectorInterceptor.class, PrepareInterceptor.class, ServletConfigInterceptor.class ); } }
Usage
Here is the most interesting part of an action from web application that uses Madvoc, Spring and Hibernate.
@MadvocAction @InterceptedBy(AppInterceptorStack.class) public class BankEditAction implements Preparable { // injected by spring, on action creation, before injections @Inject BankService bankService; @In @Out Bank bank; @Override public void prepare() { // returns null if entity is null bank = bankService.findBankById(bank); } @Action public void view() {} @Action public Object store() { // validate somehow bankService.storeBank(bank); // merge it by hibernate return OK; } }
Plain and simple. The same action is used both for creating new or updating existing banks. Next example is more complicated: it is a form where user enters employee data and where it assigns a client to which employee belongs.
@MadvocAction @InterceptedBy(AppInterceptorStack.class) public class EmployeeEditAction implements Preparable { @Inject ClientService clientService; // injected by spring @In @Out Employee employee; @In @Out Client client; @Override public void prepare() { employee = clientService.findEmployeeById(employee); if (client == null) { // reads client from current employee, if exist client = employee != null ? employee.getClient() : null; } else { client = clientService.findClientById(client); } } @Action public void view() {} @Action public Object store() { // validate somehow employee.setClient(client); // set employee's client clientService.storeEmployee(employee); return OK; } }