BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Flexible and User-configurable Charts with Flash Builder Backed by a Java-based RESTful API

Flexible and User-configurable Charts with Flash Builder Backed by a Java-based RESTful API

Bookmarks

Many software companies have complex software solutions. However, those solutions often lack a simple and flexible way of presenting a global outlook of their data using visually appealing dashboard charts to either present data trends or inform the right user of possibly risky situations. This article describes how to build a sample application that contains a portal-style home page containing user-configured charts that connect to a Java-based Representational State Transfer (REST)-ful web application for their data and to perform afferent operations for creating, updating, or retiring a dashboard widget from their pages.

The usual use case consists of a user who visits the system, creates a new chart by selecting the chart’s two dimensions from a list of value fields, sets up filtering (usually by range), chooses the chart type (the system might make recommendations based on the data types of the chosen dimensions), and (optionally) sets alert thresholds. From a technical standpoint, the best-case scenario would be to connect to a data warehousing-type of back-end where all the data has been pre-computed for performance reasons. In reality, however, organizations have hybrid systems, and the data might come from different systems. Aggregating the data into a central data storage medium may make sense if you are dealing with large datasets and multiple data stores.

The Enviroment

The minimum requirements for setting up the project include a back end and a front end. The back end consists of a Java web application (written in Java 5) using JSR 311 RESTful endpoints. For this back end, you need:

The front end consists of a simple JavaServer Pages (JSP) page (using JSP 1.2) that is dynamically generated to display a portal-style home page containing various user-configured charts. For this front end, you need:

  • Adobe Flash Builder 4, which is also built using the Eclipse plug-in architecture.

The displayed charts are identified according to the login credentials. (The user name is used so that a user will see the same charts if he or she moves to a different computer.) Each chart is an instance of the same Adobe Flash Builder component that presents a dashboard-style chart expressing the user’s selection. The charts are configurable by simply clicking on them; the user is presented with an expanded version and has the option to change the chart type, the chart's axes, advanced filtering, and so on. Once the chart’s options are persisted, the summarized chart on the user’s home page will be displayed, reflecting the updated options. The user has the option of adding new charts to the home page as well as retiring charts that are no longer of interest.

Back-end code

You set up a RESTful back-end web application to serve the charting component using a simple API with the calls shown in Table 1.

Table 1. API calls for the back-end application

URI

HTTP method

Description

/charts

GET

Retrieve a collection of charts for the current user.

/charts

POST

Create a new chart.

/charts/{id}

GET

Retrieve a chart.

/charts/{id}

PUT

Update a chart’s options.

/charts/{id}

DELETE

Retire a chart.

/charts/options

GET

Retrieve possible options for a chart.

/charts/{id}/data

GET

Retrieve a chart’s data.

To get your project up and running quickly generate a simple JSR 311–based project by using a Maven archetype. Select the jersey-quickstart-webapp option for the following Maven command:

# mvn archetype:generate -DarchetypeCatalog=http://download.java.net/maven/2 \
	-DarchetypeArtifactId=charts-backend –DarchetypeGroup=example.charts

This command generates a Web archive (WAR) project and resolves many of the required dependencies to run this sample. To set up your project for use in Eclipse, execute the eclipse:eclipse target in Maven. Continue by adding the RESTful endpoints described below to the project.

ChartsResource retrieves a list of charts for the home page and adds a new chart to the collection (using the POST HTTP method):

@Path("/charts")
public class ChartsResource {
    @Context UriInfo uriInfo;
    @Resource private ChartManager chartManager;

    ChartsResource(UriInfo uriInfo) {
        this.uriInfo = uriInfo;
    }

    @GET
    @Produces("application/json")
    public JSONArray getCharts() {
        JSONArray charts = new JSONArray();
        for (Chart chart : 
            chartManager.listChartsFor(Security.getPrincipal())) {
            charts.put(chart.toJSON());
        }
        return charts;
    }

    @POST
    @Consumes("application/json")
    public Response createChart(JSONObject chartConfig) {
        final Chart chart = new Chart(chartConfig);
        chartManager.update(chart);
        return Response.created(buildUri(chart)).build();
    }

    private URI buildUri(Chart chart) {
        return uriInfo.getAbsolutePathBuilder().path(
            chart.getId().toString()).build();
    }
}

The ChartResource is responsible for retrieving details about a chart, for updating a chart’s options, or for retiring/deleting a chart:

