Tuesday, 15 December 2009

Master-Detail on One Page

With all Seam examples of CRUD for Master-Detail relationships that I've seen so far, we always rely on Modal Panels to edit the Detail entities. (My own example posts are here, here, here, and here) However the Modal Panel approach has it's own limitations, mostly around navigation when the user starts to create a new Detail and then changes their mind (as they do).

In any case, popup modal panels tend to disrupt the flow of the page. What would be good is to be able to edit both Master and Detail entities on a single page. The following example produces a screen that looks like:


So to the code. This uses exactly the same backing bean as the other examples - a conversation-scoped edit manager - with no changes at all. Code for that is available here.

The page.xml file looks like:

<?xml version="1.0" encoding="UTF-8"?>
<page xmlns="http://jboss.com/products/seam/pages"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.1.xsd"
no-conversation-view-id="/MasterList.xhtml"
login-required="true">

<begin-conversation join="true" flush-mode="MANUAL"/>

<action execute="#{masterEditor.wire}" on-postback="false"/>

<param name="masterFrom"/>
<param name="masterId" value="#{masterEditor.masterId}"/>


<navigation from-action="#{masterEditor.persist}">
<rule>
<end-conversation/>
<redirect view-id="/OnePageEdit.xhtml"/>
</rule>
</navigation>

<navigation from-action="#{masterEditor.update}">
<rule>
<end-conversation/>
<redirect view-id="/OnePageEdit.xhtml"/>
</rule>
</navigation>

<navigation from-action="#{masterEditor.remove}">
<rule>
<end-conversation/>
<redirect view-id="/MasterList.xhtml"/>
</rule>
</navigation>

</page>


The XHTML file is:

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich" template="layout/template.xhtml">

<ui:define name="body">

<h:form id="master" styleClass="edit">

<rich:panel>
<f:facet name="header">Master Details</f:facet>

<s:decorate id="masterDescriptionField" template="layout/edit.xhtml">
<ui:define name="label">Master description</ui:define>
<h:inputText id="masterDescription" size="100" maxlength="100"
value="#{masterEditor.instance.masterDescription}">
<a:support event="onblur" reRender="masterDescriptionField"
bypassUpdates="true" ajaxSingle="true" />
</h:inputText>
</s:decorate>

<div style="clear: both">
<span class="required">*</span>
required fields</div>

</rich:panel>

<a:region>

<h:panelGrid id="detailEdit" columns="2" columnClasses="detailList,detailDetail" width="100%">

<h:panelGroup>

<rich:dataTable id="detailListTable" var="_detail" value="#{detailListFromMaster}"
width="200px" styleClass="scriptPagesList"
onRowMouseOver="this.style.backgroundColor='#EAEAEA';this.style.cursor='pointer'"
onRowMouseOut="this.style.backgroundColor='#{a4jSkin.tableBackgroundColor}'">
<h:column>
<f:facet name="header">
<ui:include src="layout/sort.xhtml">
<ui:param name="propertyLabel" value="Id" />
</ui:include>
</f:facet>
<h:outputText value="#{_detail.id}" />
</h:column>
<a:support event="onRowClick"
reRender="detailEditDetail"
action="#{masterEditor.select()}">
</a:support>
</rich:dataTable>

<div style="clear: both" />

<div class="actionButtons">
<a:commandButton id="NewDetailButton"
value="New Detail"
action="#{masterEditor.createNewDetail}"
reRender="detailEdit"
styleClass="buttonNoBorders">
<s:conversationId />
</a:commandButton>
<a:commandButton id="DeleteDetailButton"
value="Delete Detail"
action="#{masterEditor.removeDetail(currentDetail)}"
enabled="#{currentlySelectedDetail!=null}"
reRender="detailEdit"
styleClass="buttonNoBorders">
<s:conversationId />
</a:commandButton>
</div>

</h:panelGroup>

<h:panelGroup>
<rich:panel id="detailEditDetail" styleClass="panelNoBorder">
<f:facet name="header">Detail Details</f:facet>

<s:div rendered="#{currentlySelectedDetail!=null}">

<s:decorate id="detailDescriptionField" template="layout/edit.xhtml">
<ui:define name="label">Detail description</ui:define>
<h:inputText id="detailDescription"
required="true"
size="100"
maxlength="100"
value="#{currentlySelectedDetail.detailDescription}">
<a:support event="onblur" reRender="detailDescriptionField" bypassUpdates="true" ajaxSingle="true" />
</h:inputText>
</s:decorate>

</s:div>
</rich:panel>
</h:panelGroup>

</h:panelGrid>

</a:region>

<rich:panel styleClass="panelTopBorderOnly"
bodyClass="panelTopBorderOnlyBodyClass">
<div class="actionButtons">
<h:commandButton id="save"
value="Save" action="#{masterEditor.persist}"
disabled="#{!masterEditor.wired}"
rendered="#{!masterEditor.managed}" />

<h:commandButton id="update"
value="Save" action="#{masterEditor.update}"
rendered="#{masterEditor.managed}" />

<h:commandButton id="delete"
value="Delete"
action="#{masterEditor.remove}"
immediate="true"
rendered="#{masterEditor.managed}" />

<s:button id="cancelEdit"
value="Cancel"
propagation="end"
view="/MasterList.xhtml" />
</div>

<div style="clear: both">
<span class="required">*</span>
required fields
</div>

</rich:panel>

</h:form>

</ui:define>

</ui:composition>



You'll note a number of CSS classes which modify the richfaces appearance:

.panelTopBorderOnly {
margin-top:10px;
border-left:none;
border-right:none;
border-bottom:none;
}
.panelTopBorderOnlyBodyClass {
padding-top:0px;
}

.detailList {
vertical-align:top;
width:200px;
height:200px;
overflow:scroll;
}

.detailDetail {
vertical-align:top;
}

input.buttonNoBorders {
margin:1px 1px 1px 0;
padding:0px;
}


Two key points:
  • The ajax region was required to make the insert and delete of the Detail entities work when dealing with a freshly created Master entity.
  • Note the buttons - these are done differently to the seam-gen buttons, because other the creation of a new Detail lost changes to the Master entity - if any.
Really interested to see what people think.

Friday, 11 December 2009

When the cloud is not really a cloud?

In reading this article on Information Week about the Amazon EC2 North Virginia outage, the first thing that came to mind is disappointment. Disappointment that the cloud turns out to be just another data centre. We have quickly come to expect that we should be insulated from underlying hardware issues. Putting software in the cloud should mean that it continues to operate, regardless of an issue with one piece of hardware. Redundancy and failover should be built into the system.

Apparently not. I guess this is a lesson for application providers. We cannot rely on the IAAS vendors, and will need to look at geography as part of the underlying risk assessment process. Perhaps most people already do this, and it's just me that had this expectation?

Tuesday, 10 November 2009

Pay as you go Insurance

I recently read an interesting article by Stephen Dubner and Steven Levitt (of Freakonomics fame), published on the NY Times website. The article discusses a Pay As You Drive (PAYD) automobile insurance product offered by Progressive Insurance in Ohio (US). It's an interesting discussion bridging technology and product design, with the economics thrown in for good measure. The article is a little old (2008), but is well worth a read.

On the Life Insurance side, I recall many, many conversations with the Product guys, hearing of their desire for better information about consumers and the ability to price products accordingly. Of course there are often regulatory constraints to deal with...

Wednesday, 28 October 2009

Systems: Financials / ERP

In the world of Insurance, the use of Financials / ERP systems is often very limited. When you think about it, this is fairly commonsense. In most other industries, the organisation produces a physical product. ERP systems are designed to manage the purchase and sale of products, and all of the bits and pieces that go into that (e.g. Production Planning, Bill of Materials, Asset Management, etc). However in financial services, we deal with complex, intangible products, that are heavy on calculations and business rules. The sales channels are equally complex.

Organisations will always have a General Ledger system, into which the Policy Administration, Claims, and Agency / Commission systems will directly feed GL journals. Other modules may be used if the organisation is sufficiently large - Accounts Payable, Asset Management, and of course Human Resources modules. But the core linkage with the other systems will normally be the GL.

Saturday, 3 October 2009

Key limitations of Seam's IdentityManager and IdentityStore implementation

