Saturday, December 26, 2009

CRUD controller with JSF

(belongs to this series of articles)

This is a proposal for a lightweight controller layer that would be built on top or the CRUD services layer described in the previous article. Just like we could generalize the DAO and Services layer for CRUD (create-retrieve-update-delete) operations on domain objects (or entity objects, extending DomainObject and its subclasses), we can create a parametrised framework class that implements the CRUD functionality for presentation layer. This class would have the role of Controller in the Model-View-Controller pattern; thus, the main functions of such a class would be calling the corresponding Service when a CRUD operation on a domain object is being performed. This class would be the common part of all controllers of an application that perform CRUD on domain objects, and contain all the functionality that can be generalized for all domain objects: retrieving, saving, and deleting an entity, and returning a collections of entities to create scrolling lists.

Most of the functionality is implemented by simply passing control to the Services layer; however, the scrolling lists and error handling require some logic on the controller level.

One possible approach is using NetBeans JSF CRUD generator. The disadvantages of that approach would be the dependence on one-way generator (re-generating loses all changes) and NetBeans (it's best when the project does not depend on any IDE). I submit that creating a framework class (possibly complemented by facelets and custom tags) is a more flexible approach.

The root class of the proposed controller hierarchy is trying to stay agnostic of the particular view technology being used. It passes control for most of the real operations to the service layer (via injected reference to the Service), and delegates view framework dependent operations (like displaying error messages) to its subclasses:

package com.example.crud.controller;

import java.io.Serializable;
import java.io.ObjectInputStream.GetField;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.Callable;

import javax.management.RuntimeErrorException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.example.crud.service.GenericCrudService;
import com.example.domain.DomainObject;

/**
 * Common session controller to implement CRUD actions. Can be used with
 * different view technologies.
 * 
 * Does <b>not</b> make the assumption that the object type is
 * {@link DomainObject}, so needs subclasses to implement
 * {@link #getCurrentId()}: only subclasses know how to extract the ID.
 * 
 * @author maxim
 * 
 * @param <T>
 *            domain object type
 * @param <PK>
 *            primary key
 */
public abstract class GenericCrudController<T extends DomainObject<PK>, PK extends Serializable> {

 public static final String SUCCESS = "success";

 public static final String ERROR = "error";

 public static final String EDIT = "edit";

 public static final String CANCEL = "cancel";

 protected static Log logger = LogFactory
   .getLog(GenericCrudController.class);

 protected Class<T> type;
 protected T currentRecord;

 protected transient GenericCrudService<T, PK> service;

 private int pgSize = 5;

 private String order;

 private boolean sortAscending;

 private int firstRow = 0;

 /**
  * Default constructor. Tries to discover Class<T>
  */
 @SuppressWarnings("unchecked")
 public GenericCrudController() {
  type = (Class<T>) ((ParameterizedType) getClass()
    .getGenericSuperclass()).getActualTypeArguments()[0];
  if (logger.isDebugEnabled())
   logger.debug("creating new " + this.getClass().getSimpleName());
 }

 /**
  * Subclasses add end user messages according to view technology
  * 
  * @param message
  *            text message
  * @param e
  *            exception or null if none
  */
 protected abstract void addMessage(String message, Throwable e);

 /**
  * Subclasses add end user messages according to view technology
  * 
  * @param message
  *            text message
  */
 protected abstract void addMessage(String message);

 /**
  * Subclass will reset the page view (e.g. after delete)
  */
 public abstract void resetPage();

 public Collection<T> getList() {
  try {
   return service.getAll();
  } catch (Exception e) {
   addMessage("Error retrieving page", e);
   logger.error("error retrieving page:", e);
   return new ArrayList<T>();
  }
 }

 /**
  * This is where the controller operations are really performed. Single spot
  * to implement error handling and returning standard outcomes and messages.
  * 
  * @param controllerAction
  *            a {@link Callable} that does the actions
  * @return string outcome
  */
 protected String successAndErrorAction(Callable<String> controllerAction) {
  try {
   String ret = controllerAction.call();
   logger.info("Action succeeded: " + this.getClass().getSimpleName());
   return null == ret ? SUCCESS : ret;
  } catch (Exception e) {
   logger.error("Exception in controller "
     + this.getClass().getSimpleName(), e);
   addMessage(e.getMessage());
   return ERROR;
  }
 }

 public GenericCrudService<T, PK> getService() {
  return service;
 }

 public void setService(GenericCrudService<T, PK> service) {
  this.service = service;
 }

 public void setCurrentRecord(T update) {
  currentRecord = update;
 }

 /**
  * @return id of the current record
  */
 protected PK getCurrentId() {
  return null==getCurrentRecord()?null:getCurrentRecord().getId();
 }

 /**
  * 
  * @return new current record (just uses the default constructor of T)
  */
 protected T newCurrentRecord() {
  try {
   if (logger.isDebugEnabled())
    logger.debug("new current record" + type.getSimpleName());
   return (T) type.newInstance();
  } catch (Exception e) {
   logger.error("exception creating CRUD currentRecord", e);
   return null;
  }
 }

 /**
  * Getting records to display on a page
  * 
  * @param pageSize
  *            page size
  * @param firstRecord
  *            first record number (starts from 0)
  * @param order
  *            field name for order, or null
  * @param asc
  *            true if ascending order, else descending
  * @return collection, size <= pageSize
  */
 protected Collection<T> getRecords(int pageSize, int firstRecord,
   String order, boolean asc) {
  if (pageSize <= 0) {
   return service.getAll();
  } else {
   return service.getPage(pageSize, firstRecord, order, asc);
  }
 }

 /**
  * 
  * @return collection of records based on current pagination helper
  */
 public Collection<T> getRecords() {
  return getRecords(getPageSize(), getFirstRow(), getOrder(),
    isSortAscending());
 }

 /**
  * 
  * @return the current record (e.g. the one being edited)
  */
 public T getCurrentRecord() {
  return this.currentRecord;
 }

 /**
  * Called from the list view to delete selected record
  * 
  * @return standard outcome from {@link #successAndErrorAction(Callable)}
  * @see #successAndErrorAction(Callable)
  */
 public String delete() {
  return successAndErrorAction(new Callable<String>() {
   @Override
   public String call() throws Exception {
    setCurrentRecord(getSelectedRecord());
    service.delete(getCurrentId());
    addMessage("Record deleted successfully");
    resetPage();
    return null;
   }
  });
 }

 /**
  * 
  * @return record selected in subclass
  */
 protected abstract T getSelectedRecord();

 /**
  * Called from list view to show the editing view; subclasses can override
  * to do something more interesting.
  * 
  * @return "edit" to show the editing view (without curent record this means
  *         create new)
  * @see #edit()
  */
 public String create() {
  currentRecord = newCurrentRecord();
  return EDIT;
 }

 public int getPageSize() {
  return pgSize;
 }

 public void setPageSize(int pageSize) {
  pgSize = pageSize;
 }

 private boolean reReadOnEdit=false;
 
 /**
  * Called from the list view to display the record for editing: "edit" means
  * show the editing view
  * 
  * The current record is pointed to by {@link #dataTable}.
  * 
  * The content of the record is (optionally) re-read, so that the lazy loading
  * collections could be loaded by the DAO on findById(), if the DAO implements this
  * method.
  * 
  * 
  * @return
  */
 public String edit() {
  T currentRecord = getSelectedRecord();
  if (null != currentRecord) {
   if(reReadOnEdit){
    currentRecord = service.get(currentRecord.getId());
   }
   setCurrentRecord(currentRecord);
  }
  return EDIT;
 }

 /**
  * Save action called from edit record view to add or save a record
  * 
  * @see #successAndErrorAction(Callable)
  * @return standard outcome from {@link #successAndErrorAction(Callable)}
  */
 public String save() {
  return successAndErrorAction(new Callable<String>() {
   @Override
   public String call() throws Exception {
    if (null != getCurrentId()) {
     setCurrentRecord(service.update(getCurrentRecord()));
    } else {
     service.insert(getCurrentRecord());
    }
    addMessage("Record saved successfully");
    return null;
   }
  });
 }

 public String getOrder() {
  return order;
 }

 public void setOrder(String order) {
  this.order = order;
 }

 public boolean isSortAscending() {
  return sortAscending;
 }

 public int getFirstRow() {
  return firstRow;
 }

 public void setFirstRow(int firstRow) {
  this.firstRow = firstRow;
 }

 public void setSortAscending(boolean sortAscending) {
  this.sortAscending = sortAscending;
 }

 public void setReReadOnEdit(boolean reReadOnEdit) {
  this.reReadOnEdit = reReadOnEdit;
 }

 public boolean isReReadOnEdit() {
  return reReadOnEdit;
 }

} 

The choice of view technology: why JSF?

JSF seems to be a natural choice of view technology for a framework that implements CRUD using JPA (or any other object-relational mapping framework, e.g. Hibernate), because it provides for easy mapping of properties of entities to view UI. This is especially important when the properties in question are collections of dependent beans (master-detail relationship) and we want to build a table-type edit form  (as shown, for example, here). As opposed to other technologies, JSF requires surprisingly little coding to map a collection of objects to an (editable and scrolling) data table on the web page.

Hence, an extension of GenericCrudController that uses JSF to implement abstract methods:


package com.example.crud.controller.jsf;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIData;
import javax.faces.component.html.HtmlDataTable;
import javax.faces.context.FacesContext;
import javax.faces.model.DataModel;

import org.apache.myfaces.renderkit.html.util.AddResource;
import org.apache.myfaces.renderkit.html.util.AddResourceFactory;

import com.example.crud.controller.GenericCrudController;
import com.example.crud.service.GenericCrudService;
import com.example.domain.DomainObject;

/**
 * Common controller type for CRUD using {@link GenericCrudService}.
 * 
 * Extending this class guarantees consistency in JFS outcomes and error
 * handling in controller operations.
 * 
 * Standard actions used by list and edit page templates are {@link #edit()},
 * {@link #create()}, {@link #save()}, {@link #delete()}.
 * 
 * {@link #getList()} or {@link #getPage()} is used to show the scrolling table.
 * 
 * {@link #getDataTable()} is the scrolling table model.
 * 
 * @author maxim
 * 
 * @param <T>
 *            currentRecord type that the controller works with
 * @param <PK>
 *            primary key type
 * 
 * @see GenericCrudService
 */
public class GenericCrudControllerJsf<T extends DomainObject<PK>, PK extends Serializable>
  extends GenericCrudController<T, PK> implements Serializable {

 private static final long serialVersionUID = -1;

 private Map<T, Boolean> recordSelections = new HashMap<T, Boolean>();

 private String popupOptions = "dependent=yes, menubar=no, toolbar=no, height=400, width=600";

 private transient UIData dataTable;
 private transient FacesContext facesContext;

 public GenericCrudControllerJsf() {
  if (logger.isDebugEnabled())
   logger.debug("constructed " + this.getClass());
 }

 public void submit() {
  logger.debug("void submit method");
 }

 /**
  * @return the dataTable
  */
 public UIData getDataTable() {
  return dataTable;
 }

 /**
  * @param dataTable
  *            the dataTable to set
  */
 public void setDataTable(UIData dataTable) {
  this.dataTable = dataTable;
 }

 /**
  * Add message via {@link FacesContext}
  * 
  * @param message
  * @param e
  */
 protected void addMessage(String message, Throwable e) {
  getFacesContext().addMessage(
    "controller",
    new FacesMessage(FacesMessage.SEVERITY_ERROR, message,
      null == e ? message : e.getMessage()));
 }

 /**
  * Add message via {@link FacesContext}
  * 
  * @param message
  */
 protected void addMessage(String message) {
  getFacesContext().addMessage("controller",
    new FacesMessage(FacesMessage.SEVERITY_INFO, message, message));
 }

 public void setFacesContext(FacesContext context) {
  this.facesContext = context;
 }

 public FacesContext getFacesContext() {
  if (null != facesContext) {
   return facesContext;
  } else {
   return FacesContext.getCurrentInstance();
  }
 }

 private AddResource addResource;

 public void setAddResource(AddResource addResource) {
  this.addResource = addResource;
 }

 public AddResource getAddResource() {
  if (null != addResource) {
   return addResource;
  } else {
   return AddResourceFactory.getInstance(getFacesContext());
  }
 }

 private List<ChoiceListener> choiceListeners = new ArrayList<ChoiceListener>();

 /**
  * Add listener to be called when we make a selection choice (e.g. check
  * some records and click "choose selected" or some such)
  * 
  * @param listener
  */
 public void addChoiceListener(ChoiceListener listener) {
  choiceListeners.add(listener);
 }

 /**
  * That's where we get control when the choice has been made in a pop-up
  * registered listeners are called; if the parent window exists and has a
  * "submitMainForm" element, we will close the current window and "click"
  * that element in the parent window.
  */
 public void choose() {
  for (T record : recordSelections.keySet()) {
   if (null != recordSelections.get(record)
     && recordSelections.get(record)) {
    for (ChoiceListener listener : choiceListeners) {
     listener.processChoice(record);
    }
   }
  }
  recordSelections.clear();
  dataTable = null;
  // The following kludge fires click event for "submitMainForm" element
  // in the main window,
  // if one is defined.
  String javaScriptText = "   window.close(); "
    + "   if(window.opener && window.opener.document.getElementById('submitMainForm')){"
    + "    var fireOnThis = window.opener.document.getElementById('submitMainForm');"
    + "    if (window.opener.document.createEvent) {"
    + "        var evObj = window.opener.document.createEvent('MouseEvents');"
    + "        evObj.initEvent( 'click', true, false );"
    + "        fireOnThis.dispatchEvent(evObj);"
    + "    } else if (window.opener.document.createEventObject){"
    + "         fireOnThis.fireEvent('onclick');" + "      }"
    + "   }";
  getAddResource().addInlineScriptAtPosition(getFacesContext(),
    AddResource.HEADER_BEGIN, javaScriptText);
 }

 /**
  * Method called by views and other controllers to open selection pop-up for
  * the domain objects in question
  * 
  * @param event
  */
 public void openPopup(String actionUrl) {
  dataTable = null;
  String javaScriptText = "window.open('" + actionUrl
    + "', 'popupWindow', '" + popupOptions + "');";
  getAddResource().addInlineScriptAtPosition(getFacesContext(),
    AddResource.HEADER_BEGIN, javaScriptText);
 }

 /**
  * @return
  */
 @Override
 @SuppressWarnings("unchecked")
 public T getSelectedRecord() {
  return null != dataTable ? ((T) dataTable.getRowData()) : null;
 }

 public Map<T, Boolean> getRecordSelections() {
  return recordSelections;
 }

 public void setRecordSelections(Map<T, Boolean> recoordSelections) {
  this.recordSelections = recoordSelections;
 }

 public void setPopupOptions(String popupOptions) {
  this.popupOptions = popupOptions;
 }

 public String getPopupOptions() {
  return popupOptions;
 }

 public void setChoiceListeners(List<ChoiceListener> choiceListeners) {
  this.choiceListeners = choiceListeners;
 }

 public List<ChoiceListener> getChoiceListeners() {
  return choiceListeners;
 }

 private transient DataModel pagedList;

 @Override
 public void resetPage() {
  dataPage = null;
  pagedList = null;
  setDataTable(null);
 }

 private DataPage<T> dataPage;
 
 /**
  * 
  * @return paginating lazy-reading collection
  */
 public DataModel getPage() {
  if (null == pagedList) {
   pagedList = new PagedListDataModel<T>(getPageSize(), getFirstRow()) {
    @Override
    public DataPage<T> fetchPage(int startRow, int pageSize) {
     final int start = getFirstRow();
     setFirstRow(startRow);
     if (logger.isDebugEnabled())
      logger.debug("new page: " + startRow + " old=" + start
        + " pageSize=" + pageSize);
     if (null == dataPage || startRow != dataPage.getStartRow()) {
      dataPage = new DataPage<T>(getService().getTotal(),
        startRow, (List<T>) getService().getPage(
          pageSize, startRow, getOrder(),
          isSortAscending()));
     }
     return dataPage;
    }
   };
  }
  return pagedList;
 }
}

The interesting part is the pagination logic that uses special class to implement lazy reading of records to fill pages.

Another part that needs clarification is how to code facelets or JSPs using this controller as a backing bean.

To be continued...

Thursday, December 17, 2009

Service Layer and Transactions: Sprin...

Service Layer and Transactions: Spring AOP used to apply declarative transactions 


(belongs to this series of articles)

Building on top of the reusable DAO framework that introduces generic DAOs for the simplest CRUD operations (create, update, delete), we are now implementing the Service layer. Why do we need the service layer at all? Indeed, as the framework does not, and can not, implement any non-trivial business methods in the service layer, why would one want to create it at all? After all, as would be seen, the service layer does simple pass-through to DAOs for the generic CRUD operations. The answer, or answers, are as follows:

  • we need to set the transaction boundary somewhere, and the DAOs seem to be the wrong place to do that, because giving them the extra concern of managing transactions would mean they wouldn't be easily usable when we have to aggregate several data object operations in one transaction (e.g. updating the main record and dependent records in a one-to-many would have a difficulty re-using the DAO for the other part of the relationship, because it would then be used within a transaction. This could be handled via declarative transaction (e.g. setting transaction level to "required"), but making DAOs transaction-agnostic seems to be a more flexible approach

  • setting the transaction boundary is better done where we define the business operations, because they are more coarse-grained than the "retrieve-save-delete" level for single domain objects; business operations (like "create invoice" as opposed to "create update invoice item") would tend to group several data access operations and would, in the most wide-spread case, be the natural place to demarcate transactions (this is especially true with Spring's declarative transactions that typically are set at the method level) 
In fact, what is being proposed is just one of the possible transaction strategies (more on this in this series of articles); however, this -- defining the transactions declaratively in the service layer using Spring configuration -- seems to be the simplest and most flexible option.

Here's how we could define the Generic Crud Service interface:

/**
 * 
 */
package com.example.crud.service;

import java.io.Serializable;
import java.util.Collection;

import com.example.domain.DomainObject;

/**
 * Generic CRUD service. Methods throw unchecked exceptions for errors; no
 * assumptions as to the type of the exception is made.
 * 
 * @author maxim
 * 
 * @param <T>
 *            domain object type
 * @param <PK>
 *            primary key type
 */
public interface GenericCrudService<T extends DomainObject<PK>, PK extends Serializable> {
 /**
  * 
  * @return a collection of all the entities (can be large)
  * @throws @{@link RuntimeException} unchecked exception for any error
  */
 public Collection<T> getAll();

 /**
  * Update persistent entity
  * 
  * @param emp
  *            data object (contains primary key)
  * @return updated entity (contains fields that could change during update,
  *         e.g. version)
  * @throws @{@link RuntimeException} unchecked exception for any error
  */
 public T update(T emp);

 /**
  * Deletes by primary key
  * 
  * @param id
  *            primary key
  * @throws @{@link RuntimeException} unchecked exception if doesn't exist or
  *         error
  */
 public void delete(PK id);

 /**
  * 
  * @param id
  *            primary key value
  * @return one object
  * @throws @{@link RuntimeException} unchecked exception if doesn't exist or
  *         error
  */
 public T get(PK id);

 /**
  * Inserts new entity
  * 
  * @param emp
  * @throws @{@link RuntimeException} unchecked exception for any error (e.g.
  *         integrity constraint violation)
  */
 public void insert(T emp);

 /**
  * Generic pagination
  * 
  * @param pageSize
  *            page size
  * @param firstRecord
  *            first record (starts with 0)
  * @param order
  *            field name to indicate order to the persistence layer
  *            (typically an entity field name)
  * @param asc
  *            true if ascending, else descending sort order
  * @return a collection containing single page of data, size <= pageSize
  * @throws @{@link RuntimeException} unchecked exception for errors
  */
 public Collection<T> getPage(int pageSize, int firstRecord, String order,
   boolean asc);
 /**
  * Generic pagination
  * 
  * @param pageSize
  *            page size
  * @param firstRecord
  *            first record (starts with 0)
  * @param order
  *            field name to indicate order to the persistence layer
  *            (typically an entity field name)
  * @param asc
  *            true if ascending, else descending sort order
  * @return a collection containing single page of data, size <= pageSize
  * @throws @{@link RuntimeException} unchecked exception for errors
  */
 public Collection<T> getPage(int pageSize, int firstRecord, String order,
   boolean asc, String criteria, Object ... objects );

 /**
  * Get total number of entities in persistent store
  * 
  * @return number
  * @throws @{@link RuntimeException} unchecked exception
  */
 public Long getTotal();

}


... and the implementation:

/**
 * 
 */
package com.example.crud.service;

import java.io.Serializable;
import java.util.Collection;

import com.example.crud.dao.GenericDao;
import com.example.crud.dao.GenericDaoConsumer;
import com.example.domain.DomainObject;

/**
 * Implements {@link GenericCrudService} with a {@link GenericDao}
 * 
 * @author maxim
 * 
 */
public class GenericCrudDaoService<T extends DomainObject<PK>, PK extends Serializable>
  implements GenericCrudService<T, PK>, GenericDaoConsumer<T, PK> {

 protected GenericDao<T, PK> dao;

 /*
  * (non-Javadoc)
  * 
  * @see com.example.prototype.service.TService#deleteT(java.lang.PK)
  */
 @Override
 public void delete(PK id) {
  dao.remove(id);
 }

 /*
  * (non-Javadoc)
  * 
  * @see com.example.prototype.service.TService#getAllTs()
  */
 @Override
 public Collection<T> getAll() {
  return dao.findAll();
 }

 /*
  * (non-Javadoc)
  * 
  * @see com.example.prototype.service.TService#getT(java.lang.PK)
  */
 @Override
 public T get(PK id) {
  return dao.findById(id);
 }

 /*
  * (non-Javadoc)
  * 
  * @see
  * com.example.prototype.service.TService#insertT(com.example.prototype
  * .entities.T)
  */
 @Override
 public void insert(T m) {
  dao.create(m);
 }

 /*
  * (non-Javadoc)
  * 
  * @see
  * com.example.prototype.service.TService#updateT(com.example.prototype
  * .entities.T)
  */
 @Override
 public T update(T m) {
  return dao.update(m);
 }

 /**
  * @see GenericCrudService
  */
 @Override
 public Collection<T> getPage(int pageSize, int firstRecord, String order,
   boolean asc) {
  return dao.getPage(pageSize, firstRecord, order, asc);
 }

 @Override
 public Long getTotal() {
  return dao.getTotalEntities();
 }

 @Override 
 public void setDao(GenericDao<T, PK> dao) {
  this.dao = dao;
 }

 public GenericDao<T, PK> getDao() {
  return dao;
 }

 @Override
 public Collection<T> getPage(int pageSize, int firstRecord, String order,
   boolean asc, String criteria, Object... objects) {
  return dao.getPage(pageSize, firstRecord, order, asc, criteria, objects);
 }

}


A simple class extending GenericCrudDaoService does not need to implement any operations (provided all it does is simple create-retrieve-update-delete:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.example.project.service;

import com.example.crud.service.GenericCrudDaoService;
import com.example.project.domain.UserData;

/**
 *
 * @author maxim
 */
public class UserDetailsDaoService extends GenericCrudDaoService<UserData, String> implements UserDetailsService {

}

As can be seen, we don't need to add any operations if we only need a CRUD service. This class is referenced in the Spring configuration quoted below.

Note that the source code doesn't have any transaction declarations either! This is because, instead of annotations, we are using Spring's AOP to apply transaction handling at the method level:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
 xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xsi:schemaLocation="
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">


 <!--
  Service layer. Defines transaction processing. Services are
  implemented directly using GenericDaoService or its subclasses
 -->

 <context:annotation-config />

 <tx:annotation-driven />


       
 <bean id="userSvc" class="com.example.project.service.UserDetailsDaoService" >
  <property name="dao" ref="myUserDetailsService"></property>
 </bean>


 <!--
  These pointcuts work for the services that subclass generic services
  from com.example.crud.service
 -->
 <aop:config>
  <aop:pointcut id="otherServiceMethods"
   expression="execution(* com.example.project.service.*.*(..))" />
  <aop:pointcut id="updateServiceMethods"
   expression="execution(* com.example.project.service.*.update*(..))" />
  <aop:pointcut id="deleteServiceMethods"
   expression="execution(* com.example.project.service.*.delete*(..))" />
  <aop:pointcut id="insertServiceMethods"
   expression="execution(* com.example.project.service.*.insert*(..))" />
  <aop:pointcut id="otherGenericServiceMethods"
   expression="execution(* com.example.crud.service.*.*(..))" />
  <aop:pointcut id="updateGenericServiceMethods"
   expression="execution(* com.example.crud.service.*.update*(..))" />
  <aop:pointcut id="deleteGenericServiceMethods"
   expression="execution(* com.example.crud.service.*.delete*(..))" />
  <aop:pointcut id="insertGenericServiceMethods"
   expression="execution(* com.example.crud.service.*.insert*(..))" />
   
  <aop:advisor advice-ref="txAdvice" pointcut-ref="updateServiceMethods" />
  <aop:advisor advice-ref="txAdvice" pointcut-ref="updateGenericServiceMethods" />
  <aop:advisor advice-ref="txAdvice" pointcut-ref="deleteServiceMethods" />
  <aop:advisor advice-ref="txAdvice" pointcut-ref="deleteGenericServiceMethods" />
  <aop:advisor advice-ref="txAdvice" pointcut-ref="insertServiceMethods" />
  <aop:advisor advice-ref="txAdvice" pointcut-ref="insertGenericServiceMethods" />
  <aop:advisor advice-ref="supportsAdvice" pointcut-ref="otherServiceMethods" />
  <aop:advisor advice-ref="supportsAdvice" pointcut-ref="otherGenericServiceMethods" />
  
 </aop:config>

 <tx:advice id="txAdvice" transaction-manager="transactionManager">
  <tx:attributes>
   <tx:method name="*" propagation="REQUIRED" isolation="DEFAULT"
    read-only="false" />
  </tx:attributes>
 </tx:advice>

 <tx:advice id="supportsAdvice" transaction-manager="transactionManager">
  <tx:attributes>
   <tx:method name="*" propagation="SUPPORTS" read-only="true" />
  </tx:attributes>
 </tx:advice>


</beans>

Monday, November 16, 2009

Saturday, October 24, 2009

Reusable DAOs: CRUD operations with domain objects

(belongs to this series of articles)

Trying to create reusable DAO objects, following the article "Don't repeat the DAO" for the reusable domain objects proposed earlier in this series.

It is proposed to follow the recipe from the IBM site referred to above:

package com.example.crud.dao;

import java.io.Serializable;
import java.util.Collection;

import com.example.domain.DomainObject;

/**
 * Generic DAO interface - usable for CRUD and more.
 * 
 * Throws unchecked exceptions for any errors. The exception type depends on the
 * implementation.
 * 
 * @author maxim
 * 
 * @param <T>
 *            domain object type
 * @param <PK>
 *            primary key type
 */
public interface GenericDao<T extends DomainObject<PK>, PK extends Serializable> {

 /**
  * Persist the newInstance object into database
  **/
 void create(T newInstance);

 /**
  * Retrieve an object that was previously persisted to the database using
  * the indicated id as primary key
  */
 T findById(PK id);

 /**
  * 
  * @return all instances of T as a collection
  */
 Collection<T> findAll();

 /**
  * Main paging method
  * 
  * @param pageSize
  *            number of records (page size)
  * @param firstRecord
  *            start from this record (inclusive)
  * @param order
  *            field name to sort
  * @param asc
  *            sort ascending if true, descending otherwise
  * 
  * @return all instances that belong to the page
  */
 Collection<T> getPage(int pageSize, int firstRecord, String order,
   boolean asc);

 /**
  * Save changes made to a persistent object.
  * 
  * @return TODO
  */
 T update(T transientObject);

 /**
  * Remove an object from persistent storage in the database
  */
 void remove(T object);

 /**
  * Remove an object from persistent storage in the database by ID
  */
 void remove(PK id);

 /**
  * 
  * @return total number of entities in the DB
  */
 Long getTotalEntities();

 /**
  * Overload of "getPage" with a possibility to apply dynamic criteria.
  * Service layer could use this to implement find by related record, for
  * example
  * 
  * @see #getPage(int, int, String, boolean)
  * 
  * @param pageSize
  *            page size
  * @param firstRecord
  *            first record number, starts from 0
  * @param order
  *            sort order defined by field name, e.g. "id"
  * @param asc
  *            true for ascending sort
  * @param queryString
  *            string to add to query, e.g. "where o.id=?", or null
  * @param parameters
  *            objects to use the query with
  * @return up to <code>pageSize</code> of objects or empty collection
  */
 Collection<T> getPage(int pageSize, int firstRecord, String order,
   boolean asc, String queryString, Object... parameters);
}

And the JPA implementation of this:

/**
 * 
 */
package com.example.crud.dao.jpa;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;

import javax.persistence.EntityManager;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.example.crud.dao.GenericDao;
import com.example.domain.DomainObject;

/**
 * Implements {@link GenericDao} using JPA.
 * 
 * Works in managed environment using the injected {@link EntityManager}
 * 
 * @author maxim
 * 
 */
public class GenericDaoJpa<T extends DomainObject<PK>, PK extends Serializable>
  implements GenericDao<T, PK> {


 private static Log logger = LogFactory.getLog(GenericDaoJpa.class);

 
 @PersistenceContext
 public transient EntityManager em;

 public Class<T> type;

 @SuppressWarnings("unchecked")
 public GenericDaoJpa() {
  type = (Class<T>) ((ParameterizedType) getClass()
    .getGenericSuperclass()).getActualTypeArguments()[0];
 }

 /*
  * (non-Javadoc)
  * 
  * @see
  * com.example.prototype.persistence.GenericDao#create(java.lang.Object )
  */

 public void create(T newInstance) {
  em.persist(newInstance);
 }

 /*
  * (non-Javadoc)
  * 
  * @seecom.example.struts2.crud.persistence.GenericDao#findById(java.io.
  * Serializable)
  */

 public T findById(PK id) {
  T entity = em.find(type, id);
  postFindById(entity, em);
  return entity;
 }

 /**
  * Override in subclasses to do something here: this is where we could force
  * the loading of lazy loading collections, for example.
  * 
  * The rationale is that we typically need this for individual records, but
  * not for collections; so getPage() and the like return only eager-loading
  * properties, whereas findById() and the like should return fully loaded
  * objects.
  * 
  * @param entity
  *            the entity retrieved by find -- call methods on for lazy
  *            loading objects and collections to force loading
  * @param em
  */
 protected void postFindById(T entity, EntityManager em) {
 }

 /*
  * (non-Javadoc)
  * 
  * @see
  * com.example.prototype.persistence.GenericDao#remove(java.lang.Object )
  */

 public void remove(PK id) {
  remove(em.getReference(type, id));
 }

 /*
  * @see
  * com.example.prototype.persistence.GenericDao#remove(java.lang.Object
  */

 public void remove(T object) {
  em.remove(object);
 }

 /*
  * (non-Javadoc)
  * 
  * @see
  * com.example.prototype.persistence.GenericDao#update(java.lang.Object )
  */

 public T update(T transientObject) {
  try {
   return em.merge(transientObject);
  } catch (OptimisticLockException e) {
   logger.error("Record is modified by another process", e);
   throw new RuntimeException(
     "Error saving record: modified by another process");
  }
 }

 @Override
 public Collection<T> findAll() {
  Query query = em.createQuery("select o from " + type.getSimpleName()
    + " o");
  return findAll(query);
 }

 @SuppressWarnings("unchecked")
 protected Collection<T> findAll(Query q) {
  return q.getResultList();
 }

 @Override
 public Collection<T> getPage(int pageSize, int firstRecord, String order,
   boolean asc) {
  return getPage(pageSize, firstRecord, order, asc, null, (Object) null);
 }

 @SuppressWarnings("unchecked")
 public Collection<T> getPage(int pageSize, int firstRecord, String order,
   boolean asc, String queryString, Object... parameters) {
  final String whereString = queryString == null ? "" : queryString;
  final String ascending = asc ? "asc" : "desc";
  final String orderString = null == order || order.trim().equals("") ? ""
    : "order by " + order + " " + ascending;
  final Query q = em.createQuery("select o from " + type.getSimpleName()
    + " o " + whereString + " " + orderString);
  if (null != parameters && null != queryString) {
   for (int i = 0; i < parameters.length; i++) {
    q.setParameter(i + 1, parameters[i]);
   }
  }
  if (0 < pageSize) {
   q.setMaxResults(pageSize);
  }
  q.setFirstResult(firstRecord);
  return q.getResultList();
 }

 /**
  * 
  * @return the total number of entities
  */
 public Long getTotalEntities() {
  Query query = em.createQuery("select count(o) as count from "
    + type.getSimpleName() + " o");
  return (Long) query.getSingleResult();
 }

 public EntityManager getEm() {
  return em;
 }

 public void setEm(EntityManager em) {
  this.em = em;
 }

}

The only original twist in the above is the "postFindById()" method. It is a hack for something that would be normally out of scope for this article -- the problem of lazy loading collections in an entity object that is used outside of the transaction scope. This is the reason for the widespread error: "org.hibernate.LazyInitializationException: failed to lazily initialize a collection ... no session or session was closed" that would happen in the following widespread case:

(1) display a list of objects
(2) display the details of one of them, part of those details being lazy-loading collections of dependent objects

If the object is a JPA/Hibernate entity, then the ORM session would indeed be closed between (1) and (2). There are many ways to code around this, but one that I find the most generic is to make DAO methods that retrieve the entity by ID (and by definition work with a single entity, as opposed to a list) force the loading of lazy-loading collections. Which collections need to be loaded can't be determined in the generic DAO, hence the delegating of this to subclasses using an abstract method.

Monday, October 19, 2009

Spring Web Flow with Pop-Up View States

Setting up an application that uses pop-up view states with a JSF view has worked very well, except for the pop-ups; I had to spend a while on trying to make them work and had been barking up a forest of wrong trees before I finally got to downloading the "booking-jsf" sample from Spring Source Download (highly recommended, something I should have started from), and went to compare the configuration. The important points:

(1) Follow the Spring Web Flow reference for setting up Facelets and Web Flow integration

(2) The view state that uses the pop-up should be also explicitly rendering a part of the view (one doesn't need the whole in the modal window; the page itself, on the other hand, should be complete, so as to degrade gracefully and display even when pop-ups are disabled)

<view-state id="selectFiles" view="/files-select.xhtml" popup="true" redirect="true">
  <on-entry>
   <render fragments="content-area-popup"></render>
  </on-entry>
    ...
 </view-state> 

(if anyone noticed the "redirect", more on that later)

(3) Make sure the view that invokes the popup uses "sf:commandButton", and not normal JSF/Tomahawk/WhateverLibrary tag that one uses for buttons

...
 <sf:commandButton value="Add Another" action="selectFiles"/>
...

(4) Finally, make sure the transition uses "redirect"; normally, this is the default, but, in my case, I have disabled it in the flow engine configuration (need to use legacy JSF views that persist state using "t:saveState"); if disabled, redirect transition can be enabled by "redirect='true'" view definition attribute in the flow definition

It is the failure to ensure (4) that was making my popup to either not display or produce obscure errors, as the flow view rendering was trying to render the popup inside the invoking screen and was missing the element with the same ID to replace with the popup content.

Monday, October 12, 2009

Sharing my .rc files

.vimrc

let mapleader = ","
let localmapleader = ","
let g:mapleader = ","
:map ,q MailQuote
"
" Misc settings
"
filetype plugin indent on
syntax on
set mouse=a
set number
set fileformat=unix
set noeb
set et
set tw=9999 sw=2 ts=2
"
" This helps to move to another window, and maximize it at the same time
"
noremap  
noremap  
noremap  
noremap  
set winminheight=0

map  :e #
map  :w:make

func! CurrentFileDir(cmd)
  return a:cmd . " " . expand("%:p:h") . "/"
endfunc

if has("unix")
  map e :e =expand("%:p:h") . "/" 
  map w :w =expand("%:p:h") . "/" 
  map r :r =expand("%:p:h") . "/" 
  map s :sp =expand("%:p:h") . "/" 
else
  map e :e =expand("%:p:h") . "\\" 
  map w :w =expand("%:p:h") . "\\" 
  map r :r =expand("%:p:h") . "\\" 
  map sp :sp =expand("%:p:h") . "\\" 
endif

map x :set filetype=xml
      \:source $VIMRUNTIME/syntax/xml.vim
      \:let g:xml_syntax_folding = 1
      \:set foldmethod=syntax
      \:source $VIMRUNTIME/syntax/syntax.vim
      \:echo "XML mode is on"

nmap px %!"xmllint" --format -
vmap px !"xmllint" --format -

let g:args = ""
function! CaptureOutput(cmd)
  silent execute "!rm temp.output"
  silent execute a:cmd g:args
  new
  read temp.output
  silent execute "!rm temp.output"
  set nomodified
endfunction
command! -nargs=+ -complete=command CaptureOutput call CaptureOutput()

au BufNewFile,BufRead *.rb map  :w!:CaptureOutput !ruby % > temp.output 2>&1
au BufNewFile,BufRead *.rb map  :w!:CaptureOutput !ruby -c % > temp.output 2>&1
au BufNewFile,BufRead *.mxml set filetype=mxml
au BufNewFile,BufRead *.as set filetype=actionscript
au BufNewFile,BufRead *.mail set filetype=mail ai si et tw=72 ts=2 sw=2 fo=cqrnot spell spelllang=en
au BufNewFile,BufRead *.txt set et tw=72 ts=2 sw=2 fo=ncroqlt ai si spell spelllang=en


.bashrc


let mapleader = ","
# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

# don't put duplicate lines in the history. See bash(1) for more options
# don't overwrite GNU Midnight Commander's setting of `ignorespace'.
export HISTCONTROL=$HISTCONTROL${HISTCONTROL+,}ignoredups
# ... or force ignoredups and ignorespace
export HISTCONTROL=ignoreboth

# append to the history file, don't overwrite it
shopt -s histappend

# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)

# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize

# make less more friendly for non-text input files, see lesspipe(1)
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"

# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then
    debian_chroot=$(cat /etc/debian_chroot)
fi

# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
    xterm-color) color_prompt=yes;;
esac

# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
#force_color_prompt=yes

if [ -n "$force_color_prompt" ]; then
    if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
 # We have color support; assume it's compliant with Ecma-48
 # (ISO/IEC-6429). (Lack of such support is extremely rare, and such
 # a case would tend to support setf rather than setaf.)
 color_prompt=yes
    else
 color_prompt=
    fi
fi

if [ "$color_prompt" = yes ]; then
    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
    PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt

# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
    PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
    ;;
*)
    ;;
esac

# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc package.

#if [ -f ~/.bash_aliases ]; then
#    . ~/.bash_aliases
#fi

# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
    eval "`dircolors -b`"
    alias ls='ls --color=auto'
    #alias dir='dir --color=auto'
    #alias vdir='vdir --color=auto'

    #alias grep='grep --color=auto'
    #alias fgrep='fgrep --color=auto'
    #alias egrep='egrep --color=auto'
fi

# some more ls aliases
#alias ll='ls -l'
#alias la='ls -A'
#alias l='ls -CF'

# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
if [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
fi
#
# Customizations
#
set -o vi
export PATH=$PATH:/var/lib/gems/1.8/bin/

.screenrc

let mapleader = ","

#.screenrc
activity "%c activity -> %n%f %t"
autodetach on
altscreen on
bell "%c bell -> %n%f %t^G"
defflow auto
defscrollback 10000
defutf8 on
msgwait 2                 # 1 second messages
startup_message off        # disable the startup splash message
shell -bash
vbell_msg "[[[ ding ]]]"
vbell off
nethack on

term xterm
#xterm understands both im/ic and doesn't have a status line.
#Note: Do not specify im and ic in the real termcap/info file as
#some programs (e.g. vi) will not work anymore.
termcap  xterm hs@:cs=\E[%i%d;%dr:im=\E[4h:ei=\E[4l
terminfo xterm hs@:cs=\E[%i%p1%d;%p2%dr:im=\E[4h:ei=\E[4l
#xterm hardstatus in the title
termcapinfo xterm*|Eterm|mlterm 'hs:ts=\E]0;:fs=\007:ds=\E]0;screen\007'
defhstatus "screen c (nt) | $USER@nH"
hardstatus off
#80/132 column switching must be enabled for ^AW to work
#change init sequence to not switch width
termcapinfo  xterm Z0=\E[?3h:Z1=\E[?3l:is=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;4;6l

caption always "%{gk}[ %{G}%H %{g}][%= %{wk}%?%-Lw%?%{=b kR}(%{W}%n*%f %t%?(%u)%?%{=b kR})%{= kw}%?%+Lw%?%?%= %{g}][%{Y}%l%{g}]%{=b C}[ %m/%d %c ]%{W}"

#caption always "%?%F%{-b bc}%:%{-b bb}%?%C|%D|%M %d|%H%?%F%{+u wb}%?  %L=%-Lw%45>%{+b by}%n%f* %t%{-}%+Lw%-0<"

# F8 to turn the status bar off
bindkey -k k8 hardstatus alwayslastline
# F9 to turn the status bar on
bindkey -k k9 hardstatus alwaysignore
# F1 and F2 to move one screen forward or backward
bindkey -k k1 prev
bindkey -k k2 next
 

Sunday, October 4, 2009

Source code high-light in Blogger

I have used the recipe from here:

sunday-lab: Source code high-light in Blogger

However, I made the following modification: used Blogger's layout editor to add a "Text/Html" widget with the following code:

<script type="text/javascript"><!--
window.onload = function(){
prettyPrint();

};


// prettyfy.js
... code goes here ...
// end prettify.js

--></script>

<style type="text/css">
... css styles go here
</style>



Note the "window.onload" above -- this means we don't have to modify the HTML template and the "body" tag. Everything we need to use "pre" with "class='prettyprint'" is contained within the fragment above.

The reason one would want to set all this in the Text/HTML widget in the Layout, rather than in the HTML template, is that the template is overwritten each time one changes the blog's template.

Saturday, October 3, 2009

JPA and Common Domain Objects

(belongs to this series)

The idea is to create a hierarchy of classes that all domain objects can derive from. These classes are the single point where we implement the following:

  • Methods for Collections: we need all domain objects to be comparable, hashable and to equal on ID (primary key)
  • Primary key: either auto-numbering or GUID
  • Optimistic concurrency

What could be added if needed:

  • Timestamp
  • Auditing: the user id of last update

All domain objects can extend these; we could also benefit from all domain objects following the same pattern (e.g. having a "getId()" method), when implementing the generic DAOs.

If we add entity classes, and configure the project as suggested below, then, at this stage, we would achieve the following:
  • The database schema will be generated by Hibernate using the entity classes that extend the generic domain object classes suggested here; the configuration suggested below actually provides for dropping and the tables and re-generating them each time the build runs.

  • Entity manager and transaction manager will be configured by Spring and ready for the next stage -- implementing the Data Access Objects

  • The unit tests can be implemented with the assumption that the database schema is up to date -- i.e. following the entity classes -- for every build (see "execution" clause in Hibernate plug-in configuration suggested below)

Libraries and Versions


... as expressed by Maven dependencies:

....

<dependency>
 <groupid>org.springframework</groupid>
 <artifactid>spring</artifactid>
 <version>2.5.6</version>
</dependency>
<dependency>
 <groupid>org.springframework</groupid>
 <artifactid>spring-core</artifactid>
 <version>2.5.6</version>
</dependency>
<dependency>
 <groupid>org.springframework</groupid>
 <artifactid>spring-context</artifactid>
 <version>2.5.6</version>
</dependency>
<dependency>
 <groupid>org.springframework</groupid>
 <artifactid>spring-test</artifactid>
 <version>2.5.6</version>
</dependency>
<dependency>
 <groupid>org.springframework</groupid>
 <artifactid>spring-web</artifactid>
 <version>2.5.6</version>
</dependency>
...
  
<dependency>
 <groupid>javax.persistence</groupid>
 <artifactid>persistence-api</artifactid>
 <version>1.0</version>
</dependency>
<dependency>
 <groupid>org.hibernate</groupid>
 <artifactid>hibernate-entitymanager</artifactid>
 <version>3.3.2.GA</version>
</dependency>
<dependency>
 <groupid>javax.transaction</groupid>
 <artifactid>jta</artifactid>
 <version>1.1</version>
</dependency>
<dependency>
 <groupid>${jdbc.groupId}</groupid>
 <artifactid>${jdbc.artifactId}</artifactid>
 <version>${jdbc.version}</version>
</dependency>
...

Using Hibernate Maven Plugin to generate the schema


Let's start with the "build" section of the POM, where we define how the resources get filtered (in Maven parlance, this means resolving variables in them using property values from the POM):

...
  <resources>
   <resource>
    <directory>${project.basedir}/src/main/resources</directory>
    <filtering>true</filtering>
   </resource>
  </resources>
  <testresources>
   <testresource>
    <directory>${project.basedir}/src/test/resources</directory>
    <filtering>true</filtering>
   </testresource>
  </testresources>      


Where the resources we are filtering as shown above are used? It is mostly in the jdbc.properties file quoted below. This is the file that is referenced from the Hibernate section of the POM, and also in the Spring configuration quoted below. This way we are making sure that all the database properties are specified in the POM.

The reason we are choosing POM as the single place of specifying the parameters for the project is because Maven has the ability to implement logic switching sets of values depending on the environment where the build is performed (test, development, QA, production). The other possible places (.properties files, Spring configuration) lack this flexibility.

The idea was borrowed from this Maven archetype


#
# These properties are used at build time and run-time;
# they define the DB type and location; POM defines several
# sets of values for different environments (test, QA, ...)
#
jdbc.driverClassName=${jdbc.driverClassName}
jdbc.url=${jdbc.url}
jdbc.username=${jdbc.username}
jdbc.password=${jdbc.password}
jpa.database=${jpa.database}
jpa.showSql=${jpa.showSql}


# Needed by Hibernate3 Maven Plugin defined in pom.xml
hibernate.dialect=${hibernate.dialect}
hibernate.connection.username=${jdbc.username}
hibernate.connection.password=${jdbc.password}
hibernate.connection.url=${jdbc.url}
hibernate.connection.driver_class=${jdbc.driverClassName}


The property values that will be used at build time to resolve variables in the configuration file quoted above are specified in the POM -- in the "profiles" section -- as follows:

<properties>
  <dbunit.dialect>org.dbunit.ext.mysql.MySqlDataTypeFactory</dbunit.dialect>
  <hibernate.dialect>org.hibernate.dialect.MySQLDialect</hibernate.dialect>
  <jdbc.groupid>mysql</jdbc.groupid>
  <jdbc.artifactid>mysql-connector-java</jdbc.artifactid>
  <jdbc.version>5.1.6</jdbc.version>
  <jdbc.driverclassname>com.mysql.jdbc.Driver</jdbc.driverclassname>
  <jdbc.username>example</jdbc.username>
  <jdbc.password> </jdbc.password>
  <jpa.database>MYSQL</jpa.database>
  <jpa.showsql>true</jpa.showsql>
</properties>


Hibernate plug-in configuration references the jdbc.properties file that is getting receiving the values of properties specified in the POM as described above:

...
   <plugin>
    <groupid>org.codehaus.mojo</groupid>
    <artifactid>hibernate3-maven-plugin</artifactid>
    <version>2.2</version>
    <executions>
     <execution>
      <phase>process-classes</phase>
      <goals>
       <goal>hbm2ddl</goal>
      </goals>
     </execution>
    </executions>
    <configuration>
     <components>
      <component>
       <name>hbm2ddl</name>
       <implementation>jpaconfiguration</implementation>
      </component>
     </components>
     <componentproperties>
      <persistenceunit>example</persistenceunit>
      <outputfilename>schema.ddl</outputfilename>
      <propertyfile>target/classes/jdbc.properties</propertyfile>
      <drop>true</drop>
      <create>true</create>
      <export>true</export>
      <update>false</update>
      <format>true</format>
     </componentproperties>
    </configuration>
    <dependencies>
     <dependency>
      <groupid>${jdbc.groupId}</groupid>
      <artifactid>${jdbc.artifactId}</artifactid>
      <version>${jdbc.version}</version>
     </dependency>
    </dependencies>

   </plugin>
      ...


And persistence.xml is nearly empty (all mappings are handled by Hibernate, and entity manager is supplied by Spring):

<persistence version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/persistence" xsi:schemalocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
 <persistence-unit name="example" transaction-type="RESOURCE_LOCAL">
 </persistence-unit>
</persistence>

Finally, what binds together JPA and Hibernate is the Spring container; note the database parameters that reference the properties file:

<beans xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
   http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd ">

    <context:property-placeholder location="classpath:/jdbc.properties">
    </context:property-placeholder>
   
   
    <context:annotation-config>
    </context:annotation-config>

    <tx:annotation-driven>
    </tx:annotation-driven>

    <jee:jndi-lookup id="dataSource" jndi-name="jdbc/Database">
    </jee:jndi-lookup>

   <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory" p:datasource-ref="dataSource" p:jpavendoradapter-ref="jpaAdapter">
    <property name="loadTimeWeaver">
     <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver">
    </bean>
   </property>

   <bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager" p:entitymanagerfactory-ref="entityManagerFactory">

      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" id="jpaAdapter" p:database="${jpa.database}" p:showsql="${jpa.showSql}">
      </bean>
    </bean>
  </bean>
  </beans>


Root class of reusable domain object hierarchy:


Implements comparison, equality and hash code, the primary key is left for the subclasses:

package com.example.domain;

import java.io.Serializable;

import javax.persistence.MappedSuperclass;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PrePersist;
import javax.persistence.Transient;
import javax.persistence.Version;

import org.apache.commons.lang.ObjectUtils;

/**
 * Root class for all entities; implements comparison and what goes with it.
 * Also has a "version" field for optimistic concurrency checking.
 * 
 * @author maxim
 *
 * @param <PK> primary key type
 * 
 * @see AutoNumberedDomainObject
 * @see SurrogateIdDomainObject
 */
@MappedSuperclass
abstract public class DomainObject<PK> implements
  Serializable, Comparable<DomainObject<PK>>, Cloneable {
 
 @Transient
 private boolean justCreated=true;
 
 @PostPersist
 @PostLoad
 public void saved(){
  justCreated=false;
 }
 
 static private  long serialVersionUID = 1L;
 
 @Version
 protected int version;

 @Override
 public String toString() {
  return "DomainObject ("+this.getClass().getSimpleName()+")";
 }

 
 @SuppressWarnings("unchecked")
 @Override
 public boolean equals(Object o) {
  if (o == this)
   return true;
  if (o == null)
   return false;
  return (o.getClass().isAssignableFrom(this.getClass()))
    && (this.getClass().isAssignableFrom(o.getClass()))
    && ObjectUtils.equals(((DomainObject<PK>) o).getId(), getId());
 }

 @Override
 public int hashCode() {
  if (getId() == null)
   return 0;
  return getId().hashCode();
 }

 /**
  * A simple stable ordering based on Id
  */
 @SuppressWarnings("unchecked")
 public int compareTo(DomainObject<PK> o) {
  if (getId() == null)
   return -1;
  if (o.getId() == null)
   return 1;
  return ((Comparable<PK>) getId()).compareTo(o.getId());
 }

 @Override
 public Object clone() {
  try {
   return super.clone();
  } catch (CloneNotSupportedException e) {
   throw new RuntimeException(e);
  }
 }
 
 /**
  * 
  * @return lable to display in selection lists; override in subclasses to produce something better
  */
 public String getSelectionLabel(){
  return getId().toString();
 }

 /**
  * @param id
  *            the id to set
  */
 public abstract void setId(PK id);

 /**
  * @return the id
  */
 public abstract PK getId();

 public int getVersion() {
  return version;
 }

 public void setVersion(int version) {
  this.version = version;
 }


 public void setJustCreated(boolean notSaved) {
  this.justCreated = notSaved;
 }


 public boolean isJustCreated() {
  return justCreated;
 }

}



Domain object with auto-numbered ID


This class extends DomainObject to add JPA auto-numbered ID fields.

package com.example.domain;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

/**
 * Extends {@link DomainObject} to support auto-numbered IDs via JPA
 * {@link GeneratedValue} with {@link GenerationType} =
 * {@link GenerationType#TABLE}
 * 
 * Does <b>not</b> force a unique constraint on the ID: add this in subclasses
 * if performance hit is accessible.
 * 
 * @param PK
 *            the primary key class for auto-numbering; the choice is usually
 *            between Long and Integer
 * 
 * @author maxim
 * 
 */
@MappedSuperclass
public abstract class AutoNumberedDomainObject<PK extends Number> extends
  DomainObject<PK> {

 private static  long serialVersionUID = 1L;

 @Override
 public String toString() {
  return "AutoNumberedDomainObject ("+this.getClass().getSimpleName()+") [id=" + id + "]";
 }
 
 @Id
 @GeneratedValue(strategy = GenerationType.TABLE)
 protected PK id;

 @Override
 public  PK getId() {
  return this.id;
 }

 @Override
 public  void setId(PK id) {
  this.id = id;
 }

}


Domain object with Surrogate Id


Another (and, arguably, more usable) flavour of DomainObject generates surrogate primary keys:

package com.example.domain;

import java.util.UUID;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

/**
 * DomainObject that has a surrogate ID calculated as a time-based GUID.
 * 
 * Does <b>NOT</b> force a unique constraint on the ID: add this in subclasses
 * if performance hit is acceptable.
 * 
 * @author maxim
 * 
 * @see UUID
 * 
 */
@MappedSuperclass
public class SurrogateIdDomainObject extends DomainObject<String> {

 @Override
 public String toString() {
  return "SurrogateIdDomainObject ("+this.getClass().getSimpleName()+") [id=" + id + "]";
 }

 /**
  * Default constructor; generates new ID
  */
 public SurrogateIdDomainObject() {
  super();
  this.id = newId();
 }

 /**
  * 
  * @param generateId specify false to <b>not</b> generate the ID
  */
 public SurrogateIdDomainObject(boolean generateId) {
  super();
  if (generateId) {
   this.id = newId();
  }
 }

 private static final long serialVersionUID = 1L;

 /**
  * "e5d302ed-0501-4a3e-abfc-a336124fe2b5" 
  */
 @Id
 @Column(name = "ID",length=36)
 private String id;

 /**
  * Creates new and assigns a surrogate ID
  */
 protected void initializeAsNew() {
  setId(newId());
 }

 public  void setId(String id) {
  this.id = id;
 }

 public  String getId() {
  return id;
 }

 /**
  * 
  * @return new UUID-based ID, e.g. "e5d302ed-0501-4a3e-abfc-a336124fe2b5" 

  * @see UUID
  */
 public static String newId() {
  return UUID.randomUUID().toString();
 }

}

Using the generic domain object classes


Having defined all of the above, we can benefit from it defining new domain objects as follows:

...
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@Entity
public class User extends SurrogateIdDomainObject {

 private String name;
...

The XML (actually, JAXB) annotations will come into play when we will be passing this object to and from Web Services.

Hibernate, JPA, JAX-WS, Spring AOP, JSF, Facelets, Transactions with Maven and Continuous Integration.

Starting what will hopefully become a series of articles showing how to use the widespread technologies in a web application started from scratch and intended to be deployed on a simple servlet container (Tomcat, Jetty,...) lacking support for advanced J2EE.

In other words, this is sharing simple home-made recipes and hoping to make them better by sharing.

The problem: create a slim and maintainable Java application including a lightweight framework that facilitates CRUD operations. This is a template that would be the starting point of many different projects, and eventually would lead to  creation of a Maven archetype.

What is known: performance is not critical; we should use free software (database and application servers), but not exclude a transition to other technologies in the future. The resulting project should lend itself easily to agile development and continuous integration (this is where things like DBUnit come into the picture).

The set of Java classes thus created can form the core of a lightweight framework that could be used to build different applications using modern Java technologies.

The content was a result of a recent design and development effort, and producing it involved a lot of googling, and very little original effort. Enjoy and comment!
Next articles (work in progress):


Some notes on the choice of technologies
... for those who need that.
  • JPA and Hibernate: first of all, JPA seemed to be the best for a project that starts from scratch as a Java application, because it's a Java-centric technology. It would allow to do domain modeling in terms of Java objects and make the database schema follow that model and the changes in it automatically; performance considerations were written off as "premature optimization", because the application is supposed to be small-scale -- in the best spirit of agile development we would care about scaling it up when we get to that point.
  • Spring: nothing better for wiring all the rest together; also, Spring's declarative transactions via AOP enable us to use this aspect of J2EE without placing extra requirements on the container (the target platform is Tomcat, representing the lowest common denominator among Java application servers)
  • JSF: as the CRUD-type functionality requires sophisticated editing forms with in-line fields for dependent records, JSF and Facelets seemed the best technology for creating the edit forms and making them maintainable through using Facelet compositions; another aspect of Facelets is that this technology provides for using compositions for reusable parts of views, and for giving all the pages a common structure.

  • Spring MVC is a good supplement to the rather heavy view technology that is JSF: one might -- and usually does -- want to provide a simple controller or two for some special screens or features (AJAX/JSON, PDF, etc.) and not jump through JSF hoops to do that, while using a general-purpose MVC framework; Spring MVC can also provide for JSF navigation definitions' lack of scalability.

  • Spring Web Flow  is really something that one is almost forced to use given the previous choices (Spring + Facelets). JSF on its own is, sadly,  disappointing in everything except view composition mechanics. JSF/Facelets do not seem to scale as a complete view technology: its view-centric approach means it is unnecessarily difficult to provide for initialization, its navigation definition syntax is too verbose to maintain without heavy tools, and, finally, the view needs a mechanism for saving state that makes objects survive longer, than a single request-response, but not for the whole duration of a user session; a shopping cart is a typical example. Spring Faces and Spring Web Flow (SWF) provide for these shortcomings and add a lot more. That SWF is really solving a real life problem, rather than providing yet another idiosyncratic way of creating a Java web application, is indirectly confirmed by the fact that other frameworks aiming at reasonable completeness also provide work-flow features (I am thinking of JBoss Seam), and also do it with JSF/Facelets.

  • JAX-WS is simply the most modern -- and widely supported -- way of exposing services for integration. See one of the later articles on how to best expose Spring beans as JAX-WS services when using a minimal Servlet container such as Tomcat.

  • Maven is almost a must given the above mix of products. I have seen projects that were using Ant or other build tools being compelled to almost re-invent Maven just to maintain a dependency list: the number of libraries coming from different projects needed to use what has been listed above is many dozens, and each library comes in many different versions, some of which are not compatible with the versions that are required by other libraries. Apart from this -- already compelling -- need to be able to maintain a list of all the dependencies and dependencies of dependencies, Maven provides for simpler build definitions if the project is using a standard layout (and nothing would stop it from using that when starting from scratch). On top of that Maven provides a cheap solution for maintaining a firm-wide repository of code artefacts where each artefact (.jar, .war, ...) could be stored with a version number (so that several versions could be maintained) and also associated with source code archives (so that a framework library could be used by developers with source code for browsing and de-bugging). This last is again something that at least one of the major companies that I had been working for in the past had created in-house just to solve internal software distribution problems; Maven's repositories provide minimal working solution out of the box.