@Path("charts/{chartId}")
public class ChartResource {
    @Context UriInfo uriInfo;
    @Context Request request;

    int chartId;

    @Resource private ChartManager chartManager;
	
    ChartResource(UriInfo uriInfo, Request request, int chartId) {
        this.uriInfo = uriInfo;
        this.request = request;
        this.chartId = chartId;
    }
	
    @GET
    @Produces("application/json")
    public JSONObject getChart() {
        return chartManager.get(chartId).toJSON();
    }
		
    @PUT
    @Consumes("application/json")
    public void updateChart() {
        chartManager.update(chartManager.get(chartId));
    }
		
    @DELETE
    public void deleteChart() {
        chartManager.delete(chartId);
    }
}

Finally, add the endpoint designed to retrieve the actual data points that the chart component uses to render the data:

@Path("/charts/{chartId}/data")
public class ChartDataResource {

    @Resource private ChartManager chartManager;

    @GET 
    @Produces("application/json")
    public JSONObject getData(@PathParam("chartId") int chartId) {
        return chartManager.getDataForChart(chartId).toJSON();
    }
}

Implementation of the ChartManager is up to you, as it’s site specific.

Note: You can quickly test the web application endpoints after deployment by clicking the URIs mentioned above using cURL or something more elaborate like soapUI.

Front-end code

The front end consists of two charting components: the summarized dashboard view and the expanded view, where the user can edit a chart’s options as well as preview how the changes affect the data being displayed, and of course the final JSP page, which acts as a dashboard-style aggregate of the user’s chart selections.

In the Adobe Flash Builder 4 environment, start by creating the dashboard view chart component project, as shown in Figure 1.

Figure 1. Create a chart project[1]

Add a PieChart component from the Components view. From the Data/Services view, click Connect to Data/Services, and then select the HTTP service, as shown in Figure 2.

Figure 2. Connect to the HTPP service

Click Next to connect to one of the RESTful endpoints you defined for the back end. Make sure your back-end WAR is deployed and running; otherwise, you won’t be able to perform this step. Configure the HTTP service by pointing to the /charts/{chartId}/data endpoint you configured, as shown in Figure 3. Notice that the {chartId} variable present in the supplied URL is automatically recognized and appears in the parameters section with parameter type URL. This is the correct and expected behavior, because each chart option will be different, and later you need to retrieve their respective options by using the {chartId} variable. This variable is passed to the component by using the standard flashvars parameters.

Figure 3. Configure the HTTP service

Now, switch to the Source view for your component. Notice that the HTTP service appears in your <fx:Declarations> section:

<fx:Declarations>
    <s:CallResponder id="getChartDataResult"/>
    <chartdataservice:ChartDataService id="chartDataService"
        fault="Alert.show(event.fault.faultString + '\n' + event.fault.faultDetail)"
        showBusyCursor="true"/>
    <!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>

In the Data/Services view, click getChartData(). Click Configure Return Type, and let the system auto-detect the return type from sample data, as shown in Figure 4.

Figure 4. Configure the return type

When you’re done, the system will have created the correct data types to be able to receive chart data from a RESTful call. Now, drag the getChartData() service call from your Data/Services view on top of the chart you initially added to the project. In the Bind to Data window, bind the data to the chart component, as shown in Figure 5.

Figure 5. Bind to your chart component

You now register the click event on the chart to facilitate sending a JavaScript call to the originating page to expand this particular chart:

protected function chart_creationCompleteHandler(event:FlexEvent):void
{
    getChartDataResult.token = chartDataService.getChartData(
        FlexGlobals.topLevelApplication.parameters.chartId);

    // register the click event
    chart.addEventListener(ChartItemEvent.ITEM_CLICK, expand);
}

public function expand(e:Event):void {
    ExternalInterface.call(
        "expandChart", FlexGlobals.topLevelApplication.parameters.chartId);
}

Notice that this function relies on a chartId variable, which is extracted from the flashvars parameters passed to it.

Moving on to the expanded chart, begin by creating another project, as shown in Figure 6.

Figure 6. Create an expanded chart project

Repeat the steps for adding the HTTP service from the Data/Services view, but this time, add more RESTful calls to the back end. After adding the same /charts/{chartId}/data, add the RESTful endpoints for retrieving a chart’s options at /charts/options using GET and /charts/{chartId} using the PUT HTTP method (see Figure 7). They are designed to retrieve and update all the possible options a chart supports.

Figure 7. Connect the expanded chart to Data/Services