There are a couple of key limitations imposed by the implementation of the Seam IdentityManager and IdentityStore classes. The IdentityManager is supposed to be the main component for managing all Users and Roles. To create a User, you should call IdentityManager.createUser(). To enable/disable a User, or add/remove Roles, again call the appropriate IdentityManager functions.

The IdentityManager then uses an IdentityStore to manage the interface to a database or external data source (e.g. LDAP). The JpaIdentityStore class assumes storage in a series of database tables in the application's DataSource. To make this work there are a series of annotations that identify the User and Role entities, and specific fields on them (e.g. user login, password, etc).

And in case there are other attributes on the User entity (secret question, date of birth, etc), there is a PrePersistUser event raised by JpaIdentityStore, which you can hook into and set these other fields.

But therein lies the problem. The IdentityManager doesn't provide any means to Update a user after it's been created. So there is no clear way to save changes to a User through the IdentityManager.

So far, I've ended up side-stepping the IdentityManager completely. Instead, a customised UserManager component which started life as an EntityHome. Then a custom Authenticator to use the User entity to validate user name and password.

While this does tie you into a custom User and Group/Role implementation (which means switching to LDAP, etc is more difficult), it bypasses the limitations of the current IdentityManager and IdentityStore interfaces. Until the IdentityStore interface is expanded to include additional User attributes, it really doesn't look like it will cut it in an enterprise-level environment. Of course in an enterprise environment you're probably more likely to be dealing with custom integration to the organisation's existing security framework, so this is probably not a big deal.

Friday, 2 October 2009

Managing IT Geeks

A very insightful article on Computerworld about managing IT professionals. How many of us have heard of managing IT staff being compared to herding cats? This article makes a number of salient points about the motivation of IT staff, and how this impacts on management of these professionals.

Monday, 14 September 2009

Simplicity in Insurance Solutions

This article on Insurance Networking News makes the case for simplicity or clarity in marketing of Insurance systems. Too much overlap in system modules, or a lack of understanding of the customer's point of view means that the way systems are described leaves the customer's decision makers confused. Faced with a business problem, they struggle to make sense of what module will address the issue.

However the same ideas presented in the article can be extended to the systems themselves. Overly "flexible" systems become overly complex, with all of the challenges that come along with that. Flexibility is normally prized, as a way of improving time to market and reducing maintenance costs (enhancements, fixes, etc). And of course reducing dependence on the vendor with the accompanying costs. However complexity in the system comes with an increase in design, testing, and training for staff. This is often missed and/or underestimated. A complex system will require significantly more testing in the implementation process - as a direct result of the complex interplay of functions within the system.

As with most things, we need to find a balance in managing flexibility versus complexity and therefore risk.

Tuesday, 8 September 2009

Filtering details in Master-Detail edit

I came across an article about another FetchType that may be useful in reducing the number of Detail rows loaded when paging and/or filtering the Detail List (see previous posts about Master-Detail editing and Filtering).

Hibernate has the Lazy fetch strategy, which is fairly standard. Under this strategy, Hibernate will load the Detail rows only when we first access the list. The problem is it will load all rows from the database. If we are detail with thousands of Detail rows, this could be a problem.

Hibernate also has an "Extra Lazy" fetching strategy. This will only load individual rows when they are accessed (as opposed to Lazy, which loads the entire collection). This seems to be more what we are looking for if we need to filter and paginate a List of Detail rows.

It's done at the mapping level, so it's going to require some investigation to see how this affects regular processes.

Friday, 4 September 2009

SaaS for insurance, part two

I have just read an article on the suitability of SaaS for different markets, which dovetails nicely with the previous post here.

To quote a key part:
"SaaS applications fare better in larger markets where most buyers are satisfied with the same features. You don't want to build a solution that depends on satisfying the requirements of a small, quirky market."

Exactly.

Thursday, 3 September 2009

SaaS for the Insurance Industry

An article published on Insurance Networking highlighted "Security" and "Ability to Customise" the system as two main drawbacks of SaaS in the context of the insurance industry.

I agree that Security is a major problem - who hasn't dealt with IT departments that are overly concerned about allowing customer data outside the firewall? And with good reason, given some of the regulations applied in various countries.

However in terms of the second problem (Customisation), I am less convinced. If you consider Salesforce.com (used by a number of large insurers, and supports significant integration and customisation) then you could argue that the second problem (customisation) is a non-issue. Instead, I think the comment reflects the nature of smaller-to-medium-sized SaaS offerings. It is unlikely that anyone will be able to convince 37Signals to customise their software for one company. But it's equally unlikely that an application of this size this would ever be considered a "Core" system for a large insurance company.

Feedback on the article prompted a second article that points out that SaaS may be more welcome in non-core parts of the business. This is true, but misses the point of the first article - there's no-one offering (or attempting to offer?) a core policy administration system on a SaaS basis with all that implies (cloud, multi-tenancy, charging model, etc). A possible business opportunity? Surely a difficult balance of business requirements to meet while doing so!

Thursday, 20 August 2009

Seam Master-detail and Pagination - 2nd Try

After further work and testing, I have a Master-Detail editing page, with built in pagination for the Detail instances. The scenario applies where a single Master has a large number of Details. For example, a General Ledger Account, which has many thousands or millions of Transactions.

Key is to avoid referring to the Master's mapped list of Details (or remove the Hibernate mapping entirely?). This will mean that Hibernate will not load the large number of Details. However, transaction management of the Detail instances becomes a problem, as I struggle to find a solution that retains Seam's transaction management, but has it including new Details as well as changed items.

The solution that follows does function. The normal editing of existing Details works as expected, with changes saved when the Save button is pushed. However any new Detail instances are saved immediately on closing the ModalPanel. This problem is because we're not adding the new Details into the Master (which would mean that they are persisted as part of saving the Master).

One solution would be to keep a local List of new Detail instances, and override the EntityHome update() and persist() functions to iterate the List and persist() the Detail entities. However going to this much trouble is making me wonder if there is not a simpler approach.

Anyway, here is the code for the revised example:

1. The EntityHome class:

package org.asr.md.session;

import javax.persistence.EntityManager;

import org.asr.md.entity.*;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.framework.EntityHome;

@Name("masterEditorWithDetailPaging")
@Scope(ScopeType.CONVERSATION)
public class MasterEditorWithDetailPaging extends EntityHome {

private static final long serialVersionUID = -7731494569738456413L;
@In EntityManager entityManager;
@In(create=true) DetailListForPaging detailListForPaging;

private String filterDescription;
private Detail currentlySelectedDetail;

//Getters and setters
public String getFilterDescription() { return this.filterDescription; }
public void setFilterDescription(String filterDescription) { this.filterDescription = filterDescription; }
public Detail getCurrentlySelectedDetail() { return this.currentlySelectedDetail; }
public void selectDetail(Detail detail) { this.currentlySelectedDetail = detail; }

public void createNewDetail() {
Detail detail = new Detail();
detail.setMaster(getInstance());
detailListForPaging.getResultList().add(detail);
currentlySelectedDetail = detail;
}

public void saveNewDetail() {
if (!entityManager.contains(currentlySelectedDetail)) {
entityManager.persist(currentlySelectedDetail);
}
}

public void cancelNewDetail() {
removeDetail(currentlySelectedDetail);
}

public void cancelChanges() {
entityManager.clear();
}

//Delete a detail item
public void removeDetail(Detail detail) {
detailListForPaging.getResultList().remove(detail);
entityManager.remove(detail);
}

public String filterDetails() {
detailListForPaging.setMasterInstance(getInstance());
detailListForPaging.setDetailDescription(filterDescription);
return "success";
}

public void setMasterId(Integer id) {
setId(id);
}

public Integer getMasterId() {
return (Integer) getId();
}

@Override
public String update() {
return super.update();
}

@Override
protected Master createInstance() {
Master master = new Master();
return master;
}

public void wire() {
getInstance();
}

public boolean isWired() {
return true;
}

public Master getDefinedInstance() {
return isIdDefined() ? getInstance() : null;
}
}


2. The DetailQuery class:

package org.asr.md.session;

import org.asr.md.entity.*;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.framework.EntityQuery;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;

