Cmobilecom AF 5.19 Developer Guide

7.1 Entity Backing Bean

An EntityBackingBean, simply called "Entity Bean", is the backing bean for an entity type for managing and searching entities interactively. It uses JPA for persistence, and property annotations for entity presentation and query.

Entity Bean Lifecycle

The lifecycle of creating an EntityBackingBean:

	Call EntityInitializer (if any, before prepareEntity)
		 |
	Prepare Entity
		 |
	Call EntityInitializer (if any, after prepareEntity)
		 |
	Authorize Access (access control)
		 |
	Build ViewConfig
		 |
	Build EntityProperty Model
		 |
	Call EntityInitializer (if any, after building EntityProperty Model)
		 |
	Build Header/Footer Menus
After an EntityBackingBean is created, it can be added to a region of root ContainerBean or DialogBean in order to be rendered on client device. With an EntityBackingBean, user can create a new entity, view or modify an existing entity, or perform query/report depending on the persistent state of the entity and current EntityBackingBean's viewType and mode. When a bean is removed from page containerBean, it will be garbage collected.

For the lifecycle of persisting entity (create a new entity or update an existing entity), see section Transaction Interceptor.

Persistence Annotations

For an entity type to be managed by Cmobilecom JPA, it must be annotated by JPA standard. see Entity Annotations. For example,

@Entity(name = "Employee")
@Table(name = "Employee")
public class Employee extends PersistenceEntityBase implements NormativeId {
	public enum Type {
		FULL_TIME,
		PART_TIME
	}
	
	private String name;
	private String nid; // employee id
	private Type type;
	private Date hiredDate;
  
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	public Long getId() {  
		return id;
	}
	
	@Column(nullable=false, length=30)
	public String getName() {
		return name;
	}
  
	public void setName(String name) {
		this.name = name;
	}
  
	@Column(nullable=false, unique=true, length=10)
	public String getNid() {
    	return nid;
	}
  
	public void setNid(String nid) {
    	this.nid = nid;
	}

	@Column(nullable=false)
	@Enumerated(value=EnumType.ORDINAL)
	public Type getType() {
		return type;
	}

	public void setType(Type type) {
		this.type = type;
	}

	@Column(nullable=false)
	@Temporal(value=TemporalType.DATE)
	public Date getHiredDate() {
		return hiredDate;
	}

	public void setHiredDate(Date hiredDate) {
		this.hiredDate = hiredDate;
	}
  
	public String toString() {
		return nid + "/" + name;
	}
}
For an entity type to be managed by Cmobilecom AF for entity creation, edit, view and query, its EntityBackingBean must be defined. For example,

public class EmployeeBean extends EntityBackingBean<Employee> {

	@Properties({
		@Property(name="nid", view={ViewType.ALL}),
		@Property(name="name", view={ViewType.ALL}),
		@Property(name="hiredDate", view={ViewType.ALL}),
		@Property(name="type", view={ViewType.ALL},
				renderStyle=@RenderStyleDef(style=RenderStyle.SELECT_ONE_MENU))
	})
	@Override
	public boolean hasPropertyAnnotations() {
		return true; 
	}
	
	@Override
	public List<SelectItem> getPropertySelectItems(
			EntityProperty<Employee> property) throws SystemException {
		ChoiceType choiceType;
		String propertyName = property.getName();

		if (propertyName.equals("type"))
			choiceType = HrModule.EMPLOYEE_TYPE;
		else
			return super.getPropertySelectItems(property);

		return SelectItemListProvider.getInstance().
			getSelectItems(choiceType, property);
	}
}
EntityBackingBean has built-in functionalities of entity persistence, query, editing and presentation using entity and property annotations.

View Config

ViewConfig specifies the content and layout of a bean is rendered on UI view.
	                              ViewConfig
	                               /      \
	         PersistenceDataViewConfig   MenuViewConfig
	           /                  \
     EntityViewConfig     EntityListViewConfig
