Pages

Wednesday, November 24, 2010

Creating Charts on Headless Systems: JFreechart and Rogue Wave

JFreechart Example
This week we had an interesting conundrum at work. We had a headless server which is not uncommon, that would not generate charts in our web based application. Apparently some of the charts are not used very often, or it would have been reported sooner. The charts in question were originally created using Rogue Wave Software charting software from 1999-2000. This software was used to create charts in AWT based applications. Yes, I said AWT not Swing, and I know that it is very old.

The particular charts do not have replacements available in free charting software like JFreechart currently, and are available only as commercial software. We had purchased the original software and licenses from Rogue Wave, and did not want to purchase new commercial software.

Now to the conundrum. Since Java 1.4, heavyweight AWT and Swing components throw a HeadlessException, if you attempt to run them on a headless system.

As you can see from the list, it does not leave much room for the developer to work with in a headless environment. Fair enough, AWT/Swing are GUI environments.

The graphical libraries used to create charts in the case of Rogue Wave and JFreechart generally expect a GUI framework. Rogue Wave is expecting to generate the chart images in a Window, or one of its subclasses like Frame. This can cause an issue as you can see. So I am left with a couple of components that I can use like Component, Canvas, and Panel.

You can create a headless environment and check it with the code below.

System.setProperty("java.awt.headless", "true");
boolean headless = GraphicsEnvironment.isHeadless();
System.out.println("Headless: " + headless);

So how do you generate the charts, or create images in general on a headless system. It turns out to be quite simple. Since all components have a paint(Graphics g) method, and JFreechart has a createBufferedImage() method. These turn out to be the keys.

Solution

We need to create a BufferedImage which we can pass to the Component to paint. Once we create the image to paint to, we simply ask the component to paint it, and use ImageIO to output our chart.

Note: You will need to have the Rogue Wave graphing and gif libraries, or you may comment out those code sections.

NetBeans 6.9 project files: HeadlessAWT.zip

Headless.java

/*
 *  Copyright 2010 Blue Lotus Software.
 *  Copyright 2010 John Yeary.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package com.bluelotussoftware.graph.headless.example;

import java.awt.*;
import java.io.*;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.util.Locale;
import com.roguewave.chart.awt.datamodels.v2_2.SampleData;
import com.roguewave.chart.awt.standard.v2_2.beans.PieChart;
import com.roguewave.chart.awt.standard.v2_2.beans.LineChart;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.encoders.ImageFormat;
import org.jfree.data.general.DefaultKeyedValuesDataset;
import org.jfree.data.general.DefaultPieDataset;

/**
 *
 * @author John Yeary
 * @version 1.0
 */
public class Headless {

    private static BufferedImage generateRectangle(Component component, int width, int height) {
        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics graphics = bufferedImage.getGraphics();
        graphics.setColor(Color.WHITE);
        graphics.fillRect(0, 0, width, height);
        graphics.setColor(Color.ORANGE);
        graphics.fill3DRect(50, 50, 300, 300, true);
        component.paint(graphics);
        return bufferedImage;
    }

    private static BufferedImage generateRectangle(Component component) {
        return generateRectangle(component, 400, 400);
    }

    private static BufferedImage generateCylinder(int width, int height) {
        Panel panel = new Panel();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics graphics = image.getGraphics();
        graphics.setColor(Color.WHITE);
        graphics.fillRect(0, 0, width, height);
        graphics.setColor(Color.ORANGE);
        graphics.drawOval(100, 100, 50, 75);
        graphics.fillOval(200, 100, 50, 75);
        graphics.drawLine(125, 100, 225, 100);
        graphics.drawLine(125, 175, 225, 175);
        panel.paint(graphics);
        return image;
    }

    private static boolean save(BufferedImage image, Component component) {
        boolean success = false;
        try {
            ImageIO.write(image, "gif", new FileOutputStream(component.getClass().getSimpleName() + ".gif"));
            success = true;
        } catch (FileNotFoundException fnfe) {
            fnfe.printStackTrace(System.err);
        } catch (IOException ex) {
            ex.printStackTrace(System.err);
        }
        return success;
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException {
        System.setProperty("java.awt.headless", "true");
        boolean headless = GraphicsEnvironment.isHeadless();
        System.out.println("Headless: " + headless);
        Toolkit tk = Toolkit.getDefaultToolkit();
        tk.beep();

        BufferedImage bufferedImage = null;
        boolean success = false;

        // ---------------------------------------------------------------------------------------------- //
        Component component = new Component() {

            private static final long serialVersionUID = 3109256773218160485L;
        };
        Canvas canvas = new Canvas();
        Panel panel = new Panel();
        // ---------------------------------------------------------------------------------------------- //

        // Drawing Examples
        bufferedImage = Headless.generateRectangle(component);
        success = Headless.save(bufferedImage, component);
        System.out.println("Created " + component.getClass().getSimpleName() + " : " + success);
        success = false;

        bufferedImage = Headless.generateRectangle(canvas);
        success = Headless.save(bufferedImage, canvas);
        System.out.println("Created " + canvas.getClass().getSimpleName() + " : " + success);
        success = false;

//        bufferedImage = Headless.generateRectangle(panel);
//        success = Headless.save(bufferedImage, panel);
//        System.out.println("Created " + panel.getClass().getSimpleName() + " : " + success);
//        success = false;

        bufferedImage = Headless.generateCylinder(400, 400);
        success = Headless.save(bufferedImage, panel);
        System.out.println("Created " + panel.getClass().getSimpleName() + " : " + success);
        success = false;
        // ---------------------------------------------------------------------------------------------- //

        // Rogue Wave Examples
        SampleData sd = new SampleData();
        LineChart lineChart = new LineChart();
        lineChart.setData(sd);
        PieChart pieChart = new PieChart();
        pieChart.setData(sd);

        // The chart size is required.
        lineChart.setSize(400, 400);
        pieChart.setSize(400, 400);

        bufferedImage = new BufferedImage(400, 400, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = (Graphics2D) bufferedImage.getGraphics();
        lineChart.paint(g);
        ImageIO.write(bufferedImage, ImageFormat.PNG, new FileOutputStream("LineChart.png"));
        pieChart.paint(g);
        ImageIO.write(bufferedImage, ImageFormat.PNG, new FileOutputStream("PieChart.png"));
        // ---------------------------------------------------------------------------------------------- //

        // JFreechart Examples
        DefaultPieDataset dpds = new DefaultKeyedValuesDataset();
        dpds.setValue("Java", 60.0);
        dpds.setValue("C++", 20.0);
        dpds.setValue("MS Technologies", 10.0);
        dpds.setValue("Misc.", 10.0);

        JFreeChart jfc = ChartFactory.createPieChart("Programming Languages", dpds, true, true, Locale.ENGLISH);
        bufferedImage = jfc.createBufferedImage(400, 400);
        ImageIO.write(bufferedImage, "gif", new FileOutputStream("jfc-piechart.gif"));
    }
}

Enhanced by Zemanta

1 comments :