@Name("detailListForPaging")
@Scope(ScopeType.CONVERSATION)
public class DetailListForPaging extends EntityQuery {

private static final long serialVersionUID = -427189818462003108L;
private static final String EJBQL = "select detail from Detail detail";
private static final String[] RESTRICTIONS = { "detail.master = #{detailListForPaging.masterInstance}",
"lower(detail.detailDescription) like concat(#{detailListForPaging.detailDescription},'%')", };

private String detailDescription = new String();
private Master master;
private boolean criteriaHaveChanged = true;

public DetailListForPaging() {
setEjbql(EJBQL);
setRestrictionExpressionStrings(Arrays.asList(RESTRICTIONS));
setMaxResults(25);
}

public List getResultList() {
//If the detailDescription is not set to something, then return an empty List.
if ((master == null) || (detailDescription.isEmpty())) {
return new Vector(0);
}

if (criteriaHaveChanged) {
setRestrictionExpressionStrings(Arrays.asList(RESTRICTIONS));
criteriaHaveChanged = false;
}
return super.getResultList();
}

public void setDetailDescription(String detailDescription) {
this.detailDescription = detailDescription;
this.criteriaHaveChanged = true;
}
public String getDetailDescription() { return this.detailDescription; }
public void setMasterInstance(Master master) {
this.master = master;
this.criteriaHaveChanged = true;
}
public Master getMasterInstance() { return this.master; }

}


3. The XHTML page:

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
template="layout/template.xhtml">

<ui:define name="body">

<rich:modalPanel id="detailEditWizard"
minHeight="200"
minWidth="500"
autosized="true">

<f:facet name="header"><h:outputText value="Edit Detail..." /></f:facet>
<f:facet name="controls">
<h:graphicImage value="/img/ico_close.gif"
styleClass="linkImage"
onclick="#{rich:component('detailEditWizard')}.hide()" />
</f:facet>

<h:form id="detailEditForm" styleClass="edit">

<h:messages globalOnly="true"/>

<s:decorate id="masterIdField" template="layout/edit.xhtml">
<ui:define name="label">Master id</ui:define>
<h:outputText id="masterId"
required="true"
value="#{masterEditorWithDetailPaging.currentlySelectedDetail.master.id}">
</h:outputText>
</s:decorate>

<s:decorate id="detailDescriptionField" template="layout/edit.xhtml">
<ui:define name="label">Detail description</ui:define>
<h:inputText id="detailDescription"
required="true"
size="100"
maxlength="100"
value="#{masterEditorWithDetailPaging.currentlySelectedDetail.detailDescription}">
<a:support event="onblur" reRender="detailDescriptionField" bypassUpdates="true" ajaxSingle="true"/>
</h:inputText>
</s:decorate>

<div class="actionButtons" style="clear:both">

<a:commandButton id="detailEditMPClose"
onclick="#{rich:component('detailEditWizard')}.hide()"
action="#{masterEditorWithDetailPaging.saveNewDetail()}"
reRender="detailListTable"
value="Save" />

<a:commandButton id="detailEditMPCancel"
onclick="#{rich:component('detailEditWizard')}.hide()"
action="#{masterEditorWithDetailPaging.cancelNewDetail()}"
reRender="detailListTable"
value="Cancel" />

</div>
</h:form>

</rich:modalPanel>

<h:form id="master" styleClass="edit">

<rich:panel>
<f:facet name="header">#{masterEditorWithDetailPaging.managed ? 'Edit' : 'Add'} Master</f:facet>

<s:decorate id="masterDescriptionField" template="layout/edit.xhtml">
<ui:define name="label">Master description</ui:define>
<h:inputText id="masterDescription"
size="100"
maxlength="100"
value="#{masterEditorWithDetailPaging.instance.masterDescription}">
<a:support event="onblur" reRender="masterDescriptionField" bypassUpdates="true" ajaxSingle="true"/>
</h:inputText>
</s:decorate>

<div style="clear:both">
<span class="required">*</span>
required fields
</div>

</rich:panel>

<rich:panel>
<f:facet name="header">Detail Filter</f:facet>
<s:decorate id="detailDescriptionFilterField" template="layout/edit.xhtml">
<ui:define name="label">Detail Description</ui:define>
<h:inputText id="detailDescriptionFilter"
size="100"
maxlength="100"
value="#{masterEditorWithDetailPaging.filterDescription}">
<a:support event="onblur" reRender="detailDescriptionFilterField" bypassUpdates="true" ajaxSingle="true"/>
</h:inputText>
</s:decorate>

<div class="actionButtons">

<h:commandButton id="filter"
value="Search"
action="#{masterEditorWithDetailPaging.filterDetails}"
disabled="#{!masterEditorWithDetailPaging.wired}"
rendered="#{masterEditorWithDetailPaging.managed}">
<s:conversationId />
</h:commandButton>

<a:commandButton id="doFiltering"
action="#{masterEditorWithDetailPaging.filterDetails()}"
reRender="detailListTable"
value="Filter">
<s:conversationId />
</a:commandButton>

</div>

</rich:panel>

<!-- Detail section -->
<rich:panel>
<f:facet name="header">Detail List</f:facet>
<div class="results" id="detailList">

<h:outputText value="The detail search returned no results."
rendered="#{empty detailListForPaging.resultList}"/>

<rich:dataTable id="detailListTable"
var="_detail"
value="#{detailListForPaging.resultList}">
<h:column>
<f:facet name="header">
<ui:include src="layout/sort.xhtml">
<ui:param name="propertyLabel" value="Id"/>
</ui:include>
</f:facet>
<h:outputText value="#{_detail.id}"/>
</h:column>
<h:column>
<f:facet name="header">
<ui:include src="layout/sort.xhtml">
<ui:param name="propertyLabel" value="Master id"/>
</ui:include>
</f:facet>
<h:outputText value="#{_detail.master.id}"/>
</h:column>
<h:column>
<f:facet name="header">
<ui:include src="layout/sort.xhtml">
<ui:param name="propertyLabel" value="Detail description"/>
</ui:include>
</f:facet>
<h:outputText value="#{_detail.detailDescription}"/>
</h:column>
<rich:column styleClass="action">
<f:facet name="header">Action</f:facet>
<a:commandLink id="editDetailMP"
action="#{masterEditorWithDetailPaging.selectDetail(_detail)}"
oncomplete="javascript:Richfaces.showModalPanel('detailEditWizard')"
reRender="detailEditForm"
value="Edit">
<s:conversationId />
</a:commandLink>
#{' '}
<a:commandLink id="removeDetail"
action="#{masterEditorWithDetailPaging.removeDetail(_detail)}"
reRender="detailListTable"
value="Delete">
<s:conversationId />
</a:commandLink>

</rich:column>
</rich:dataTable>

</div>
<s:div styleClass="actionButtons" rendered="#{empty from}">
<a:commandLink id="editDetailMP"
action="#{masterEditorWithDetailPaging.createNewDetail()}"
oncomplete="javascript:Richfaces.showModalPanel('detailEditWizard')"
reRender="detailEditForm"
value="New Detail...">
<s:conversationId />
</a:commandLink>
</s:div>
<div class="tableControl">

<s:link id="firstPage"
action="#{detailListForPaging.first()}"
reRender="detailListTable"
value="First Page">
</s:link>

<s:link id="previousPage"
action="#{detailListForPaging.previous()}"
reRender="detailListTable"
value="Previous Page">
</s:link>

<s:link id="nextPage"
action="#{detailListForPaging.next()}"
reRender="detailListTable"
value="Next Page">
</s:link>

<s:link id="lastPage"
action="#{detailListForPaging.last()}"
reRender="detailListTable"
value="Last Page">
</s:link>

</div>
</rich:panel>

<div class="actionButtons">

<h:commandButton id="save"
value="Save"
action="#{masterEditorWithDetailPaging.persist}"
disabled="#{!masterEditorWithDetailPaging.wired}"
rendered="#{!masterEditorWithDetailPaging.managed}"/>

<h:commandButton id="update"
value="Save"
action="#{masterEditorWithDetailPaging.update}"
rendered="#{masterEditorWithDetailPaging.managed}"/>

