Thứ Tư, 13 tháng 2, 2013

Styling JavaFX Pie Chart with CSS

JavaFX provides certain colors by default when rendering charts. There are situations, however, when one wants to customize these colors. In this blog post I look at changing the colors of a JavaFX pie chart using an example I intend to include in my presentation this afternoon at RMOUG Training Days 2013.

Some Java-based charting APIs provided Java methods to set colors. JavaFX, born in the days of HTML5 prevalence, instead uses Cascading Style Sheets (CSS) to allow developers to adjust colors, symbols, placement, alignment and other stylistic issues used in their charts. I demonstrate using CSS to change colors here.

In this post, I will look at two code samples demonstrating simple JavaFX applications that render pie charts based on data from Oracle's sample 'hr' schema. The first example does not specify colors and so uses JavaFX's default colors for pie slices and for the legend background. That example is shown next.

EmployeesPerDepartmentPieChart (Default JavaFX Styling)

package rmoug.td2013.dustin.examples;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

/**
* Simple JavaFX application that generates a JavaFX-based Pie Chart representing
* the number of employees per department.
*
* @author Dustin
*/
public class EmployeesPerDepartmentPieChart extends Application
{
final DbAccess databaseAccess = DbAccess.newInstance();

@Override
public void start(final Stage stage) throws Exception
{
final PieChart pieChart =
new PieChart(
ChartMaker.createPieChartDataForNumberEmployeesPerDepartment(
this.databaseAccess.getNumberOfEmployeesPerDepartmentName()));
pieChart.setTitle("Number of Employees per Department");
stage.setTitle("Employees Per Department");
final StackPane root = new StackPane();
root.getChildren().add(pieChart);
final Scene scene = new Scene(root, 800 ,500);
stage.setScene(scene);
stage.show();
}

public static void main(final String[] arguments)
{
launch(arguments);
}
}

When the above simple application is executed, the output shown in the next screen snapshot appears.

I am now going to adapt the above example to use a custom "theme" of blue-inspired pie slices with a brown background on the legend. Only one line is needed in the Java code to include the CSS file that has the stylistic specifics for the chart. In this case, I added several more lines to catch and print out any exception that might occur while trying to load the CSS file. With this approach, any problems loading the CSS file will lead simply to output to standard error stating the problem and the application will run with its normal default colors.

EmployeesPerDepartmentPieChartWithCssStyling (Customized CSS Styling)

package rmoug.td2013.dustin.examples;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

/**
* Simple JavaFX application that generates a JavaFX-based Pie Chart representing
* the number of employees per department and using style based on that provided
* in CSS stylesheet chart.css.
*
* @author Dustin
*/
public class EmployeesPerDepartmentPieChartWithCssStyling extends Application
{
final DbAccess databaseAccess = DbAccess.newInstance();

@Override
public void start(final Stage stage) throws Exception
{
final PieChart pieChart =
new PieChart(
ChartMaker.createPieChartDataForNumberEmployeesPerDepartment(
this.databaseAccess.getNumberOfEmployeesPerDepartmentName()));
pieChart.setTitle("Number of Employees per Department");
stage.setTitle("Employees Per Department");
final StackPane root = new StackPane();
root.getChildren().add(pieChart);
final Scene scene = new Scene(root, 800 ,500);
try
{
scene.getStylesheets().add("chart.css");
}
catch (Exception ex)
{
System.err.println("Cannot acquire stylesheet: " + ex.toString());
}
stage.setScene(scene);
stage.show();
}

public static void main(final String[] arguments)
{
launch(arguments);
}
}

The chart.css file is shown next:

chart.css

/*
Find more details on JavaFX supported named colors at
http://docs.oracle.com/javafx/2/api/javafx/scene/doc-files/cssref.html#typecolor
*/

/* Colors of JavaFX pie chart slices. */
.data0.chart-pie { -fx-pie-color: turquoise; }
.data1.chart-pie { -fx-pie-color: aquamarine; }
.data2.chart-pie { -fx-pie-color: cornflowerblue; }
.data3.chart-pie { -fx-pie-color: blue; }
.data4.chart-pie { -fx-pie-color: cadetblue; }
.data5.chart-pie { -fx-pie-color: navy; }
.data6.chart-pie { -fx-pie-color: deepskyblue; }
.data7.chart-pie { -fx-pie-color: cyan; }
.data8.chart-pie { -fx-pie-color: steelblue; }
.data9.chart-pie { -fx-pie-color: teal; }
.data10.chart-pie { -fx-pie-color: royalblue; }
.data11.chart-pie { -fx-pie-color: dodgerblue; }

/* Pie Chart legend background color and stroke. */
.chart-legend { -fx-background-color: sienna; }

Running this CSS-styled example leads to output as shown in the next screen snapshot. The slices are different shades of blue and the legend's background is "sienna." Note that while I used JavaFX "named colors," I could have also used "#0000ff" for blue, for example.

I did not show the code here for my convenience classes ChartMaker and DbAccess. The latter simply retrieves the data for the charts from the Oracle database schema via JDBC and the former converts that data into the Observable collections appropriate for the PieChart(ObservableList) constructor.

It is important to note here that, as Andres Almiray has pointed out, it is not normally appropriate to execute long-running processes from the main JavaFX UI thread (AKA JavaFX Application Thread) as I've done in this and other other blog post examples. I can get away with it in these posts because the examples are simple, the database retrieval is quick, and there is not much more to the chart rendering application than that rendering so it is difficult to observe any "hanging." In a future blog post, I intend to look at the better way of handling the database access (or any long-running action) using the JavaFX javafx.concurrent package (which is well already well described in Concurrency in JavaFX).