Next, design the expanded chart. Drag two label controls from your Components view - one for each axis - —and alongside each, drag ComboBox controls. These controls will be automatically populated using the data retrieved from the /charts/options RESTful call meant to describe the configuration options for each chart.

Drag a PieChart and ColumnChart, and arrange them next to each other along with a DataGrid control below the charts. Above the ColumnChart, drag an HSlider (horizontal slider) control for further filtering the results based on a minimum–maximum range. Selecting the slider control, switch to the Source view and change the slider to an mx component rather than a sparks component, because the sparks component doesn’t support multiple sliders. Add the thumbCount property with a value of 2. It should look something like this:

<mx:HSlider  x="301" y="11" width="429" thumbCount="2"/>

The DataGrid control is meant to display raw data based on the filtering the user selects by simply clicking on any of the chart’s items (see Figure 8).

Figure 8. Design the expanded chart

Equivalent to Figure 5, bind the PieChart and the ColumnChart to the data section of the data provider and the DataGrid to the rawData section.

You use the X and Y axes as criteria for displaying the PieChart. The selection based on the PieChart and the timeline filtering based on the slider control are the criteria for displaying the ColumnChart and DataGrid. (In the case of the ColumnChart, its Y axis is replaced by the time dimension.)

After the two axis dimensions have been selected, the user has the option of clicking the pie chart’s slices. The selected slice explodes out of the pie, indicating that it has been selected. The ColumnChart and the DataGrid are updated based on the new selection. Furthermore, the user can slide on any of the sliders at the top to change minimum and maximum for the range corresponding to the timeline axis. The selections should be able to be persisted via the filtering options for the chart and performed on the RESTful PUT operation for updating a chart’s options. Selecting the two axis data fields along with the selection on the PieChart and the range selection constitute the equivalent of a web form meant to be updating the chart’s options.

Now, attach event handlers to the following components:

  • For the PieChart, the itemClick event;
  • For both ComboBox components, the change event; and
  • For the HSlider control, the change event.

By clicking Apply, the selected criteria are sent to the server to be saved via the updateChart operation.

These steps are one possible scenario you might choose to display filtering criteria, although you could add advanced options so that the user could filter based on non-visual fields, have various custom comparators, and so on. Another idea would be to display on the ColumnChart multiple bars based on multiple fields in order to perform head-to-head comparisons of data sets.

Adding animation to the charts presents an appealing visual enhancement, although using it extensively might be distracting to the user. Be careful which animations you select for each chart: The safe option is usually to enable the following effect:

showDataEffect="rearrangeData" 

You might also want to paginate the DataGrid, because when first visiting the page (or when accessing the creation of a new chart panel), there are no active selections, and the returned dataset might exceed reasonable numbers. In this case, you will probably need to make an extra call to the server each time the selections change.

Finally, the JSP aggregation page contains code similar to the following jQuery sample:

$.ajax({
    url: '/charts',
    success: function(result){
        for(chart in JSON.parse(result))
            $(this).displayChart(chart.id);
}})

The invoked function displayChart is a JavaScript function meant to display one chart with the passed ID. This function basically represents embedding an Adobe Flash object in the page similar to the one found in the index.template.html file that Adobe Flash automatically generated. You will also need to implement the expand function, which is used to display the expanded component using a passed ID..

Conclusion

In this article, we described how to configure a back-end web application using Java technology to serve a RESTful API for creating, updating, deleting, and retrieving dashboard-style charts based on user information in order to show users only data relevant to them. You then assembled the user interface components using Adobe Flash Builder 4 by creating a simple summarized chart and an expanded chart meant for changing the options of a chart.

The main challenges you will face are related to how you integrate your existing data stores with the project and how to address issues like performance once many users are selecting a multitude of these charts to be displayed on their home page. One recommendation is to create a data warehouse that would only update periodically so that the real-time data used in applications is not affected.

Finally the presentation options both for the underlying data and annimation need to be carefully considered.

About the Author

Daniel Morgan is a software engineer interested in Web, Java, SOA and Flex development. With a background in computer science, he's worked with various companies in financial, telecom, biotech and e-commerce. He enjoys creating easy to use and flexible user interfaces and integrating them with various enterprise software ecosystems. In his spare time, Daniel examines various mobile technologies.

Note: The example code can be downloaded here.


[1] Prior to version 4, Adobe Flash Builder was named Flex, and Adobe has kept the name for some of its internal components

Rate this Article

Adoption
Style

BT