<h:commandButton id="delete"
value="Delete"
action="#{masterEditorWithDetailPaging.remove}"
immediate="true"
rendered="#{masterEditorWithDetailPaging.managed}"/>

<s:button id="done"
value="Done"
propagation="end"
view="/MasterList.xhtml"
rendered="#{masterEditorWithDetailPaging.managed}"/>

<s:button id="cancel"
value="Cancel"
action="#{masterEditorWithDetailPaging.cancelChanges}"
propagation="end"
view="/#{empty masterFrom ? 'MasterList' : masterFrom}.xhtml"
rendered="#{masterEditorWithDetailPaging.managed}"/>

</div>

</h:form>

</ui:define>

</ui:composition>


4. The page.xml:

<?xml version="1.0" encoding="UTF-8"?>
<page xmlns="http://jboss.com/products/seam/pages"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.1.xsd"
no-conversation-view-id="/MasterList.xhtml"
login-required="true">

<begin-conversation join="true" flush-mode="MANUAL"/>

<action execute="#{masterEditorWithDetailPaging.wire}" on-postback="false"/>

<param name="masterFrom"/>
<param name="masterId" value="#{masterEditorWithDetailPaging.masterId}"/>


<navigation from-action="#{masterEditorWithDetailPaging.persist}">
<rule>
<end-conversation/>
<redirect view-id="/MasterEditorWithPaging.xhtml"/>
</rule>
</navigation>

<navigation from-action="#{masterEditorWithDetailPaging.update}">
<rule>
<end-conversation/>
<redirect view-id="/MasterEditorWithPaging.xhtml"/>
</rule>
</navigation>

<navigation from-action="#{masterEditorWithDetailPaging.remove}">
<rule>
<end-conversation/>
<redirect view-id="/MasterEditorWithPaging.xhtml"/>
</rule>
</navigation>

</page>

Wednesday, 19 August 2009

Filtering details in Master-Detail edit

In response to questions about the Seam Master-Detail editing approach that I described here, I have put together a couple of small enhancements that allow for filtering the Detail list within an instance of the Master. This might arise when working with a large list of Detail instances.

Add to MasterEditor.java:

private static final String EJB_QL = "SELECT d FROM Detail d " +
"WHERE d.master = ?1 AND d.detailDescription like ?2";
private Query detailQuery;
private String filterDescription;
public String getFilterDescription() { return this.filterDescription; }
public void setFilterDescription(String filterDescription) { this.filterDescription = filterDescription; }

@SuppressWarnings("unchecked")
public String filterDetails() {
detailQuery.setParameter(1, getInstance());
detailQuery.setParameter(2, this.filterDescription + "%");
detailListFromMaster = (List) detailQuery.getResultList();
return "success";
}



Add to the front-end:

<rich:panel>
<f:facet name="header">Detail Filter</f:facet>

<s:decorate id="detailDescriptionFilterField" template="layout/edit.xhtml">
<ui:define name="label">Detail Description</ui:define>
<h:inputText id="detailDescriptionFilter"
size="100"
maxlength="100"
value="#{masterEditor.filterDescription}">
<a:support event="onblur" reRender="detailDescriptionFilterField" bypassUpdates="true" ajaxSingle="true"/>
</h:inputText>
</s:decorate>

<div class="actionButtons">

<a:commandButton id="doFilter"
value="Search"
action="#{masterEditor.filterDetails()}"
reRender="detailListTable">
<s:conversationId />
</a:commandButton>
</div>
</rich:panel>

The "wire()" function links the detailListFromMaster to the embedded list of Detail instances from within the Master that we're editing. This can be problematic, because wire() is called as a page action. This means every resubmission will re-trigger the wire() function, effectively overwriting the filtering that we've done. In order to stop this, change the XXX.page.xml to use on-postback = false:


<action execute="#{masterEditor.wire}" on-postback="false"/>

Friday, 14 August 2009

CRM in Insurance

Here's a great analysis of typical problems that insurance companies run into when attempting to implement CRM software. The article has been written with Commercial Insurance in mind. However in my experience most of the lessons apply just as much to the personal Life Insurance industry.

In particular, it's important to highlight the difference between a reporting-focused CRM and a sales-oriented system ("1. Built for management, not sales."). Life insurance agents are incredibly protective of their customer information at the best of times, and why not when their income depends entirely on making sales to those customers? So naturally a CRM system that does not give tangible benefits to the agent is not going to have any credibility. The agents need information pushed out to them automatically - warnings of customers' birthdays, contacts from the customer to the insurance company (say surrender request), etc.

Friday, 7 August 2009

Eclipse, Quartz and Seam, Classloader exception

When changing workspaces in Eclipse, one of the projects started encountering a problem with a ClassLoader problem when running an asynchronous job through Quartz. The error was:

15:43:17,175 ERROR [JobStoreCMT] Error retrieving job, setting trigger state to ERROR.
org.quartz.JobPersistenceException: Couldn't retrieve job because a required class was not found: No ClassLoaders found for: org.jboss.seam.async.AsynchronousInvocation [See nested exception: java.lang.ClassNotFoundException: No ClassLoaders found for: org.jboss.seam.async.AsynchronousInvocation]
at org.quartz.impl.jdbcjobstore.JobStoreSupport.retrieveJob(JobStoreSupport.java:1392)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.triggerFired(JobStoreSupport.java:2879)
at org.quartz.impl.jdbcjobstore.JobStoreSupport$38.execute(JobStoreSupport.java:2847)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInNonManagedTXLock(JobStoreSupport.java:3760)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.triggerFired(JobStoreSupport.java:2841)
at org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:342)
Caused by: java.lang.ClassNotFoundException: No ClassLoaders found for: org.jboss.seam.async.AsynchronousInvocation


Needless to say this was a bit suprising. The scheduler started ok (setup to start at application startup), and the application could submit a job correctly. It just wouldn't run through the scheduler.

After much hunting through the forums (again), it became clear the problem was that the Quartz library being used was the one included in JBoss (which I had previously upgraded to 1.6.5). The solution turned out to be manually adding a copy of the library (quartz.jar) into the WebContent/WEB-INF/lib directory in the Eclipse project. Then a clean rebuild and restart of the server, and hey presto, it works.

SaaS and the Tenancy Debate

This post on CouldAve highlights a debate that has been a hot topic recently - specifically whether SaaS should always be multi-tenant or not. As he notes, this is close to a religious topic, but one which I believe misses the key point.

A customer buying into the SaaS model is buying a service rather than software, and needs to stick with that understanding. Generally, they are unlikely to be overly interested in the architecture or technology behind the service, other than as much as is relevant to guaranteed service levels.

The post quotes Josh Greenbaum, who points out that this is linked to the maturity of the business / service / software. Offerings aimed at small businesses will probably end up in a multi-tenant model, offering no customisation capabilities and an arms-length support model. However it is likely that larger companies will move towards the SaaS model, and this will present some challenges as greater flexibility is demanded. The multi-tenant model is not designed for this.

Thursday, 23 July 2009

Seam - Could not instantiate Seam component, Hibernate schema

While cleaning an resetting the development environment, I encountered a "Could not instantiate Seam component" exception when accessing the application.

To set the context, this is using Eclipse 3.4.2 (M20090211-1700) on Mac OS X. Running JBoss 4.2.3 GA, and Seam 2.1.2.

The existing application had been working correctly. However, the system threw the following exception:


12:21:55,923 INFO [STDOUT] Hibernate:
select
systcalend0_.scal_id as scal1_42_,
systcalend0_.scal_timestamp as scal2_42_,
systcalend0_.scal_last_updated_by as scal3_42_,
systcalend0_.scal_last_updated as scal4_42_,
systcalend0_.scal_name as scal5_42_
from
agency.agency.syst_calendar systcalend0_
order by
systcalend0_.scal_name
12:21:55,925 WARN [JDBCExceptionReporter] SQL Error: 1064, SQLState: 42000
12:21:55,925 ERROR [JDBCExceptionReporter] You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.syst_calendar systcalend0_ order by systcalend0_.scal_name' at line 1
12:21:55,929 ERROR [STDERR] Jul 23, 2009 12:21:55 PM com.sun.facelets.FaceletViewHandler handleRenderException
SEVERE: Error Rendering View[/System/Jobs/ScheduleManager.xhtml]
org.jboss.seam.InstantiationException: Could not instantiate Seam component: scheduleManager
at org.jboss.seam.Component.newInstance(Component.java:2144)
...
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not execute query
at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:629)
...
12:21:55,930 ERROR [STDERR]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
...
Caused by: org.hibernate.exception.SQLGrammarException: could not execute query
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:67)
...
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.syst_calendar systcalend0_ order by systcalend0_.scal_name' at line 1
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
12:21:55,976 INFO [STDOUT] initScheduler() called
12:21:55,978 WARN [JDBCExceptionReporter] SQL Error: 0, SQLState: null
12:21:55,978 ERROR [JDBCExceptionReporter] Transaction is not active: tx=TransactionImple <>; - nested throwable: (javax.resource.ResourceException: Transaction is not active: tx=TransactionImple <>)
...