JavaFX allows developers to control much more than simply chart colors with CSS. Two very useful resources detailing what can be done to style JavaFX charts with CSS are the Using JavaFX Charts section Styling Charts with CSS and the JavaFX CSS Reference Guide. CSS is becoming increasingly popular as an approach to styling web and mobile applications. By supporting CSS styling in JavaFX, the same styles can easily be applied to JavaFX apps as the HTML-based applications they might coexist with.

Thứ Ba, 12 tháng 2, 2013

JavaFX Coming Soon to an Android or iOS Device Near You?

There has been big news recently in the world of JavaFX regarding many more components of JavaFX being open sourced as advertised at JavaOne 2012. In February Open Source Update, Richard Bair compiled a table of JavaFX projects that have been open sourced as of that post's writing (Monday, 11 February 2013). As exciting as all that open sourcing is, there was something even more exciting highlighted below the table: "We’re also going to open source our iOS and Android implementations over the next couple months."

Bair adds some timing and background information to this significant announcement:

The first bits and pieces for iOS should be out next week, with the rest of iOS and Android coming out at about the same time as the rest of prism (there is some timing dependency there). Both our ports are based on an as-yet unreleased version of JavaSE Embedded for iOS/Android.

After expressing the expected caveat "I’m not a lawyer", Bair also addresses licensing issues on iOS and points out that "both OpenJFX and OpenJDK are both licensed with the same GPLv2 with Classpath Extension." He further describes his understanding of the licensing situation: "this means that if you take OpenJFX + OpenJDK (minus any binary stubs released under a different license), then you can safely combine this with your application and release your application under your own license as a single application co-bundle." I am sure we'll hear more about the licensing details in the future as this develops.

Being able to develop Android and iOS applications with JavaFX will likely be a game-changer for JavaFX. I echo Bair's concluding sentence: "I am looking forward to seeing what you all will do with this contribution, and hope to be running many Java apps on my phone / iPad in the near future." I look forward to using (and maybe even writing) some JavaFX-based apps on my Droid! I am presenting on JavaFX at Rocky Mountain Oracle Users Group (RMOUG) Training Days 2013 tomorrow, so the timing of this announcement couldn't have been better.

Thứ Hai, 11 tháng 2, 2013

Hello GroovyFX

GroovyFX brings together two of my favorite things: Groovy and JavaFX. The main GroovyFX Project page describes GroovyFX as "[providing] a Groovy binding for JavaFX 2.0." GroovyFX is further described on that page:

GroovyFX is an API that makes working with JavaFX in Groovy much simpler and more natural. GroovyFX is focused on exploiting the power of the Groovy Builder pattern to make JavaFX development easier and more concise than what is possible in Java. GroovyFX also leverages Groovy's powerful DSL features and AST transformations to eliminate boilerplate, making GroovyFX code easier to write and, just as importantly, easier to read.

The just referenced main GroovyFX page includes a "Hello World" example. In this post, I look at an even simpler "Hello World" example using GroovyFX. After that, I look at a slightly more involved example of using GroovyFX to render a Pie Chart. Both of these are examples I intend to show at my RMOUG Training Days 2013 presentation ("Charting Oracle Database Data with JavaFX and Groovy") this coming week.

A bare-bones GroovyFX Hello World! example is shown in the next code listing.


import groovyx.javafx.GroovyFX
import groovyx.javafx.SceneGraphBuilder
import javafx.stage.StageStyle
import javafx.stage.Stage

GroovyFX.start
{
stage(title: 'RMOUG Training Days 2013',
width: 300, height: 100,
show: true)
{
scene
{
stackPane
{
text('Hello GroovyFX!', x: 50, y: 40)
}
}
}
}

Running the above script leads to the following output:

The code and screen snapshot show how the concise text of GroovyFX makes it easy in just a few lines of code to specify a fully functioning JavaFX graphical application.

The next code listing shows a slightly more involved examples that generates a JavaFX Pie Chart. The database access code is not shown here, but it is easily accomplished with JDBC or Groovy SQL.


import rmoug.td2013.dustin.examples.ChartMaker
import rmoug.td2013.dustin.examples.DbAccess
import groovyx.javafx.GroovyFX
import groovyx.javafx.SceneGraphBuilder
import javafx.stage.StageStyle
import javafx.stage.Stage

def databaseAccess = DbAccess.newInstance()

GroovyFX.start
{
stage(title: 'Employees Per Department',
width: 800, height: 500,
show: true)
{
scene
{
stackPane
{
pieChart(title: 'Number of Employees per Department',
data: ChartMaker.createPieChartDataForNumberEmployeesPerDepartment(
databaseAccess.getNumberOfEmployeesPerDepartmentName()))
}
}
}
}

The above GroovyFX code leads to the following screen snapshot.

The simple GroovyFX code shown above combines Groovy with JavaFX to render a pie chart representation of the number of employees per department in the Oracle 'hr' sample schema.

The next code sample indicates the roughly equivalent source code for a JavaFX application that does not use GroovyFX.


package rmoug.td2013.dustin.examples;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class EmployeesPerDepartmentPieChart extends Application
{
final DbAccess databaseAccess = DbAccess.newInstance();

@Override
public void start(final Stage stage) throws Exception
{
final PieChart pieChart =
new PieChart(
ChartMaker.createPieChartDataForNumberEmployeesPerDepartment(
this.databaseAccess.getNumberOfEmployeesPerDepartmentName()));
pieChart.setTitle("Number of Employees per Department");
stage.setTitle("Employees Per Department");
final StackPane root = new StackPane();
root.getChildren().add(pieChart);
stage.setScene(new Scene(root, 800 ,500));
stage.show();
}

public static void main(final String[] arguments)
{
launch(arguments);
}
}