An EntityViewConfig can be provided when creating an EntityBackingBean, and it can be updated during bean initialization.

	@Override
	protected EntityViewConfig buildViewConfig(T entity) throws SystemException {
		EntityViewConfig viewConfig = super.buildViewConfig(entity);
		viewConfig.setPropertiesToHide(...);

		return viewConfig;
	}
An EntityListViewConfig can be provided when creating an EntityListBackingBean, and it can be updated during bean initialization.

	@Override
	protected EntityListViewConfig buildEntityListViewConfig(EntityListBackingBean<T> entityListBackingBean,
  			EntityListViewConfig viewConfig) throws SystemException {
  		viewConfig = super.buildEntityListViewConfig(entityListBackingBean, viewConfig);
  		viewConfig.setPropertiesToShow(...);

  		return viewConfig;
	}

Build Property Model

As seen from the example EmployeeBean above, An EntityBackingBean has a list of Property annotations for entity view, which supports view inheritance and overrides. see Property Annotations for details.

The list of properties shown on entity view can be configured in View Config.

Custom properties can be annotated in the same way as regular property annotations (custom=true), or they can be added dynamically after entity property model is built.

@Override protected void postRefreshEntityPropertyModel(T entity) throws SystemException { super.postRefreshEntityPropertyModel(entity); List<EntityProperty<T>> entityPropertyList = getEntityPropertyList(); EntityProperty<T> fooProperty = new EntityProperty(this, "foo", new RenderStyle(RenderStyle.INPUT_TEXT); fooProperty.setCustomProperty(true); entityPropertyList.add(fooProperty); }

By default, an EntityBackingBean has

More menu nodes can be added if needed. The following example, add menu node(Approve) in view mode.


	private static final String COMMAND_APPROVE = "Approve";

	@Override
	protected void addMenuNodes(ModeType modeType) throws SystemException {
		super.addMenuNodes(modeType);
		
		if (modeType.equals(ModeType.VIEW)) {   		
  			AccessControlAccessorWrapper acAccessor = getAccessControlAccessor();
  	  		User currentUser = this.getAuthenticatedUser();
  	  
  	  		if (acAccessor.isUserHasPermission(currentUser, "HR", "ApproveEC")) {
   	  			MenuBean menuBean = getMenuBean(COMMAND_APPROVE, false);
  	  			if (menuBean != null) {
  	  				MenuNode approveMenuNode = new MenuNode(menuBean,  
  	  					COMMAND_APPROVE, RenderStyle.COMMAND_BUTTON);
  	  				approveMenuNode.setIcon(MenuNode.UI_ICON_CHECK);
  	  				menuBean.add(approveMenuNode); 
  	  			}
			}
		}
	}
Override clickMenuNode() to handle menu node actions.

	@Override
	public PageNavigation clickMenuNode(MenuNode menuNode) throws SystemException {
		String command = menuNode.getCommand();
     
		if (command.equals(COMMAND_APPROVE)) { 	    	
			// handle "Approve" command
			return null;
		}
    
		return super.clickMenuNode(menuNode);
	}
Override header or footer MenuViewConfig to show/hide commands and specify which menu nodes are shown in header or footer menu.

	@Override
	protected MenuViewConfig getHeaderMenuViewConfig() {
		MenuViewConfig viewConfig = MenuViewConfig(MenuViewConfig.MenuStyle.MENU_BAR, RenderStyle.COMMAND_LINK, true);
		// show some menu nodes
		viewConfig.setMenuNodesToShow(...);
		viewConfig.addMenuNodesToShow(...);
  	
		//or hide some menu nodes
		viewConfig.setMenuNodesToHide(...);
		viewConfig.addMenuNodesToHide(...);
  	
		return viewConfig;
	}
	
	@Override
	protected MenuViewConfig getFooterMenuViewConfig() {
		MenuViewConfig viewConfig = MenuViewConfig(MenuViewConfig.MenuStyle.MENU_BAR, RenderStyle.COMMAND_BUTTON, false);
		// show some menu nodes
		viewConfig.setMenuNodesToShow(...);
		viewConfig.addMenuNodesToShow(...);

		//or hide some menu nodes
		viewConfig.setMenuNodesToHide(...);
		viewConfig.addMenuNodesToHide(...);
  	
		return viewConfig;
	}
Another way to specify whether a command is supported, override the isCommandSupported(...) method. If a command is not supported, it will not be in header or footer menu.

  	@Override
	protected boolean isCommandSupported(PersistenceDataBackingBean<T> backingBean, String command) throws SystemException {
	
	}
The nested ViewConfig name for header menu is "headerMenu", and the nested ViewConfig name for footer menu is "footerMenu". And they can also be accessed by:

	MenuViewConfig headerMenuViewConfig = entityViewConfig.getHeaderMenuViewConfig(true);
	MenuViewConfig footerMenuViewConfig = entityViewConfig.getFooterMenuViewConfig(true);
So they can be configured in buildViewConfig().

Entity Type Context Menu

By default, an entity type has a context menu including create, import and query. To add more menu nodes,

	@Override
	public void addContextMenuNodes(TypedMenuNodeContextMenuBean menuBean) throws SystemException {
		super.addContextMenuNodes(menuBean);
  	
		// add more menu nodes
	}
Override clickContextMenuNode() to handle context menu node actions:

	@Override
	public PageNavigation clickContextMenuNode(MenuNode menuNode) throws SystemException {
		String command = menuNode.getCommand();
		if (command.equals("aCommand")) {
 			// handle action
		}
		
		return super.clickContextMenuNode(menuNode);
	}

Handle Partial Behavior Events

If an EntityProperty supports partial behavior and when its value is changed, a PartialBehaviorEvent will be sent to server. Handle property PartialBehaviorEvent of a backing bean by overriding the following method:

	@Override
	public PageNavigation handlePartialBehaviorEvent(PartialBehaviorEvent event,
		PersistenceDataBackingBean<T> backingBean,
		T entity, EntityProperty<T> property) throws SystemException {
	}
For example, set Employee default name when its associated user is set or changed.

public class EmployeeBean extends EntityFormSupportBean<Employee> {
	@Override
	public PageNavigation handlePartialBehaviorEvent(PartialBehaviorEvent event, PersistenceDataBackingBean<Employee> backingBean,
			Employee entity, EntityProperty<Employee> property) throws SystemException{
		String propertyName = property.getName();
		if (propertyName.equals(Employee.PROPERTY_USER)) {
			// set employee name
			User user = entity.getUser();
			if (user != null) {
				entity.setName(user.getName());

				// add name as a render target
				addPropertyRenderTarget(Employee.PROPERTY_NAME, entity);
			}
			return null;
		}

		return super.handlePartialBehaviorEvent(event, backingBean, entity, property);
	}
}
ExpenseClaimItem list bean: update expense total when an item expense is changed.

public class ExpenseClaimItemBean extends EntityBackingBean<ExpenseClaimItem> {
	@Override
	public PageNavigation handlePartialBehaviorEvent(PartialBehaviorEvent event,
			PersistenceDataBackingBean<ExpenseClaimItem> backingBean,
			ExpenseClaimItem entity, EntityProperty<ExpenseClaimItem> property) throws SystemException{
		String propertyName = property.getName();
		if (propertyName.equals(ExpenseClaimItem.PROPERTY_EXPENSE) &&
				event.getPartialBehavior().isValueChange()) {
			// update statisticsRow: expense total (render target)
			((EntityListBackingBean<ExpenseClaimItem>)backingBean).
				refreshStatisticsEntityPropertyValue(property);
			return null;
		}

		return super.handlePartialBehaviorEvent(event, backingBean, entity, property);
	}
}
A menu node can have an EntityBackingBean for user to input data as arguments for the command, and the input data EntityBackingBean supports partial behaviors for its properties. Override the following method to handle property PartialBehaviorEvent(s) of a menu node's input data bean.

	@Override
	public <M extends PersistenceEntity> PageNavigation handlePartialBehaviorEvent(
  		PartialBehaviorEvent event, MenuNode menuNode, EntityBackingBean<M> inputDataBackingBean, 
		EntityProperty<M> property) throws SystemException {
	  	
	}

Entity Fetch Graph

An entity type may define some properties and associated entities as lazy. An EntityFetchGraph defines the entities and properties to be fetched when retrieving an entity or a list of entities from persistence. For example, to define a fetch graph for showing an ExpenseClaim, override the following method in its EntityBackingBean:
  
	@Override
	protected EntityFetchGraph<ExpenseClaim> getEntityFetchGraph(ExpenseClaim entity) {
		EntityFetchGraph<ExpenseClaim> fetchGraph = super.getEntityFetchGraph(entity);
		
		fetchGraph.addProperty("expenseClaimItems", JoinType.LEFT);
		fetchGraph.addPropertyPath("employee.address", JoinType.LEFT);
		
		return fetchGraph;
	}
To define a fetch graph for showing a list of ExpenseClaim(s), override the following method in its EntityBackingBean:

	@Override
	protected EntityFetchGraph<ExpenseClaim> getEntityFetchGraph(EntityListBackingBean<xpenseClaim> entityListBackingBean) {
		EntityFetchGraph<ExpenseClaim> fetchGraph = super.getEntityFetchGraph(entityListBackingBean);
		
		fetchGraph.addProperty("employee", JoinType.INNER);
		return fetchGraph;
	}
	

Form Bean Properties

An entity property can be of a basic type, embedded type, entity type, or collection of basic/embedded/entity member type. For example, If an entity property is rendered as an EntityBackingBean or EntityListBackingBean, then the property is called a "FormBeanProperty" that has an enclosed bean (called "FormBean"). So the value of a FormBeanProperty can be managed inside its parent enclosing bean. An entity FormBeanProperty is annotated with type EntityFormBeanProperty.class. For example,

@Properties({
    @Property(name=ExpenseClaim.PROPERTY_EXPENSE_CLAIM_ITEMS, 
    	entityPropertyType=EntityFormBeanProperty.class,
    	nullAsDeleteProperties={ExpenseClaimItem.PROPERTY_DATE},
        view={ViewType.ENTITY})
})        
A FormBean viewConfig can be set as a nested ViewConfig of its parent bean's ViewConfig.
	viewConfig.setFormBeanViewConfig(formBeanPropertyName, formBeanViewConfig);

Property Select Items

For a property whose render style is selecting one or many from a list of select items, its EntityBackingBean needs to provide the select items that can be static or dynamic. Dynamic select items will not be cached.

Get property select items that is static and will be cached.


	@Override
	public List<SelectItem> getPropertySelectItems(EntityProperty<T> property) throws SystemException {
		String propertyName = property.getName();
		if (propertyName.equals("type")) {
			...
		}
			
		return super.getPropertySelectItems(property);
	}
Get property dynamic select items that will not be cached.

	@Properties({
		@Property(name="type", view={ViewType.ALL},
			renderStyle=@RenderStyleDef(style=RenderStyle.SELECT_ONE_MENU, dynamicSelectItems=true))
	})

	@Override
	public List<SelectItem> getDynamicSelectItems(PersistenceDataBackingBean<T> backingBean, 
			T entity, EntityProperty<T> property) throws SystemException {
		String propertyName = property.getName();
		if (propertyName.equals("type")) {
			...
		}
		
		return super.getDynamicSelectItems(backingBean, entity, property);
	}

Note that the values of SelectItems must be of String type. The string value will be converted to its property type when property value is updated(Model Update). Refer to SelectItemListProvider class for methods to create SelectItems from various data values.

SelectItem labels are of type I18NName, and should not be localized. The labels will be translated using resource bundles during render phase.

Property Dynamic RenderStyle

When an EntityProperty is created, its render style needs to be specified. But if its render style is dynamic and can be different from entity to entity, then its EntityBackingBean needs to override getDynamicRenderStyle(...) method:

	@Properties({
		@Property(name="type", view={ViewType.ALL},
			dynamicRenderStyle=true,
			renderStyle=@RenderStyleDef(style=RenderStyle.SELECT_ONE_MENU))
	})

	@Override
	public RenderStyle getDynamicRenderStyle(PersistenceDataBackingBean<T> backingBean, 
		T entity, EntityProperty<T> property) throws SystemException {
		
		String propertyName = property.getName();
		if (propertyName.equals("type")) {
			...
		}
		
		return super.getDynamicRenderStyle(backingBean, entity, property);
	}

Property Dynamic Value

A property value can be dynamic, which means that its value is different from the property value read from entity or the property is a custom property with dynamic value.

	@Properties({
		@Property(name="type", view={ViewType.ALL},
			dynamicValue=true)
	})
	
	@Override
	public Object getPropertyDynamicValue(PersistenceDataBackingBean<T> backingBean, 
		T entity, EntityProperty<T> property) throws SystemException {
		
		String propertyName = property.getName();
		if (propertyName.equals("type")) {
			...
		}		
		return super.getPropertyDynamicValue(backingBean, entity, property); 
	}

Entity Validation

An entity will be validated before created or updated in persistence. In addition to property value validation, entity validation can be any logic. For example, calculate total, change the values of related entities, etc. Note that entity unique key restrictions and property values have been validated based on their annotations.

	@Override
	public void validate() throws SystemException {
		super.validate();
		...
	}
In query view, the validate() method is also called before search.

See Validation.

Create Entity Lifecycle

When the entity of an EntityBackingBean is not persisted, and mode is CREATE, the entity will be created on persistence when user clicks "Create" command.

Lifecycle of creating entities on persistence.

	Begin Transaction
		|
	Validate Entity
		|
	Access Control (check permission)
		|
	prePersist (called before added to PersistentContext)
		|
	Persist Entity (added to PersistentContext)
		|
	postPersist (called after added to PersistentContext)
		|
	Commit Transaction
		|
	Call Transaction Listeners (called after transaction is committed)
Add business logic in validate(), prePersist(), postPersist() and/or transaction listeners. All the changes to any entities in the lifecycle methods before commit will be in the same transaction. There can be many entities created on persistence due to persist cascade and other application logic. Transaction listeners are used to refresh views after commit.

preCreate: called before the entity is added to PersistenceContext. entity id is not available at this point if it has an id generator.


	protected boolean preCreate(T entity, ActionDescriptor actionDescriptor,
  		PersistenceEntityManager peManager) throws SystemException {
	}

postCreate: called after the entity is added to PersistenceContext and entity id is generated if it has an id generator.

 
	protected void postCreate(T entity, ActionDescriptor actionDescriptor,
  		PersistenceEntityManager peManager)throws SystemException {

	}

Update Entity Lifecycle

When the entity of an EntityBackingBean is persisted, and mode is EDIT, the entity will be updated on persistence when user clicks "Apply Change" command.

Lifecycle of updating entities on persistence.

	Begin Transaction
		|
	Validate Entity
		|
	preMerge (called before merge)
		|
	Merge Entity (added to PersistenceContext)
		|
	postMerge (called after merge)
		|
	Commit Transaction
		|
	Call Transaction Listeners (called after transaction is committed)

There is no access control in the lifecycle because the EntityBackingBean is enabled for editing only when the user has the permission to do so. That is, access control is enforced before the lifecycle starts.

Add business logic in validate(), preMerge(), postMerge() and/or transaction listeners. All the changes to any entities in the lifecycle methods before commit will be in the same transaction. There can be many entities merged or created on persistence due to merge cascade and other application logic. Transaction listeners are used to refresh views after commit.

preMerge: called before entity is merged to PersistentContext.

	protected T preMerge(T entity, ActionDescriptor actionDescriptor,
  		PersistenceEntityManager peManager)throws SystemException {  	
	}
postMerge: called after entity is merged to PersistentContext.
  
	protected void postMerge(T entity, ActionDescriptor actionDescriptor,
  		PersistenceEntityManager peManager) throws SystemException {
	}

Delete Entity Lifecycle

When the entity of an EntityBackingBean is persisted, mode is VIEW, and user has the permission to delete the entity, the entity will be deleted from persistence when user clicks "Delete" command.

Lifecycle of deleting entities on persistence.

	Begin Transaction
		|
	Access Control
		|
	preRemove (called before remove)
		|
	Remove Entity (remove entity in Persistent Context)
		|
	postRemove (called after remove)
		|
	Commit Transaction
		|
	Call Transaction Listeners (called after transaction is committed)

Add business logic in preRemove(), postRemove() and/or transaction listeners. All the changes to any entities in the lifecycle methods before commit will be in the same transaction. There can be many entities deleted on persistence due to remove cascade and other application logic. Transaction listeners are used to refresh views after commit.

preRemove: called before removing the entity in persistent context.

	@Override
	protected boolean preRemove(T entity, ActionDescriptor actionDescriptor,
  		PersistenceEntityManager peManager)throws SystemException {
  		
	}
postRemove: called after the entity is removed in persistent context.

	@Override
	protected void postRemove(T entity, ActionDescriptor actionDescriptor,
  		PersistenceEntityManager peManager)throws SystemException {

	}

Statistics Properties

A list of statistics properties can be defined that will be computed in the list view. The most common statistics function is sum and average. The statistics row will be shown at the bottom of entity list.

	@Override
	public StatisticsProperty[] getStatisticsProperties() {
		return new StatisticsProperty[]{
			new StatisticsProperty(ExpenseClaimItem.PROPERTY_EXPENSE, Aggregate.SUM)			
		}; 
	}

Entity Property Layout

The layout of entity properties is specified in EntityBackingBean viewConfig. For example, entityViewConfig.setLayoutType(LayoutType.FLOW);

Entity bean layout type can be GRID_NAME_VALUE, LAYOUT_CODE, TABLE, FLOW, TAB_VIEW, LIST_VIEW, COLUMN_VIEW or GRID. The default layout is GRID_NAME_VALUE with property name column and property value column.

For tab view, top-level properties should be property groups or FormBean properties. For layout code, form designs allow end users to customize/create layouts. But if form design is not supported, override getLayoutCode() to provide the layout code. Layout code is a piece of HTML code that can embed expressions. For example,


	@Override
	public String getLayoutCode() throws SystemException {
		return "<h1>Expense Claim</h1> +
				"#{bundle.Employee}: #{employee}<br/>" +
				"#{OBJECT:expenseClaimItems}
" + "#{creator} #{createdDate}" + }

Entity Name Resolver

For an EntityProperty (in showing mode) whose value is an entity or a collection of entities, entity names represent the associated entities if the property is not rendered as a FormBeanProperty. For example, the entity name for a Department is department name with its manager name. By default, entity name is entity.toString(). Override the following method to provide a custom EntityNameResolver.
public class DepartmentBean extends HierarchyEntityBackingBean<Department> {

	@Override
	public <R extends PersistenceEntity> EntityNameResolver<Department, R> getEntityNameResolver() {
		return new EntityNameResolver<Department, R>() {
			@Override
			public I18NName getEntityName(Department department, EntityProperty<R> entityProperty) {
				return new I18NName(department.getName() + "(" + department.getManager() + ")", true);
			}
		};
	}
}

Entity Import/Export as XML

See Entity Export/Import for detail.

Progress Bar

The action of a MenuNode can be a long-running task. A progress bar is the user-friendly way to show that an action is being executed and the UI is blocked until the task is completed.

Deleting all the entities of all pages can be a long time task, so progress bar is automatically enabled.

A default progress bar will be enabled if the menu node is set to show progress bar.

	MenuNode menuNode = new MenuNode(...);
	menuNode.setShowProgress(true);
The following method can be overridden to customize progress bar. Return null if progress bar is disabled for the menu node.

	@Override
	public ProgressDescriptor getProgressDescriptor(PersistenceDataBackingBean<T> backingBean,
			MenuNode menuNode) throws SystemException {

	}
DialogEntity List Backing BeanFrames / No Frames