The hibernate query is the key. Hibernate was pre-pending an extra schema name to the table. Instead of "agency.syst_calendar", it was trying to access "agency.agency.syst_calendar".

Turns out I'd ended up with the following line in src/main/META-INF/persistence.xml



Removing/commenting this fixed the problem immediately.

Wednesday, 22 July 2009

Core Processing System Vendors - US

A US-centric view of the key Life Insurance Administration system vendors:
  • CSC
  • StoneRiver - does not appear to have any business in Asia?
  • InsPro (was Atiam)
  • Accenture - trying to find an opportunity to localise the ALIP (formerly Navisys)
  • Oracle - AdminServer product
  • SunGuard
  • PilotFish
Interestingly CSC is really the only US vendor to make any significant headway in Asia. SunGuard has made some sales, although nowhere near the profile.

Saturday, 11 July 2009

Insurance and Innovation

This article asks whether Innovation in Insurance is an oxymoron.

It's very true that innovation tends to be limited within the insurance industry. Products are incremental improvements on competitors' products. New products are few and far between, and quickly become the subject of copying.

It's possible that trends like the rise of social media will drag insurance kicking and screaming into a more modern place. Not really innovation per se, rather just facing reality. A recent post on YouTube shows the impact of social media in the context of customer satisfaction - or in this case chronic dissatisfaction. The rapid spread of this case suggests that monitoring social media has progressed from being a nice-to-have and is now a must-have. While it is unlikely that insurance will experience events that have quite the same infamous results, it's something to be aware of and watch carefully.

Thursday, 2 July 2009

Open Source CRM

Here is a detailed review of 3 open source CRM systems available at the current time:
Possibly the biggest point to consider is the need for a good understanding of the organisation that is implementing the solution. All 3 of the CRM systems reviewed are flexible, and therefore complex. As such they tend to be unsuitable for organisations that do not have some level of IT infrastructure in place. Understanding and then configuring the screens, workflows, and other settings is a non-trivial task.

Wednesday, 1 July 2009

Internet Usage

Although a very small sample, this recent study (which updates a previous study) suggests that Internet usage is much more restricted than most would think.

In particular, while Facebook is highlighted as a family/friends networking tool (which implies attempts to seriously monetise it will fail), business networking and research is much less prevalent. The community of LinkedIn is probably a good example. It started off as a useful and interesting forum to connect with people, discuss problems/questions, and research industries/jobs/etc. However it's gone through a few phases, as the question forums increasingly became a place to advertise your service or product, and more recently the same has happened with many Groups.

Still, with all of these challenges, you might reasonably expect that more people would be involved. The study suggests that internet usage is still fairly restricted in terms of depth of exposure in the general population.

Tuesday, 16 June 2009

Legacy Applications

Many (most?) financial services companies operating in the insurance sector are bound by legacy applications. These are characterised by old technology, poor database design, and (usually) poor documentation. Resulting in expensive maintenance and support services.

As such, modernising legacy systems is usually on the agenda for CIOs. An article on eWeek.com reports on a survey conducted on this topic.

However, financial services organisations face special challenges when looking at modernisation. Insurance is a complex domain, and while it is easy to identify the key objects in a data model (client, policy, transaction, etc.), there is scope for immense variation at detail levels. And since insurance companies delight in making products complex and opaque, the complexity within the system grows over time. Poor documentation only exacerbates the problem.

Many companies come to a choice. They can replace the system with a package with all of the inherent risks around vendors, system suitability, and migration. Or they can take a lipstick-on-the-pig approach, layering a web front-end (or whatever else is the current flavour) over the top of the existing back-end database and code. Each has it's advantages, although you could argue that the package solution is probably a better way to go, as it allows the insurance company to focus more on the business and less on being an IT development shop.

Monday, 15 June 2009

ITSM & SaaS

An interesting article proposing the suitability of SaaS for implementing ITSM. As the author points out, most ITSM packages have ended up being much of a muchness.

SaaS, with it's emphasis on continuous improvement/development does mean that there is opportunity for innovation that has been lacking in previous systems.

The article also touches on the risks of losing some functionality by moving to a web-only client. This is balanced by the availability of ongoing system improvement that should come from the SaaS model.

One key point raised in the article is the desire to have data stored wherever the customer wants it. In the cloud, or locally within the organisation. For financial services companies, it is likely that "local" will be the preferred option - there is still a significant conservatism around access to and protection of data. Although since in this case we are concerned with data on service processes, this may be a less critical issue than in other areas (e.g. SaaS CRM).

Wednesday, 10 June 2009

Oracle Revenue Management for Insurance

Oracle has launched a new component for their Insurance coverage, a Premium Collection module called Revenue Management and Billing for Insurance.

On first look (there is little information available on the website so far), this looks to be a consolidator that will integrate multiple admin systems with multiple collection channels/banks. And Group Billing functionality is included.

However, it appears to stop short of the processes needed to manage the follow-up processes with final lapse notice/APL processing, etc. From what I have personally seen, Oracle's approach is being driven in a US context, and primarily looking at P&C rather than the wider insurance market (a presentation for a Life Claims management solution in Asia was delivered using a US demo of a P&C claims system).

Tuesday, 9 June 2009

Versions of ITIL

There is an interesting discussion around the multiple versions of ITIL standards, on Juan Jiminez's blog at ITP. Possibly one of the most interesting comments he makes in the discussion is:

"...the architecture and goal of the framework have radically changed. The idea is no longer to achieve alignment, but instead to achieve integration with the organization. Instead of just a supplier, the goal is to become a partner with the organization’s and/or customer’s management."

This links back to a common theme across financial services - how can IT become more relevant/important/etc in the business. While ITIL has been a good step, with it's system thinking and service focus, it pushed people to think more as an internal supplier. This introduces an extra "distance" in the relationship between IT and the business - not always a good thing. It will be interesting to see how V3 works in practice as it is implemented and settles down. Important not to lose the discipline involved, but at the same time achieve tighter integration.

Thursday, 21 May 2009

Small Business CRM

Here is an interesting review of the JavelinCRM software.

JavelinCRM is a SaaS offering, running on the Amazon cloud services.

Monday, 18 May 2009

Project & Program Management

A couple of interesting links.

This first one is a Gartner report that says it looks at a few vendors providing project and program management tools. However once getting into the detail you will find that this is a list of a few interesting tools that may assist in terms of increased productivity. Particularly in respect of task management, assignments, and email.

The second link is also a Gartner report, which looks at Project and Portfolio Management Offices. In particular it pushes the Gartner PMO Maturity Model. A pretty good (albeit heavily theoretical) take on how PMO's should fit with an organisation's process maturity. Generally, insurance companies tend to be somewhere between the lower end and the middle of the maturity model... (gross generalisation, of course).

Friday, 15 May 2009

The Challenges of Open Source

One of the challenges inherent in using Open Source software has been bought home over the past weeks. In building a new website, an intermittent error has shown itself in the Content Management System (www.silverstripe.com) that is being used as a framework.

Consulting the forums has been unhelpful, with zero response from the community. The question lurks - is this because it is so obvious that no-one can be bothered pointing the developer in the right direction, or is it so obscure that no-one can suggest a direction of investigation?

Interestingly, while the software itself is distributed under an open source license, there is a company based on it's development and utilisation in a commercial environment.