The code for the direct JavaFX example can be compared to the GroovyFX example to see how the GroovyFX syntax is more concise (as is expected for something based on Groovy) and arguably more readable than the straight JavaFX code (though I maintain the JavaFX code is fairly readable in its own right). Comparing the two code samples also helps to see how GroovyFX uses property names well-known to users of the JavaFX API.

I am a fan of Groovy and of JavaFX and GroovyFX brings them together.

Thứ Tư, 30 tháng 1, 2013

Hello Camel: Automatic File Transfer

Apache Camel is described on its main web page (and in the Camel User Guide) as "a versatile open-source integration framework based on known Enterprise Integration Patterns." The Camel framework is based on the book Enterprise Integration Patterns and provides implementations of the patterns described in that book. I look at a "Hello World" type example of using Camel in this post.

The Camel web page and Users Guide also reference the StackOverflow thread what exactly is Apache Camel? that includes several good descriptions of Apache Camel. David Newcomb has described Camel there:

Apache Camel is messaging technology glue with routing. It joins together messaging start and end points allowing the transference of messages from different sources to different destinations. For example: JMS->JSON, HTTP->JMS or funneling FTP->JMS, HTTP->JMS, JMS=>JSON.

In this post, I look at a simple use of Camel that doesn't require use of a JMS provider or even FTP or HTTP. Keeping the example simple makes it clearer how to use Camel. This example uses Camel to transfer files automatically from a specified directory to a different specified directory. Three cases will be demonstrated.

In the first case, files placed in the "input" directory are automatically copied to an "output" directory without affecting the source files. In the second case, the files placed in the "input" directory are automatically copied to an "output" directory and then the files in the "input" directory are stored in a special ".camel" subdirectory under the "input" directory. The third case removes the files from the "input" directory upon copying to the "output" directory (effectively a "move" operation). All three cases are implemented with almost identical code. The only difference between the three is in the single line specifying how Camel should handle the file transfers.

The next code listing shows the basic code needed to use Camel to automatically copy files placed in an input directory into a different output directory with Camel.


/**
* Simple executable function to demonstrate Camel file transfer.
*
* @param arguments Command line arguments; excepting duration in milliseconds
* as single argument.
*/
public static void main(final String[] arguments)
{
final long durationMs = extractDurationMsFromCommandLineArgs(arguments);
final CamelContext camelContext = new DefaultCamelContext();
try
{
camelContext.addRoutes(
new RouteBuilder()
{
@Override
public void configure() throws Exception
{
from("file:C:\\datafiles\\input?noop=true").to("file:C:\\datafiles\\output");
}
});
camelContext.start();
Thread.sleep(durationMs);
camelContext.stop();
}
catch (Exception camelException)
{
LOGGER.log(
Level.SEVERE,
"Exception trying to copy files - {0}",
camelException.toString());
}
}

The code above demonstrates minimal use of the Camel API and Camel's Java DSL support. A CamelContext is defined with an instantiation of DefaultCamelContext (line 10). Lines 13-21 add the Camel route to this instantiated context and line 22 starts the context with line 24 stopping the context. It's all pretty simple, but the most interesting part to me is the specification of the routing on line 19.

Because the instance implementing the RoutesBuilder interface provided to the Camel Context only requires its abstract configure method to be overridden, it is an easy class to instantiate as an anonymous class inline with the call to CamelContext.addRoutes(RoutesBuilder). This is what I did in the code above and is what is done in many of the Camel examples that are available online.

Line 19 shows highly readable syntax describing "from" and "to" portions of routing. In this case, files placed in the input directory ("from") are to be copied to the output directory ("to"). The "file" protocol is used on both the "from" and "to" portions because the file system is where the "message" is coming from and going to. The "?noop=true" in the "from" call indicates that nothing should be changed about the files in the "input" directory (the processing should have "noop" effect on the source files).

As just mentioned, Line 19 in the code above instructs Camel to copy files already in or placed in the "input" directory to the specified "output" directory without impacting the files in the "input" directory. In some cases, I may want to "move" the files rather than "copying" them. In such cases, ?delete=true can be specified instead of ?noop=true when specifying the "from" endpoint. In other words, line 19 above could be replaced with this to have files removed from the "input" directory when placed in the "output" directory. If no parameter is designated on the input (neither ?noop=true nor ?delete=true), then an action that falls in-between those occurs: the files in the "input" directory are moved into a specially created new subdirectory under the "input" directory called .camel. The three cases are highlighted next.

Files Copied from datafiles\input to datafiles\output Without Impacting Original Files

from("file:C:\\datafiles\\input?noop=true").to("file:C:\\datafiles\\output");
Files Moved from datafiles\input to datafiles\output

from("file:C:\\datafiles\\input?delete=true").to("file:C:\\datafiles\\output");
Files Copied from datafiles\input to datafiles\output and Original Files Moved to .camel Subdirectory

from("file:C:\\datafiles\\input").to("file:C:\\datafiles\\output");

As a side note, the uses of fluent "from" and "to" are examples of Camel's Java DSL. Camel implements this via implementation inheritance (methods like "from" and "to" are defined in the RouteBuilder class) rather than through static imports (an approach often used for Java-based DSLs.)

Although it is common to pass anonymous instances of RouteBuilder to the Camel Context, this is not a requirement. There can be situations in which it is advantageous to have standalone classes that extend RouteBuilder and have instances of those extended classes passed to the Camel Context. I'll use this approach to demonstrate all three cases I previously described. The next code listing shows a class that extends RouteBuilder. In many cases, I would have a no-arguments constructor, but in this case I use the constructor to determine which type of file transfer should be supported by the Camel route.

