Version 3: Part I, Drilling CellSet Encapsulation

Friday, January 13, 2012

First of all: Happy New Year to everybody!!

During this holidays I've working on implementing capabilities visually edit a query: adding/removing of hierarchies and inclusion/exclusion of members of those hierarchies. I'll explain you my achievements in two blogs entries: this one explains the AbstractQueryBean and its usage to provide an easy to use drillable cellset table, the next one will explain components allowing selection of hierarchies and inclusion/exclusion of its members.

Anyway, for the impatient among us, this is a screenshot of "version 3", and a link to the source code.


An Abstract Managed Bean to Handle Query State

The version in my previous blog entry used a helper class to serialize/deserialize the Query instance, and put it explicitly into the ViewState. This strategy works but has two main drawbacks: it's not easily extensible, and the name used to store the serialized query into the view state can silently clash with the names of other objects.
After some refactoring cycles I've ended up with a better solution: use a ViewScoped managed bean to manage the state of the query, and use the standard Java serialization mechanism to save and restore that state. This bean will handle connection management, needed to support the query deserialization; and caching for the query CellSet result.
To accomplish the vision of an easily pluggable component library I've added an abstract class AbstractQueryBean intended to serve as a base class for concrete view
scoped managed beans. This class defines two abstract methods to be implemented by deriving classes:
  • OlapConnection initConnection()Classes derived from AbstractQueryBean must implement this method to return a connection to the olap4j provider.
  • Query initQuery()Classes derived from AbstractQueryBean must implement this method to return newly a initialized query.
This abstract class, included in the olap4j-faces jar, implements writeObject and readObject to serialize the query state. The implementations of those members parallel those of the previous helper class, with an important difference: after serializing the query state, writeObject tears down the connection with the olap4j provider.
As I've pointed before, this class provides also with caching for the resulting CellSet from the query. The method getCellSet() tests for a cached CellSet before executing the current query. Convenience methods included in this class altering the query state invalidate this cache, to force a query execution in the next call to getCellSet(). And there is a invalidateCellSet() method to force this cache invalidation if the user of the class modifies externally the state of the query.
I'll change this manual cache invalidation mechanism and replace it with another based on QueryListener as the one used in the original query package.
Follows a minimal example of how to use this class to create a managed bean to serve as backing bean for drillable cellset:
package olaptest;


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;


import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;


import org.olap4j.Axis;
import org.olap4j.OlapConnection;
import org.olap4j.OlapException;
import org.olap4j.mdx.IdentifierNode;
import org.olap4j.metadata.Cube;


import es.cgalesanco.faces.olap4j.managedBeans.AbstractQueryBean;
import es.cgalesanco.olap4j.query.Query;
import es.cgalesanco.olap4j.query.QueryAxis;
import es.cgalesanco.olap4j.query.QueryHierarchy;
import es.cgalesanco.olap4j.query.Selection.Operator;

@ManagedBean

@ViewScoped // View scoped to save the state of the query between requests

public
class SampleQueryBean extends AbstractQueryBean{

    private static final long serialVersionUID = 1L;
    @Override    
    protected OlapConnection initConnection() throws OlapException {
        …
        Connection jdbcCn = DriverManager.getConnection();

        …
        return jdbcCn.unwrap(OlapConnection.class);
    }

    @Override    
    protected Query initQuery() throws OlapException {
        Cube c = getConnection().getOlapSchema().getCubes().get("Sales");
        Query query = new Query("MyQuery", c);
        QueryAxis columnsAxis = query.getAxis(Axis.COLUMNS);
        columnsAxis.setNonEmpty(true);
        columnsAxis.addHierarchy(query.getHierarchy("Gender"));
        …
        return query;
    }
}

Composite Control To Encapsulate Drilling CellSet

Insisting on easy construction of olap4j/JSF applications I've encapsulated the previous sample of a drillable cellset into a composite component using the AbstractQueryBean. This version of the composite components adds functionality to remove a hierarchy from the query by clicking on a close button in the hierarchy header cell.
So, the following code snippet, includes a drillable cellset table using the previous sample query bean:
        <olap:queryCellSet id="table" query="#{sampleQueryBean}">


Yeah, that's all folks. And, a slightly more complicated version using AJAX to update the table on drills and hierarchy removals
    <h:form id="form">
         …

        <olap:queryCellSet id="table" query="#{queryBean}">

            <f:ajax event="drill" render=":form:table"/>

            <f:ajax event="axisChange" render=":form:table"/>

        </olap:queryCellSet>