In the end, we come back to the conclusion that while Open Source has a number of inherent benefits, users must be prepared to get their hands dirty in the detail. It seems a deeper level of skill/knowledge is an implicit requirement when compared with other options.

Saturday, 9 May 2009

Interesting Technology

An industry colleague introduced this company the other day. Open Square has developed a domain-specific language (PACT) along with a rules engine/infrastructure that can be used to model insurance products. A Visio front-end is used to develop the models, which should be fairly familiar for users. The models are uploaded to the engine, where they are instantiated.

This is going to be a fairly flexible piece of software, and could be used by Product Development team (model the product and generate complete product specs automatically?), or the IT department (model the product and automatically generate both requirement specifications and a prototype). It may also be of interest to other areas of the business. Worth keeping an eye on to see how it develops.

Thursday, 7 May 2009

Hartford Japan

As this article shows, Hartford will cease selling New Business in Japan from 1st June. Having made the biggest splash in developing the Variable Annuity market in Japan, this is quite a turnaround. There is some speculation washing around that this is in part due to local insurance companies selling at a loss to drive other companies out of the market.

Tuesday, 5 May 2009

More Policy Administration vendors

Two more Policy Administration system vendors to add to the list:

Airas Intersoft (http://www.airasintersoft.com), hails from Ireland. They produce the iGas system, covering Life and Pensions. The system has been built using the Oracle technology toolset. They have clients across the world, although to date this has not extended to Asia.

IntelligentLife (http://www.intelligentlife.co.nz/), is from the opposite side of the world, New Zealand. The primary claim to fame is the launch of completely online, straight-through-processing of Life products (there is a link on the company website).

Thursday, 30 April 2009

Agency Management

Agency Management often gets left to internally built systems - there are not the same number of systems vendors providing AMS systems as there are those that provide Policy Admin or other components. Further, the business rules around Agency Management are difficult to standardise into an off-the-shelf piece of software. And every insurance company seems to have decided that there are a couple of rules that provide them with a particular competitive advantage compared to how other companies manage their agency channel...

Functionality
So what is included in an AMS?
  • Agency Master record (individual details, addresses, bank accounts, etc)
  • Hierarchy Management
  • Training and Competency - qualifications, regular retraining, etc
  • Commission Generation, both basic and overrides, bonuses, etc
  • Loans
  • Competitions and campaigns
  • Management Reporting (including "Top 10" style reports)

Usually there will be some level of integration to an Agency Portal, or alternatively there will be portal functionality built into the system. This will allow the agent to see information on their commission/loans/points, plus information on their customers and their policies.

Vendors
Some companies that do offer Agency Management Systems:
  • Redland Business Solutions (UK)
  • Mastek (India)
Most of the main systems vendors (e.g. CSC, Bravura) have some level of agency functionality within their systems, however it is usually fairly basic. Key areas missing are override structures, competitions, loans, etc). Because the focus of these vendors is actually on Policy Administration, the functionality tends to stop at the point of generating the basic commission.

Monday, 27 April 2009

Tom Peters

A great quote from Tom Peters:

Whoever tries the most stuff and screws the most stuff up and most rapidly launches the next try wins. Failures are not to be 'tolerated,' they are to be celebrated.

Oracle and Sun

The Financial Times tech blog has an interesting take on the recent deal, particularly in how it may impact on Java. Interestingly IBM has "preferred customer" status for Java licensing, with current arrangements protected until 2016.

Saturday, 25 April 2009

Useful Seam Tip - Page Actions

The page-action tag is a useful way of settings things up in the backing bean, ready for rendering the page. However, it' important to note that the page action is re-fired on every submission.

In many cases, I use the page action to set up default data, and to populate lists for dropdowns, etc. I don't want this to be re-executed during the conversation, as this would result in loss of user-entered data (e.g. knowing which item they selected in a DataTable). I've spent time debugging strange behavior, only to realise that the problem was caused by Seam re-triggering the page action (usually the wire() function on the EntityHome).

It's pretty easy fix - just set on-postback to false:


<action execute="#{myEntityHome.wire}" on-postback="false">


This tells Seam not to trigger the function when a form is posted back to the server as a result of actions being executed/validation within the conversation.

Friday, 24 April 2009

Business Failure in the Downturn

Interesting article: 5 Myths About Business Failure in a Downturn

As someone pointed out, stop blaming others for your problems, and get on and do something about it.

Wednesday, 22 April 2009

Seam - Master-Detail Views

Building master-detail views using Seam is a regular topic on the forums, and with good reason. It's not trivial for new developers.

There are three levels that have to work together:
  1. The Hibernate mapping between the master and detail entities
  2. The Seam layer - the EntityHome or other bean
  3. The presentation layer
In terms of requirements for a maintenance screen that deals with both master and detail:
  • you want the user to update both master and detail records on the same page
  • you want to save changes to master and detail at the end (or optionally part-way through)
  • you should be able to add new detail rows or delete existing ones and see the changes reflected immediately on the screen, again without navigating between pages

So to an example. My two entities are Master and Detail, represented in the MySQL database as:



CREATE TABLE master (
id int(11) NOT NULL AUTO_INCREMENT,
master_description varchar(100) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM

CREATE TABLE detail (
id int(11) NOT NULL AUTO_INCREMENT,
master_id int(11) NOT NULL,
detail_description varchar(100) NOT NULL,
PRIMARY KEY (id),
KEY newfk (master_id)
) ENGINE=MyISAM



The Hibernate mappings need to reflect the one-to-many relationship from master to detail.



@Entity
@Table(name = "master", catalog = "test")
public class Master implements java.io.Serializable {

private Integer id;
private String masterDescription;
private List<detail> details = new Vector<detail>(0);

public Master() {
}

public Master(String masterDescription, List<detail>details) {
this.masterDescription = masterDescription;
this.details = details;
}

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}

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

@Column(name = "master_description", length = 100)
@Length(max = 100)
public String getMasterDescription() {
return this.masterDescription;
}

public void setMasterDescription(String masterDescription) {
this.masterDescription = masterDescription;
}

@OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.LAZY, mappedBy = "master")
public List<detail> getDetails() {
return this.details;
}

public void setDetails(List<detail> details) {
this.details = details;
}
}


Note that this example uses List and Vector instead of the Sets that Seam-gen uses by default. Select lists and some other presentation widgets seem to require Lists rather than Sets, and it's become a habit to use them instead of the default Seam-gen code.


@Entity
@Table(name = "detail", catalog = "test")
public class Detail implements java.io.Serializable {

private Integer id;
private String detailDescription;
private Master master;

public Detail() {
}

public Detail(Master master, String detailDescription) {
this.master = master;
this.detailDescription = detailDescription;
}

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}

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

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "master_id", nullable = false)
@NotNull
public Master getMaster() {
return this.master;
}

public void setMaster(Master master) {
this.master = master;
}

@Column(name = "detail_description", nullable = false, length = 100)
@NotNull
@Length(max = 100)
public String getDetailDescription() {
return this.detailDescription;
}

public void setDetailDescription(String detailDescription) {
this.detailDescription = detailDescription;
}
}


MappedBy = "master" identifies the name of the property on the detail entity that links back to the master entity.


Now we need a backing bean for the maintenance page. Seam-gen will give you a set of XXXList, XXX, and XXXEdit pages for each entity. The XXX and XXXEdit pages refer to an EntityHome instance for the entity to be viewed/edited. So after running Seam-gen to reverse-engineer from the MySQL database we will have a set of XHTML pages
  • Master.xhtml
  • MasterEdit.xhtml
  • MasterList.xhtml
  • Detail.xhtml
  • DetailEdit.xhtml
  • DetailList.xhtml
and a set of four backing beans:
  • MasterHome
  • MasterList
  • DetailHome
  • DetailList
Really we want to end up with a single page, and therefore a single backing bean that encompasses the functions of both MasterHome and DetailHome.