The next code listing shows a named standalone class that handles all three cases shown above (copying, copying with archiving of input files, and moving). This single extension of RouteBuilder takes an enum in its constructor to determine how to configure the input endpoint.


package dustin.examples.camel;

import org.apache.camel.builder.RouteBuilder;

/**
* Camel-based Route Builder for transferring files.
*
* @author Dustin
*/
public class FileTransferRouteBuilder extends RouteBuilder
{
public enum FileTransferType
{
COPY_WITHOUT_IMPACTING_ORIGINALS("C"),
COPY_WITH_ARCHIVED_ORIGINALS("A"),
MOVE("M");

private final String letter;

FileTransferType(final String newLetter)
{
this.letter = newLetter;
}

public String getLetter()
{
return this.letter;
}

public static FileTransferType fromLetter(final String letter)
{
FileTransferType match = null;
for (final FileTransferType type : FileTransferType.values())
{
if (type.getLetter().equalsIgnoreCase(letter))
{
match = type;
break;
}
}
return match;
}
}

private final String fromEndPointString;
private final static String FROM_BASE = "file:C:\\datafiles\\input";
private final static String FROM_NOOP = FROM_BASE + "?noop=true";
private final static String FROM_MOVE = FROM_BASE + "?delete=true";

public FileTransferRouteBuilder(final FileTransferType newFileTransferType)
{
if (newFileTransferType != null)
{
switch (newFileTransferType)
{
case COPY_WITHOUT_IMPACTING_ORIGINALS :
this.fromEndPointString = FROM_NOOP;
break;
case COPY_WITH_ARCHIVED_ORIGINALS :
this.fromEndPointString = FROM_BASE;
break;
case MOVE :
this.fromEndPointString = FROM_MOVE;
break;
default :
this.fromEndPointString = FROM_NOOP;
}
}
else
{
fromEndPointString = FROM_NOOP;
}
}

@Override
public void configure() throws Exception
{
from(this.fromEndPointString).to("file:C:\\datafiles\\output");
}
}

This blog post has demonstrated use of Camel to easily route files from one directory to another. Camel supports numerous other transport mechanisms and data formats that are not shown here. Camel also supports the ability to transform the messages/data being routed, which is also not shown here. This post focused on what is likely to be as simplest possible example of how to apply Camel in a useful manner, but Camel supports far more than shown in this simple example.

Thứ Năm, 24 tháng 1, 2013

JavaFX 2 XYCharts and Java 7 Features

One of my favorite features of JavaFX 2 is the standard charts it provides in its javafx.scene.chart package. This package provides several different types of charts out-of-the-box. All but one of these (the PieChart) are "2 axis charts" (specific implementations of the XYChart). In this post, I look at the commonality between these specializations of XYChart. Along the way, I look at several Java 7 features that come in handy.

A UML class diagram for key chart types in the javafx.scene.chart package is shown next. Note that AreaChart, StackedAreaChart, BarChart, StackedBarChart, BubbleChart, LineChart, and ScatterChart all extend XYChart.

As the UML diagram above (generated using JDeveloper) indicates, the PieChart extends Chart directly while all the other chart types extend XYChart. Because all the chart types other than PieChart extend XYChart, they share some common features. For example, they are all 2-axis charts with a horizontal ("x") axis and a vertical ("y") axis. They generally allow data to be specified in the same format (data structure) for all the XY charts. The remainder of this post demonstrates being able to use the same data for most of the XYCharts.

The primary use of a chart is to show data, so the next code listing indicates retrieving of data from the 'hr' sample schema in an Oracle database. Note that JDBC_URL, USERNAME, PASSWORD, and AVG_SALARIES_PER_DEPARTMENT_QUERY are constant Strings used in the JDBC connection and for the query.

getAverageDepartmentsSalaries()

/**
* Provide average salary per department name.
*
* @return Map of department names to average salary per department.
*/
public Map<String, Double> getAverageDepartmentsSalaries()
{
final Map<String, Double> averageSalaryPerDepartment = new HashMap<>();
try (final Connection connection = DriverManager.getConnection(JDBC_URL, USERNAME, PASSWORD);
final Statement statement = connection.createStatement();
final ResultSet rs = statement.executeQuery(AVG_SALARIES_PER_DEPARTMENT_QUERY))
{
while (rs.next())
{
final String departmentName = rs.getString(COLUMN_DEPARTMENT_NAME);
final Double salaryAverage = rs.getDouble(ALIAS_AVERAGE_SALARY);
averageSalaryPerDepartment.put(departmentName, salaryAverage);
}
}
catch (SQLException sqlEx)
{
LOGGER.log(
Level.SEVERE,
"Unable to get average salaries per department - {0}", sqlEx.toString());
}
return averageSalaryPerDepartment;
}

The Java code snippet above uses JDBC to retrieve data for populating a Map of department name Strings to the average salary of the employees in each department. There are a couple of handy Java 7 features used in this code. A small feature is the inferred generic parameterized typing of the diamond operator used with the declaration of the local variable averageSalaryPerDepartment (line 8). This is a small granule of syntax sugar, but it does make the code more concise.

A more significant Java 7 feature is use of try-with-resources statement for the handling of the Connection, Statement, and ResultSet resources (lines 9-11). This is a much nicer way to handle the opening and closing of these resources, even in the face of exceptions, than was previously necessary when using JDBC. The Java Tutorials page on The try-with-resources Statement advertises that this statement "ensures that each resource is closed at the end of the statement" and that each resource will "be closed regardless of whether the try statement completes normally or abruptly." The page also notes that when there are multiple resources specified in the same statement as is done in the above code, "the close methods of resources are called in the opposite order of their creation."

The data retrieved from the database can be placed into the appropriate data structure to support use by most of the XYCharts. This is shown in the next method.

ChartMaker.createXyChartDataForAverageDepartmentSalary(Map)

/**
* Create XYChart Data representing average salary per department name.
*
* @param newAverageSalariesPerDepartment Map of department name (keys) to
* average salary for each department (values).
* @return XYChart Data representing average salary per department.
*/
public static ObservableList<XYChart.Series<String, Double>> createXyChartDataForAverageDepartmentSalary(
final Map<String, Double> newAverageSalariesPerDepartment)
{
final Series<String, Double> series = new Series<>();
series.setName("Departments");
for (final Map.Entry<String, Double> entry : newAverageSalariesPerDepartment.entrySet())
{
series.getData().add(new XYChart.Data<>(entry.getKey(), entry.getValue()));
}
final ObservableList<XYChart.Series<String, Double>> chartData =
FXCollections.observableArrayList();

chartData.add(series);
return chartData;
}

The method just shown places the retrieved data in a data structure that can be used by nearly all of the XYChart-based charts. With the retrieved data now packaged in a JavaFX observable collection, the charts can be easily generated. The next code snippet shows methods for generating several XYChart-based charts (Area, Bar, Bubble, Line, and Scatter). Note how similar they all are and how the use the same data provided by the same method. The StackedBar and StackedArea charts can also use similar data, but are not shown here because they are not interesting for the single series of data being used in this example.

Methods for Generating XYCharts Except BubbleChart and Stacked Charts

private XYChart<String, Double> generateAreaChart(
final Axis<String> xAxis, final Axis<Double> yAxis)
{
final AreaChart<String, Double> areaChart =
new AreaChart<>(
xAxis, yAxis,
ChartMaker.createXyChartDataForAverageDepartmentSalary(
this.databaseAccess.getAverageDepartmentsSalaries()));
return areaChart;
}

private XYChart<String, Double> generateBarChart(
final Axis<String> xAxis, final Axis<Double> yAxis)
{
final BarChart<String, Double> barChart =
new BarChart<>(
xAxis, yAxis,
ChartMaker.createXyChartDataForAverageDepartmentSalary(
this.databaseAccess.getAverageDepartmentsSalaries()));
return barChart;
}

private XYChart<Number, Number> generateBubbleChart(
final Axis<String> xAxis, final Axis<Double> yAxis)
{
final Axis<Number> deptIdXAxis = new NumberAxis();
deptIdXAxis.setLabel("Department ID");
final BubbleChart<Number, Number> bubbleChart =
new BubbleChart(
deptIdXAxis, yAxis,
ChartMaker.createXyChartDataForAverageDepartmentSalaryById(
this.databaseAccess.getAverageDepartmentsSalariesById()));
return bubbleChart;
}

private XYChart<String, Double> generateLineChart(
final Axis<String> xAxis, final Axis<Double> yAxis)
{
final LineChart<String, Double> lineChart =
new LineChart<>(
xAxis, yAxis,
ChartMaker.createXyChartDataForAverageDepartmentSalary(
this.databaseAccess.getAverageDepartmentsSalaries()));
return lineChart;
}

private XYChart<String, Double> generateScatterChart(
final Axis<String> xAxis, final Axis<Double> yAxis)
{
final ScatterChart<String, Double> scatterChart =
new ScatterChart<>(
xAxis, yAxis,
ChartMaker.createXyChartDataForAverageDepartmentSalary(
this.databaseAccess.getAverageDepartmentsSalaries()));
return scatterChart;
}

These methods are so similar that I could have actually used method handles (or more traditional reflection APIs) to reflectively call the appropriate chart constructor rather than use separate methods. However, I am using these for my RMOUG Training Days 2013 presentation in February and so wanted to leave the chart-specific constructors in place to make them clearer to audience members.

One exception to the general handling of XYChart types is the handling of BubbleChart. This chart expects a numeric type for its x-axis and so the String-based (department name) x-axis data provided above will not work. A different method (not shown here) provides a query that returns average salaries by department ID (Long) rather than by department name. The slightly different generateBubbleChart method is shown next.

generateBubbleChart(Axis, Axis)

private XYChart<Number, Number> generateBubbleChart(
final Axis<String> xAxis, final Axis<Double> yAxis)
{
final Axis<Number> deptIdXAxis = new NumberAxis();
deptIdXAxis.setLabel("Department ID");
final BubbleChart<Number, Number> bubbleChart =
new BubbleChart(
deptIdXAxis, yAxis,
ChartMaker.createXyChartDataForAverageDepartmentSalaryById(
this.databaseAccess.getAverageDepartmentsSalariesById()));
return bubbleChart;
}

Code could be written to call each of these different chart generation methods directly, but this provides a good chance to use Java 7's method handles. The next code snippet shows this being done. Not only does this code demonstrate Method Handles, but it also uses Java 7's multi-catch exception handling mechanism (line 77).