@Name("masterEditor")
public class MasterEditor extends EntityHome<Master> {

@In EntityManager entityManager;

@DataModel private List<Detail> detailListFromMaster = new Vector<Detail>(0);
@DataModelSelection @Out(required=false) private Detail currentlySelectedDetail;

//For the selection on the detail list
public void select() {}

public void createNewDetail() {
Detail detail = new Detail();
detail.setMaster(getInstance());
detailListFromMaster.add(detail);
currentlySelectedDetail = detail;
}

public void cancelNewDetail() {
removeDetail();
}

//Delete a detail item
public void removeDetail() {
detailListFromMaster.remove(currentlySelectedDetail);
entityManager.remove(currentlySelectedDetail);
}

public void setMasterId(Integer id) {
setId(id);
}

public Integer getMasterId() {
return (Integer) getId();
}

@Override
public String update() {
return super.update();
}

@Override
protected Master createInstance() {
Master master = new Master();
return master;
}

public void wire() {
//Both triggers getInstance() and sets the local variable
//to the list of Detail items from the Master entity
detailListFromMaster = getInstance().getDetails();
}

public boolean isWired() {
return true;
}

public Master getDefinedInstance() {
return isIdDefined() ? getInstance() : null;
}
}


This is an edited EntityHome, which started life as MasterHome. A few things to note:
  • we want to use a DataModel and DataModelSelection to link with the presentation layer
  • the wire() function now gets the list of Detail objects from the Master object and pushes them into the DataModel list
  • there are a few simple functions added to be triggered from the presentation layer to do simple CRUD stuff (add a new Detail, remove, etc)


So on to the presentation layer. This is in two parts - the main page, plus a Modal Panel, all using RichFaces.

[UPDATE: Note that if you want a presentation technique that does not use Modal Panels, try this more recent post.]


<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
template="layout/template.xhtml">

<ui:define name="body">

<rich:modalPanel id="detailEditWizard"
minHeight="200"
minWidth="500"
autosized="true">

<f:facet name="header"><h:outputText value="Edit Detail..." /></f:facet>
<f:facet name="controls">
<h:graphicImage value="/img/ico_close.gif"
styleClass="linkImage"
onclick="#{rich:component('detailEditWizard')}.hide()" />
</f:facet>

<h:form id="detailEditForm" styleClass="edit">

<h:messages globalOnly="true"/>

<s:decorate id="masterIdField" template="layout/edit.xhtml">
<ui:define name="label">Master id</ui:define>
<h:inputText id="masterId"
required="true"
value="#{currentlySelectedDetail.master.id}">
<a:support event="onblur" reRender="masterIdField" bypassUpdates="true" ajaxSingle="true"/>
</h:inputText>
</s:decorate>

<s:decorate id="detailDescriptionField" template="layout/edit.xhtml">
<ui:define name="label">Detail description</ui:define>
<h:inputText id="detailDescription"
required="true"
size="100"
maxlength="100"
value="#{currentlySelectedDetail.detailDescription}">
<a:support event="onblur" reRender="detailDescriptionField" bypassUpdates="true" ajaxSingle="true"/>
</h:inputText>
</s:decorate>

<div class="actionButtons" style="clear:both">

<a:commandButton id="detailEditMPClose"
onclick="#{rich:component('detailEditWizard')}.hide()"
reRender="detailListTable"
value="Close" />

<a:commandButton id="detailEditMPCancel"
onclick="#{rich:component('detailEditWizard')}.hide()"
action="#{masterEditor.cancelNewDetail()}"
reRender="detailListTable"
value="Cancel" />

</div>
</h:form>

</rich:modalPanel>



<h:form id="master" styleClass="edit">

<rich:panel>
<f:facet name="header">#{masterEditor.managed ? 'Edit' : 'Add'} Master</f:facet>

<s:decorate id="masterDescriptionField" template="layout/edit.xhtml">
<ui:define name="label">Master description</ui:define>
<h:inputText id="masterDescription"
size="100"
maxlength="100"
value="#{masterEditor.instance.masterDescription}">
<a:support event="onblur" reRender="masterDescriptionField" bypassUpdates="true" ajaxSingle="true"/>
</h:inputText>
</s:decorate>

<div style="clear:both">
<span class="required">*</span>
required fields
</div>

</rich:panel>


<!-- Detail section -->
<rich:panel>
<f:facet name="header">Detail List</f:facet>
<div class="results" id="detailList">

<h:outputText value="The detail search returned no results."
rendered="#{empty masterEditor.instance.details}"/>

<rich:dataTable id="detailListTable"
var="_detail"
value="#{detailListFromMaster}"
rendered="#{not empty detailListFromMaster}">
<h:column>
<f:facet name="header">
<ui:include src="layout/sort.xhtml">
<ui:param name="propertyLabel" value="Id"/>
</ui:include>
</f:facet>
<h:outputText value="#{_detail.id}"/>
</h:column>
<h:column>
<f:facet name="header">
<ui:include src="layout/sort.xhtml">
<ui:param name="propertyLabel" value="Master id"/>
</ui:include>
</f:facet>
<h:outputText value="#{_detail.master.id}"/>
</h:column>
<h:column>
<f:facet name="header">
<ui:include src="layout/sort.xhtml">
<ui:param name="propertyLabel" value="Detail description"/>
</ui:include>
</f:facet>
<h:outputText value="#{_detail.detailDescription}"/>
</h:column>
<rich:column styleClass="action">
<f:facet name="header">Action</f:facet>
<a:commandLink id="editDetailMP"
action="#{masterEditor.select()}"
oncomplete="javascript:Richfaces.showModalPanel('detailEditWizard')"
reRender="detailEditForm"
value="Edit">
<s:conversationId />
</a:commandLink>
#{' '}
<a:commandLink id="removeDetail"
action="#{masterEditor.removeDetail()}"
reRender="detailListTable"
value="Delete">
<s:conversationId />
</a:commandLink>

</rich:column>
</rich:dataTable>

</div>
<s:div styleClass="actionButtons" rendered="#{empty from}">
<a:commandLink id="editDetailMP"
action="#{masterEditor.createNewDetail()}"
oncomplete="javascript:Richfaces.showModalPanel('detailEditWizard')"
reRender="detailEditForm"
value="New Detail...">
<s:conversationId />
</a:commandLink>
</s:div>
</rich:panel>

<div class="actionButtons">

<h:commandButton id="save"
value="Save"
action="#{masterEditor.persist}"
disabled="#{!masterEditor.wired}"
rendered="#{!masterEditor.managed}"/>

<h:commandButton id="update"
value="Save"
action="#{masterEditor.update}"
rendered="#{masterEditor.managed}"/>

<h:commandButton id="delete"
value="Delete"
action="#{masterEditor.remove}"
immediate="true"
rendered="#{masterEditor.managed}"/>

<s:button id="done"
value="Done"
propagation="end"
view="/Master.xhtml"
rendered="#{masterEditor.managed}"/>

<s:button id="cancel"
value="Cancel"
action="#{masterEditor.cancelChanges}"
propagation="end"
view="/#{empty masterFrom ? 'MasterList' : masterFrom}.xhtml"
rendered="#{masterEditor.managed}"/>

</div>

</h:form>

</ui:define>

</ui:composition>

Systems: Automated Underwriting

What is Automated Underwriting?
Automated Underwriting systems are an attempt to reduce or remove the manual activities that have long formed part of the underwriting process. Instead of having underwriters of varying levels of experience evaluating proposals, an automated solution is designed to weed out the simpler cases. The underwriters are not removed from process entirely, but instead focus on the more complex cases, where their skills can be best applied.

Technology-wise, underwriting systems often take one of two main forms:
  • Back-office, rules engine integrated with the policy admin system
  • Point-of-sale integration of rule engine at proposal capture time
The system will normally comprise:
  • A rules database. This contains the accumulated wisdom regarding symptoms, risk factors for diseases, occupations, etc. Because the database captures this type of information, in practice it often comes from a reinsurer. Rules databases will normally include standard rules about diseases, traits, pursuits, and occupations. They will also include product-specific rules, which are often configured specifically for the company's products that will be evaluated.
  • Engine software. This is the relatively straight-forward software that evaluates a case (proposal), applying the rules database to determine a course of action.
Back-Office Use
In a back-office environment, the automated underwriting system is often integrated with a workflow system (although integration with the new business or policy admin system is the other approach). The workflow is usually managing a case through the New Business process, which include one or more iterations of evaluation by the underwriting system. The underwriting system will require data about the proposal, along with any existing policies the customer holds with the company (for aggregated risk calculations). In this scenario the automated underwriting system will normally respond with one of a limited set of recommendations:
  • Accept ordinary rates (cleanskin)
  • Decline
  • Refer for manual underwriting