/**
* Generate JavaFX XYChart-based chart.
*
* @param chartChoice Choice of chart to be generated.
* @return JavaFX XYChart-based chart; may be null.
* @throws IllegalArgumentException Thrown if the provided parameter is null.
*/
private XYChart<String, Double> generateChart(final ChartTypes chartChoice)
{
XYChart<String, Double> chart = null;
final Axis<String> xAxis = new CategoryAxis();
xAxis.setLabel("Department Name");
final Axis<? extends Number> yAxis = new NumberAxis();
yAxis.setLabel("Average Salary");
if (chartChoice == null)
{
throw new IllegalArgumentException(
"Provided chart type was null; chart type must be specified.");
}
else if (!chartChoice.isXyChart())
{
LOGGER.log(
Level.INFO,
"Chart Choice {0} {1} an XYChart.",
new Object[]{chartChoice.name(), chartChoice.isXyChart() ? "IS" : "is NOT"});
}

final MethodHandle methodHandle = buildAppropriateMethodHandle(chartChoice);
try
{
chart =
methodHandle != null
? (XYChart<String, Double>) methodHandle.invokeExact(this, xAxis, yAxis)
: null;
chart.setTitle("Average Department Salaries");
}
catch (WrongMethodTypeException wmtEx)
{
LOGGER.log(
Level.SEVERE,
"Unable to invoke method because it is wrong type - {0}",
wmtEx.toString());
}
catch (Throwable throwable)
{
LOGGER.log(
Level.SEVERE,
"Underlying method threw a Throwable - {0}",
throwable.toString());
}

return chart;
}

/**
* Build a MethodHandle for calling the appropriate chart generation method
* based on the provided ChartTypes choice of chart.
*
* @param chartChoice ChartTypes instance indicating which type of chart
* is to be generated so that an appropriately named method can be invoked
* for generation of that chart.
* @return MethodHandle for invoking chart generation.
*/
private MethodHandle buildAppropriateMethodHandle(final ChartTypes chartChoice)
{
MethodHandle methodHandle = null;
final MethodType methodDescription =
MethodType.methodType(XYChart.class, Axis.class, Axis.class);
final String methodName = "generate" + chartChoice.getChartTypeName() + "Chart";

try
{
methodHandle =
MethodHandles.lookup().findVirtual(
this.getClass(), methodName, methodDescription);
}
catch (NoSuchMethodException | IllegalAccessException exception)
{
LOGGER.log(
Level.SEVERE,
"Unable to acquire MethodHandle to method {0} - {1}",
new Object[]{methodName, exception.toString()});
}
return methodHandle;
}

A series of images follows that shows how these XY Charts appear when rendered by JavaFX.

Area Chart
Bar Chart
Bubble Chart
Line Chart
Scatter Chart

As stated above, Method Handles could have been used to reduce the code even further because individual methods for generating each XYChart are not absolutely necessary and could have been reflectively called based on desired chart type. It's also worth emphasizing that if the x-axis data had been numeric, the code would be the same (and could be reflectively called) for all XYChart types including the Bubble Chart.

JavaFX makes it easy to generate attractive charts representing provided data. Java 7 features make this even easier by making code more concise and more expressive and allowing for easy application of reflection when appropriate.

Thứ Sáu, 18 tháng 1, 2013

Java Zero Day Vulnerability Exploits JMX and MethodHandles

I recently identified software security issues (#2), especially related to Java, as one of the most significant software development themes of 2012. Not even a month into 2013, a news story receiving a lot of press is the U.S. Department of Homeland Security's issuing of Alert (TA13-010A), which is described with more technical details in Vulnerability Note VU#625617. Oracle has since released a Security Alert for CVE-2013-0422.

Vulnerability Note VU#625617 includes a paragraph that is particularly insightful:

By leveraging the a vulnerability in the Java Management Extensions (JMX) MBean components, unprivileged Java code can access restricted classes. By using that vulnerability in conjunction with a second vulnerability involving recursive use of the Reflection API via the invokeWithArguments method of the MethodHandle class, an untrusted Java applet can escalate its privileges by calling the the setSecurityManager() function to allow full privileges, without requiring code signing. Oracle Java 7 update 10 and earlier Java 7 versions are affected. OpenJDK 7, and subsequently IcedTea, are also affected. The invokeWithArguments method was introduced with Java 7, so therefore Java 6 is not affected.

The above scenario is described in great detail in Tim Boudreau's excellent The Java Security Exploit in (Mostly) Plain English and he references Java 0day 1.7.0_10 decrypted source code that demonstrates the code that can implement an attack that takes advantage of the described JMX/MethodHandles combination vulnerability. Kafeine's (Malware don't need Coffee) post 0 day 1.7u10 (CVE-2013-0422) spotted in the Wild - Disable Java Plugin NOW ! provides numerous screen snapshots to illustrate this Java Zero-Day Malware in action.

The TA13-010A/CVE-2013-0422 Java Zero Day Vulnerability has made the mainstream news with coverage by Norton/Symantec (What's All the Buzz About Java? Fixing The Vulnerability and Java Zero-Day Dished Up from Cool Exploit Kit), McAfee (Java Zero-Day Vulnerability Pushes Out Crimeware), InformationWeek (Java Zero Day Attack: Second Bug Found), Fox News (Reuters: As Hacking Concerns Build, U.S. Warns on Java Software), CNN (Critical Java vulnerability due to incomplete earlier patch), and many more news outlets.

As stated above, Oracle has issued a patch, but the Department of Homeland Security still recommends disabling Java in the browser.

Thứ Năm, 17 tháng 1, 2013

Hamcrest Containing Matchers

The Hamcrest 1.3 Javadoc documentation for the Matchers class adds more documentation for several of that class's methods than were available in Hamcrest 1.2. For example, the four overloaded contains methods have more descriptive Javadoc documentation as shown in the two comparison screen snapshots shown next.

Although one can figure out how the "contains" matchers work just by trying them out, the Javadoc in Hamcrest 1.3 makes it easier to read how they work. Most Java developers probably think of behavior like that of String.contains(CharSequence) or Collection.contains(Object) when they think of a contains() method. In other words, most Java developers probably think of "contains" as describing if the String/Collection contains the provided characters/objects among other possible characters/objects. However, for Hamcrest matchers, "contains" has a much more specific meaning. As the Hamcrest 1.3 documentation makes much clearer, the "contains" matchers are much more sensitive to number of items and order of items being passed to these methods.

My examples shown here use JUnit and Hamcrest. It is important to emphasize here that Hamcrest's JAR file must appear on the unit tests' classpath before JUnit's JAR file or else I must use the "special" JUnit JAR file built for use with the standalone Hamcrest JAR. Using either of these approaches avoids the NoSuchMethodError and other errors (suc as org.hamcrest.Matcher.describeMismatch error) resulting from mismatched versions of classes. I have written about this JUnit/Hamcrest nuance in the blog post Moving Beyond Core Hamcrest in JUnit.

The next two screen snapshots indicate the results (as shown in NetBeans 7.3) of the unit test code snippets I show later in the blog to demonstrate Hamcrest containing matchers. The tests are supposed to have some failures (7 tests passing and 4 tests failing) to make it obvious where Hamcrest matchers may not work as one expects without reading the Javadoc. The first image shows only 5 tests passing, 2 tests failing, and 4 tests causing errors. This is because I have JUnit listed before Hamcrest on the NetBeans project's "Test Libraries" classpath. The second image shows the expected results because the Hamcrest JAR occurs before the JUnit JAR in the project's "Test Libaries" classpath.

For purposes of this demonstration, I have a simple contrived class to be tested. The source code for that Main class is shown next.

Main.java

package dustin.examples;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
* Main class to be unit tested.
*
* @author Dustin
*/
public class Main
{
/** Uses Java 7's diamond operator. */
private Set<String> strings = new HashSet<>();

public Main() {}

public boolean addString(final String newString)
{
return this.strings.add(newString);
}

public Set<String> getStrings()
{
return Collections.unmodifiableSet(this.strings);
}
}

With the class to be tested shown, it is now time to look at building some JUnit-based tests with Hamcrest matchers. Specifically, the tests are to ensure that Strings added via the class's addString(String) method are in its underlying Set and accessible via the getStrings() method. The unit test methods shown next demonstrate how to use Hamcrest matchers appropriately to determine if added Strings are contained in the class's underlying Set

Using Hamcrest contains() Matcher with Single String in Set Works

/**
* This test will pass because there is only a single String and so it will
* contain that single String and order will be correct by implication.
*/
@Test
public void testAddStringAndGetStringsWithContainsForSingleStringSoWorks()
{
final Main subject = new Main();
final boolean resultJava = subject.addString("Java");
final Set<String> strings = subject.getStrings();
assertThat(strings, contains("Java"));
}

The unit test shown above passes because the Set only has one String in it and so the order and number of Strings tested with the contains matcher matches.

Using Hamcrest Contains with Same Number of Elements Works if Order Matches

/**
* The "contains" matcher expects exact ordering, which really means it should
* not be used in conjunction with {@code Set}s. Typically, either this method
* will work and the method with same name and "2" on end will not work or
* vice versa.
*/
@Test
public void testAddStringAndGetStringsWithContainsForMultipleStringsNotWorks1()
{
final Main subject = new Main();
final boolean resultJava = subject.addString("Java");
final boolean resultGroovy = subject.addString("Groovy");
final Set<String> strings = subject.getStrings();
assertThat(strings, contains("Java", "Groovy"));
}

/**
* The "contains" matcher expects exact ordering, which really means it should
* not be used in conjunction with {@code Set}s. Typically, either this method
* will work and the method with same name and "1" on end will not work or
* vice versa.
*/
@Test
public void testAddStringAndGetStringsWithContainsForMultipleStringsNotWorks2()
{
final Main subject = new Main();
final boolean resultJava = subject.addString("Java");
final boolean resultGroovy = subject.addString("Groovy");
final Set<String> strings = subject.getStrings();
assertThat(strings, contains("Groovy", "Java"));
}

The two example unit tests shown above and their resultant output of running those test as shown in the previous screen snapshot show that as long as the number of arguments to the contains() matcher are the same as the number of Strings in the collection being tested, the match may work if the elements tested are in exactly the same order as the elements in the collection. With an unordered Set, this order cannot be relied upon, so contains() is not likely to be a good matcher to use with a unit test on a Set of more than one element.

Using Hamcrest Contains with Different Number of Elements Never Works

/**
* Demonstrate that contains will NOT pass when there is a different number
* of elements asked about contains than in the collection.
*/
@Test
public void testAddStringAndGetStringsWithContainsNotWorksDifferentNumberElements1()
{
final Main subject = new Main();
final boolean resultJava = subject.addString("Java");
final boolean resultGroovy = subject.addString("Groovy");
final Set<String> strings = subject.getStrings();
assertThat(strings, contains("Java"));
}

/**
* Demonstrate that contains will NOT pass when there is a different number
* of elements asked about contains than in the collection even when in
* different order.
*/
@Test
public void testAddStringAndGetStringsWithContainsNotWorksDifferentNumberElements2()
{
final Main subject = new Main();
final boolean resultJava = subject.addString("Java");
final boolean resultGroovy = subject.addString("Groovy");
final Set<String> strings = subject.getStrings();
assertThat(strings, contains("Groovy"));
}

As the JUnit test results indicate, these two unit tests never pass because the number of elements being tested for in the Set is fewer than the number of elements in the Set. In other words, this proves that the contains() matcher does not test simply for a given element being in a collection: it tests for all specified elements being present and in the specified order. This might be too limiting in some cases, so now I'll move onto some other matches Hamcrest provides for determining if an element is contained in a particular collection.

Using Hamcrest's containsInAnyOrder() Matcher

The containsInAnyOrder matcher is not as strict as the contains() matcher: it allows tested elements to be in any order within the containing collection to pass.


/**
* Test of addString and getStrings methods of class Main using Hamcrest
* matcher containsInAnyOrder.
*/
@Test
public void testAddStringAndGetStringsWithContainsInAnyOrder()
{
final Main subject = new Main();
final boolean resultJava = subject.addString("Java");
final boolean resultCSharp = subject.addString("C#");
final boolean resultGroovy = subject.addString("Groovy");
final boolean resultScala = subject.addString("Scala");
final boolean resultClojure = subject.addString("Clojure");
final Set<String> strings = subject.getStrings();
assertThat(strings, containsInAnyOrder("Java", "C#", "Groovy", "Scala", "Clojure"));
}

/**
* Use containsInAnyOrder and show that order does not matter as long as
* all entries provided are in the collection in some order.
*/
@Test
public void testAddStringAndGetStringsWithContainsInAnyOrderAgain()
{
final Main subject = new Main();
final boolean resultJava = subject.addString("Java");
final boolean resultGroovy = subject.addString("Groovy");
final Set<String> strings = subject.getStrings();
assertThat(strings, containsInAnyOrder("Java", "Groovy"));
assertThat(strings, containsInAnyOrder("Groovy", "Java"));
}

The two unit tests shown immediately above both pass despite the Strings being tested being provided to the containsInAnyOrder() matcher in a different order than what they could exist in for both collections. However, the less strict containsInAnyOrder() matcher still requires all elements of the containing collection to be specified to pass. The following unit test does not pass because this condition is not met.


/**
* This will fail because containsInAnyOrder requires all items to be matched
* even if in different order. With only one element being tried and two
* elements in the collection, it will still fail. In other words, order
* does not matter with containsInAnyOrder, but all elements in the collection
* still need to be passed to the containsInAnyOrder matcher, just not in the
* exact same order.
*/
@Test
public void testAddStringAndGetStringsWithContainsInAnyOrderDiffNumberElements()
{
final Main subject = new Main();
final boolean resultJava = subject.addString("Java");
final boolean resultGroovy = subject.addString("Groovy");
final Set<String> strings = subject.getStrings();
assertThat(strings, containsInAnyOrder("Java"));
}
Hamcrest hasItem() and hasItems() Matchers Work As Sounds

As shown in the next two unit test methods (both of which pass), the Hamcrest hasItem() (for single item) and hasItems (for multiple items) successfully tests whether a collection has the one or more than one specified items respectively without regard for order or number of specified items. This really works more like most Java developers are used to "contains" working when working with Strings and collections.


/**
* Demonstrate hasItem() will also work for determining a collection contains
* a particular item.
*/
@Test
public void testAddStringAndGetStringsWithHasItem()
{
final Main subject = new Main();
final boolean resultJava = subject.addString("Java");
final boolean resultGroovy = subject.addString("Groovy");
final Set<String> strings = subject.getStrings();
assertThat(strings, hasItem("Groovy"));
assertThat(strings, hasItem("Java"));
}

/**
* Demonstrate that hasItems works for determining that a collection has one
* or more items and that the number of items and the order of the items
* is not significant in determining pass/failure.
*/
@Test
public void testAddStringAndGetStringsWithHasItems()
{
final Main subject = new Main();
final boolean resultJava = subject.addString("Java");
final boolean resultGroovy = subject.addString("Groovy");
final Set<String> strings = subject.getStrings();
assertThat(strings, hasItems("Groovy", "Java"));
assertThat(strings, hasItems("Java", "Groovy"));
assertThat(strings, hasItems("Groovy"));
assertThat(strings, hasItems("Java"));
}
Hamcrest isIn() Matcher Tests Containment from Other Direction

The just-discussed hasItem() and hasItems() matchers are less strict than contains() and even less strict than containsInAnyOrder() and are often what one wants when one wants to simply ensure that one or multiple items are somewhere in a collection without concern about the item's order in that collection or that other possible items are in that collection. One other way to use Hamcrest to determine the same relationship, but from the opposite perspective, is to use isIn matcher. The isIn matcher determines if an item is somewhere with the collection provided to the matcher without regard for that item's order in the collection or whether or not there are other items in that containing collection.


/**
* Use isIn matcher to test individual element is in provided collection.
*/
@Test
public void testAddStringAndGetStringsWithIsIn()
{
final Main subject = new Main();
final boolean resultJava = subject.addString("Java");
final boolean resultGroovy = subject.addString("Groovy");
final Set<String> strings = subject.getStrings();
assertThat("Groovy", isIn(strings));
assertThat("Java", isIn(strings));
}
Conclusion

Hamcrest provides a rich set of matchers that can be used to determine if specified elements reside within a specified collection. Here are important points to keep in mind when deciding to apply these and determining which to use:

  • Ensure that the Hamcrest JAR is on the test classpath before the JUnit JAR.
  • Use contains when you want to ensure that the collection contains all specified items and no other items and you want the collection to contain the items in the specified order.
    • Generally avoid using contains() matcher with Sets because they are unordered by nature.
  • Use containsInAnyOrder matcher when you still want to strictly test for presence of exactly same items in collection as specified in test, but don't care about the order (applicable for Sets).
  • Use hasItem() and hasItems() matchers to ask a collection if it contains, possibly among other unlisted items and in no particular order, the specified item or items.
  • Use isIn() matcher to ask if a particular item is in the specified collection with no regard for whether other items are in that collection or what order that item is in within the containing collection.