As the company becomes more comfortable with the reliability of the automatic underwriting (in reality the quality of the rules database), the system may be entrusted with applying loadings or exclusions in more straightforward substandard cases.

Point Of Sale
Over the past few years, vendors (particularly in Life) have pushed automated underwriting tools integration in the proposal capture process at Point of Sale. This makes some sense, as the tool can prompt the customer/agent through a series of underwriting questions during the capture stage. If the case is evaluated as standard, the customer and agent have the benefit of knowing that the policy should be issued quickly. Alternatively the questions/responses may indicate to the customer that further medical evaluation will be required.

For cleanskin cases this offers the possibility of achieving the Straight-Through-Processing that has been discussed over the years. With POS integration of automated underwriting, combined with workflow/automation at the back office, a cleanskin proposal could be submitted electronically, and issued with little/no manual work.

Mis-Use: some companies have been known to mis-use the automated underwriting tools, configuring "underwriting rules" that really should be handled within the New Business / Policy Admin system. For example, the maximum sum assured on a product. This really comes down to the perceived difficulty in making changes to the admin systems when compared with configuring the underwriting tools. Of course the disadvantage of this approach is that you are left with product rules spread around the systems rather than being grouped sensibly into a few maintainable locations.

Vendor Examples
SwissRe (www.swissre.com) - Magnum, supports both back-office and POS approaches.
MunichRe subsidiary - AllFinanz (www.allfinanz.com) - URE: Underwriting Rules Engine.
Insbridge (now part of Oracle), for P&C rather than Life/Pensions

Monday, 20 April 2009

Open Source in Financial Services

Open Source is often regarded as a bad thing in corporate Financial Services, although this does depend on the personal views of the IT architect and/or CIO. When open source software is used, it is often in the guise of a utility component within in-house developed systems, or similarly within vendor systems. No corporate that I am aware of would entrust their mission-critical data to an unsupported system. And that is generally how Open Source is viewed - unsupported.

An interesting move to change that is a consortium that is working on an Open Source insurance system. Currently only for non-Life, Insurance In a Box (iabforge.wikidot.com) is an interesting experiment. Built using Ruby on Rails, it also takes an interesting approach to how to store the data - as XML.

Systems: Direct Marketing and Lead Management

Direct Marketing in Life Insurance (in Asia at least) largely tends to come down to telemarketing. Either inbound in response to TV home shopping/commercials, or outbound to database lists obtained from sponsors.

The segment does not seem to be well served on the systems side, either in the planning or monitoring activities. Generic lead management tools are available, however event these are not well suited to insurance sales. The call center is better served, unsurprisingly since there are many vendors focused on call centers.

Planning/Monitoring
One solution that comes close on the execution side is Aprimo (http://aprimo.com/). This tool is focused on the sales process, with some functionality around campaign planning. It has inbuilt workflow definition and management tools, which are used to execute lead-level followup.

However the key lack (and in most/all other systems) is the profitability planning capabilities when it comes to insurance-product sales. The problem is that an insurance sale creates an annuity - an ongoing revenue stream. The sale is only profitable when you take into account the stream of premiums received over months and years. Tools like Aprimo's assume each sale is a lump sum amount.

An insurance planning tool needs to be able to evaluate how long it will take for the campaign to break even, which requires the system to model the spend and cash inflows over an extended period. There do not appear to be a wealth of options available in this area, and most insurance companies that do anything on this tend to use spreadsheets.

The downside of this approach is the restricted ability to monitor actual experience against the plan and projections. And if you don't know whether the campaign is profitable, how can you decide whether to continue, ramp up, or even stop?

Lead Management
Databases of customers and potential customers need to be sanitised, filtered, and passed to call centers for action. This tends to be fairly basic work, if tedious. Many organisations seem to rely on in-house systems, perhaps reflecting the custom nature of the data sources. A fairly simple set of database and query tools will usually suffice to load data from sponsors, perform the de-duplication checks, and re-extract data for passing to the call center.

For web-based sales channels (much discussed but not overly effective by nature), tools like Aprimo provide the ability to manage and in some cases automate parts of the sale process. Leads are captured in the internal database, and eventually the sale is passed to the back-office systems. The challenge here will be to retain the less-structured data that is captured during the sale process.

Sunday, 12 April 2009

Coignition

Well, possibly another victim of the markets? Coignition has shut it's doors in HK.

For anyone not aware, Coignition was a wholly owned subsidiary of Munich Re AG. Coignition was set up to be a professional services partner for businesses "seeking alternative routes of entry into life insurance". They offered to provide both services and an "end-to-end business platform".

I suspect that given the timing, the business was given a year to see whether they could make a success of things. However it's not been an ideal time to be building new insurance businesses, particularly in the banking world where they have enough existing problems to focus on.

At the time of writing, there were no official announcements on the Munich Re website. The Coignition website has been decommissioned.

Tuesday, 31 March 2009

Financial Services Vendors

Mention of a couple more vendors in Asia:

A HK-based vendor has been advertising for new project staff recently. EAB Systems (HK) Limited was called EastPro until they changed name in 2007 following investments from Tokio Marine.

EAB Systems is building and distributing iLife, iWealth, iGeneral, which are based on a J2EE architecture.

Second one is Figlo Systems in Singapore, which is selling tools focussed primarily on the professional advisor channel. Includes both business-to-business and business-to-consumer tools.

Thursday, 19 March 2009

Supply & Demand

Not strictly related to IT and Financial Services, this article is nevertheless a good analysis of some aspects of the challenges facing the economy today.

Thursday, 29 January 2009

Systems: Policy Administration

Depending on the audience (certainly within Operations and IT), the Policy Administration is often seen as the core of the business. And there is no denying that it will normally be the focus of all post-sale activity for a customer/policy.

Key Functions
The Policy Admin system focuses on managing a policy throughout the usual lifecycle. Traditionally the emphasis is on handling the policy rather than the customer. Only some of the more recent systems available in the market are truly customer-centric in their behaviours. Key modules will usually include:
  • New Business capture, underwriting, and policy issuance
  • Premium collection, including suspense account management
  • Lapse processing (managing non-payment of premiums)
  • Policy Servicing (endorsements)
  • Maturity handling (incl expiry for risk products)
  • Loans
  • Claims (sometimes handled in separate systems)
  • Annuity Payments (sometimes handled in separate systems)

Position in Systems Architecture
Organisations tend to take one of two approaches. They either stick religiously to a single Policy Administration system, forcing all products onto the same system, or they allow systems to proliferate to the point where many systems contain a single product.

This second approach tends to give rise to complex architectures that involve operational data stores and "single customer view" components. These can often be an effort to patch over inadequacies in the admin systems in terms of functionality, technical architecture, and scalability issues. Vendor capacity for development can also be a key driver.

Variations
Policy Admin systems will sometimes be separated based on the Group or Individual product sets. Group policies require different handling (in many ways simpler) in both policy servicing and premium collection/management.

Further, a breakdown of product sets into (a) Risk/Traditional, (b) Unitised, and (c) Annuities will also drive separation of policies across multiple Policy Admin systems.

Vendors
In Asia, examples include:
  • Life/Asia (CSC's Life/400 product, customised to fit Asian needs)
  • Talisman (Bravura Solutions)
  • Ingenium (originally SolCorp, purchased by EDS, now part of HP)
  • eBao (eBaoTech in Shanghai)
  • FirstLife (First Apex)
  • InsuranceFaces (Target Harlosh)
  • Accenture is pushing ALIP (from US) in Asia but at the time of writing was unable to demonstrate a localised version of the system for any Asian country.
  • Oracle has purchased AdminServer in the US with the intention of selling in Asia. This is a new area for Oracle, and at the time of writing there is no localised version.
In UK:
  • Talisman (Bravura Solutions)
  • Elixir (Mastek)
  • ALIS (FIServe)
  • Lamda (was Marlborough Stirling, acquired by Vertex in 2005)

Many companies use whatever the parent company provides or insists on, which is often a mainframe architecture (or similar) that originated in UK or USA.