To increase the compatibility between servlets and applets, KavaChart servlets use exactly the same parameter strings (and parameter parsing class) as KavaChart applets. This makes it easy to switch from applet to servlet and back. For example, this is a simple applet definition that creates a bar chart:
<applet code=javachart.applet.barApp width=300 height=200> <param name="dataset0yValues" value="321, 234, 234, 456> <param name="xAxisLabels" value="apples, oranges, peaches, pears"> </applet>The same servlet definition looks like this:
<servlet code=javachart.servlet.ChartServlet> <param name=chartType value=barApp> <param name=width value=300> <param name=height value=200> <param name="dataset0yValues" value=321, 234, 234, 456> <param name="xAxisLabels" value="apples, oranges, peaches, pears"> </servlet>Because servlets run on the server, rather than the client, the chart and image computation expense isn't automatically distributed across many machines. In fact, the server may be called upon to generate many charts simultaneously. To maximize KavaChart's servlet production speed, the javachart.servlet package implements a mechanism to cache images on the server. Once a particular chart definition has passed through the servlet to generate an image file, subsequent requests will simply retrieve the requested image from the server's cache, rather than generate it dynamically.
Servlets deployment varies somewhat from server to server, but for most servers, you simply need to make sure the kcServlet.jar file (found in javachart/jars) is in your CLASSPATH when the server starts up. Passing parameters to a servlet also varies. The definition above assumes you're using SSI (Server Side Includes), to automatically replace the servlet definition with a JPEG reference. Generally, servers will look for servlet tags in files with a .shtml extension. Most servers also provide some mechanism for aliasing specific requests to a servlet with specific parameters.
To generate an image, KavaChart employs various classes in Java's AWT package. This generally requires actual display hardware of some sort on the server, even though nothing will actually appear on the screen. For Windows and NT servers, this isn't generally a problem. Unix servers must have access to an X-windows display. The display needn't be local (although performance may degrade if it's not), and it can even be a "virtual" display, such as the xvfb (X Windows Virtual Framebuffer), which can be freely downloaded from most Linux sites. Bottom line? For Unix, your DISPLAY environment variable must point to an actual X server. Why? KavaChart servlets use a variety of classes (like Font, Image, etc.), which are linked to a native "peer" classes. Without peer classes, Java cannot generate graphical output. (In future JDK 1.3 environments, the BufferedImage class may permit image generation without native peers, but as of this writing, BufferedImage requires an X connection on Unix Java implementations).
It's also possible to replace these peer classes with non-native, pure Java implementations. We suggest you try PJAToolkit, a free download from ETeks if you want to try a pure Java approach.
Even though the default writeDirectory uses a relative path, an absolute path, such as "E:\webserver\public\images" is more likely to work, unless you know exactly which directory is "current" when the servlets run. Double check this directory's permissions to make sure the server ID can read and write this directory. The server probably has more restrictions than does your own login ID.
First, it's important to note that KavaChart servlets are in a Java package. Unlike simple demonstration servlets, KavaChart servlets require a number of interdependent Java classes, and are organized into package groupings. Your servlet engine probably has a "HelloWorld" class that would run from a browser something like this:
http://server:8080/servlet/HelloWorldYou would replace "server" with the name of your server machine or with localhost if the server is on your local machine. Similarly, you'd replace "8080" with a different port, if your servlet engine uses another port, and "servlet" might be different on your configuration. If "HelloWorld" were part of a package called "demo", you'd start it from your browser like this:
http://server:8080/servlet/demo.HelloWorldThe equivalent command for a KavaChart servlet would be similar to this:
http://server:8080/servlet/javachart.servlet.ChartServletWhen the server receives this HTTP GET command, it will attempt to find a Java class named ChartServlet.class in the javachart.servlet package located within it's CLASSPATH. Next, it will attempt to create a JPEG image in the directory "public_html/images", since we didn't change the default writeDirectory within our command. The servlet will return something like <IMG SRC="images/abcd123.jpg">. Your browser will then attempt to get this image file with a conventional HTTP transaction.
We could modify our servlet URL a little bit to write the image to a specific location, and just return the image stream instead of an IMG SRC tag. We'll also add a "debug" parameter to add some information on the server console or log about the image name. Finally, we'll add a parameter to define a bit of data. Here's our modified URL:
http://server:8080/servlet/javachart.servlet.ChartServlet?byteStream=true&debug=true&writeDirectory=/usr/tmp&dataset0yValues=123,342,123You can also refer to your servlet with a registered name. Consult your server documentation for information on how to do this. A registered name can be the same or different than the actual class name. Some servers also permit you to create servlet aliases that will automatically create shortcuts to servlets (and startup properties in some cases).
Another common technique is to use Server Side Include (SSI) to pre-process HTML output to embed the output of a servlet. Pages that contain servlet definitions usually have a .shtml or .jhtml suffix to notify the server that the page should be filtered for servlet output before being sent to a browser. This is what an SSI definition might look like:
<servlet code=javachart.applet.barApp> <param name="dataset0yValues" value="321, 234, 234, 456> <param name="xAxisLabels" value="apples, oranges, peaches, pears"> </servlet>
http://myserver/javachart.servlet.ChartServlet?dataset0yValues=123,432,123&writeDirectory=/usr/tmp&readDirectory=imagesThis will cause the "ChartServlet" servlet to receive a parameter named "dataset0yValues", with a value of "123,432,123". The servlet attempts to write its image into the directory /usr/tmp. Then it sends the browser <IMG SRC=images/imageName.jpg >, which will should be the same as /usr/tmp/imageName.jpg. The image will also have a default width and height. You can use a definition like this with the simplest servlet engine, such as servletrunner.
Even this simple example has a rather unwieldy URL, so you'll probably want to use another mechanism, like server-side includes or servlet chaining to build your parameter lists.
Server-side includes are HTML files that contain special <servlet >tags to indicate regions that will be replaced with servlet output. These files are processed by the server to translate any servlet information into more conventional information that any browser can understand. Our URL above would look like this in a server-side include:
<servlet code=javachart.servlet.ChartServlet > <param name=dataset0yValues value="123,432,123" > <param name=writeDirectory value="/usr/tmp" > <param name=readDirectory value="images" > </servlet >How does the server know that this information should be processed as a servlet? Generally, servers use a special .shtml or .jhtml extension to trigger server-side processing. Some servers also permit you to explicitly identify certain pages as servlet-containing pages.
Some servers also permit you to create "servlet chains". This is a particularly effective way of building dynamic output using KavaChart servlets. You can create a servlet definition like the one above with another servlet; perhaps a servlet that reads a database or reacts to user input. This servlet then passes the page back to the server for further processing. The server identifies an additional "servlet" tag and passes processing on to KavaChart servlets for image creation and retrieval. The final result is an IMG SRC tag that points to a graphical rendition of some data that was dynamically defined.
Yet another way to use KavaChart servlet output is to let the IMG SRC tag point to a parameterized URL like the one described above. By adding the parameter byteStream=true, KavaChart will return a stream of image bytes, rather than pointing to a particular file in a directory. Here's an example:
<IMG SRC=http://myserver/javachart.servlet.ChartServlet?dataset0yValues=123,432,123&writeDirectory=/usr/tmp&byteStream=true" >You can create servlet definitions like this dynamically from the browser using JavaScript, from the server using ASP, ColdFusion, php, or just about any other available mechanism. Note that you can even point to a separate machine, perhaps an imaging-specific server, to generate the output.
Finally, you can build one of our servlets from within your own servlet, and pass parameters to it from our javachart.servlet.Bean classes. These classes let you programmatically populate a parameter list, add KavaChart output to your own servlet's output stream, and take complete advantage of KavaChart's imaging and cache technologies as a "black box" within your own servlet. This is described in more detail below.
Parameter | value type | effect |
chartType | String | the kind of chart you want to generate. These are all the "App" files in the servlet package, and include all the chart types enumerated in the Individual Charts section of the documentation, such as "barApp", "lineApp", and so on. The default chart type is a horizontal bar chart. |
imageType | String | Output image type. This defaults to native JPEG generation. Other supported imageTypes include "gifmaker" (KavaChart's sample GIF generator), j_jpeg (a java jpeg generator), j_png (a java PNG generator, recommended), and j_bmp (a java .BMP file generator). |
properties | string | a server-side properties file containing default chart parameters. This is a great way to set up a customized look for your chart without constantly passing the same parameters to the servlet |
height | integer | pixel height of generated image |
width | integer | pixel width of generated image |
writeDirectory | String | KavaChart writes GIF images into this directory. By default this is set to "public_html/images". Since writeDirectory is "public_html/images" by default, the server will attempt to write images into $SERVER_ROOT/readDirectory/file_location. You can also specify an absolute path, such as /usr/lib/webserver/images for this directory. The write directory must be writable by the servlet engine, and readable by the web server. |
readDirectory | String | This is the cache directory browsers will use to retrieve images generated by KavaChart servlets. By default, readDirectory is "images", which means the servlet will return an "img src" tag that points to "images/filename.jpg". See also "writeDirectory". |
debug | String | If a KavaChart servlet finds the parameter "debug" it will log messages to the server log about files names, locations, sizes, etc. The value of this parameter is ignored. |
useCache | boolean | If "true", the servlets will attempt to use an image in the cache directory matching this servlet's parameters. If "false" the image will be generated each time. Note: if you are reading data by reference (e.g. dataset0yURL) and the data changes from time to time, you should either use this parameter to generate a fresh image, or clear the image cache whenever your data changes. Image caching is enabled by default. |
byteStream | boolean | By default, the servlets write an image to disk, and then send a <IMG SRC=...> message to the response OutputStream. If byteStream is set to "true", however, the servlet will instead set the content-type to the appropriate image type and send a stream of bytes. Note, however, that servlet parameters must still be set to generate a meaningful chart. By combining useCache=false and byteStream=true you can avoid using the server's disk entirely. |
fileName | String | An image file name for this servlet. By default, the servlets use a Secure Hash Algorithm (SHA) to create a checksum of all the parameters in a given servlet definition. This checksum is used to create a filename that uniquely identifies a chart defined by a given set of parameters. The servlet's CacheManager looks for the unique SHA filename in the image cache, and sends that image without regenerating it, if the image exists. Any change in the parameters, even the addition of an unused parameter, will create a new file name, and will cause the image to be regenerated. You can override the default image name with this parameter to force the image to be a specified name. |
dwellLabelsOn | true/false | Tells the servlet whether to create a client-side MAP for tool-tip dwell labels. |
dwellUseLabelString | true/false | Tells the servlet whether to use each datapoint's label as a part of the popup dwell labels. |
dwellUseXLabel | true/false | Tells the servlet whether to use each datapoint's X value as a part of the popup dwell labels. |
dwellUseYLabel | true/false | Tells the servlet whether to use each datapoint's Y value as a part of the popup dwell labels. |
dwellXString | String | A text string containing the characters "XX" to add descriptive text to the dwell label X value. Example: "Category XX" |
dwellYString | String | A text string containing the characters "XX" to add descriptive text to the dwell label Y value. Example: "Unit Sales: $XX" |
dwellLabelPrecision | Integer | Number of digits of precision for dwell label values. For example, if precision is "2", labels will look like this: 123.45 or 123,45. |
datasetNLinks | List | A list of comma-separated URLs that will be used in a client-side imagemap as hyperlinks for each Datum (bar, pie slice, line marker, etc.) in Dataset N. |
datasetNLinks | List | A list of comma-separated frame targets that will be used in conjunction with client-side imagemap hyperlinks for Dataset N |
The first step in creating any servlet is to extend the HttpServlet superclass, used by all servlets. An empty servlet looks something like this:
import javax.servlet.*; import javax.servlet.http.*; public class SimpleChartServlet extends HttpServlet { }To make this servlet do something useful, we want to override the "service" method to put some graphics data on the output stream. Here's what doGet looks like:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { }The request parameter includes all sorts of interesting information about this HTTP GET request, including a list of arguments, passed in as request parameters. For this simple case, we don't really care about the request information. We'll use the response parameter to send our output back to the browser.
First, we want to get our OutputStream from the response. Then we want to tell the browser that we're going to send some image bytes. This is done with a couple of lines like this:
ServletOutputStream out = response.getOutputStream(); response.setContentType("image/gif");Now that we're done with the preliminaries, we can get down to the actual work of building a chart. If this looks suspiciously familiar, there's a reason. These lines of code are pretty much the same as our simplest standalone program examples:
BarChart chart = new BarChart("Hello World"); double[] data = new double[5]; String[] labels = {"Larry", "Curly", "Moe", "Bob", "Dave" }; for(int i=0;i<;data.length;i++) data[i] = Math.random() * 500.; chart.addDataset("Stuff", data, labels); chart.resize(WIDTH, HEIGHT); chart.drawGraph(g);But where did we get a Graphics class to draw the chart? It's not a part of the servlet request or the servlet response. We need to create our own Image class, and retrieve the Graphics class from the image, with some code like this:
Frame frame = new Frame(); frame.addNotify(); Image image = frame.createImage(WIDTH, HEIGHT); Graphics g = image.getGraphics();You might wonder about the call to the "addNotify()" method. This method attaches native peer classes to our Frame, so the Frame can actually create a valid Image. After we have an Image, we just get its Graphics class, and draw our chart on it.
Now that we have an Image with a chart, we need to write it in some browser-friendly format, such as GIF or JPEG. Here's how we'd write it using the javachart.utility.GifMaker class:
GifMaker gifMaker = new GifMaker(out, image); gifMaker.write();If we were to do this with Sun's JPEG encoder, the code would look like this:
import java.awt.image.BufferedImage; import com.sun.image.codec.jpeg.*; JPEGImageEncoder jpg = JPEGCodec.createJPEGEncoder(out); jpg.encode((BufferedImage) image);So here's our complete servlet:
public class SimpleChartServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletOutputStream out = response.getOutputStream(); Frame frame = new Frame(); frame.addNotify(); Image image = frame.createImage(WIDTH, HEIGHT); Graphics g = image.getGraphics(); //create the chart BarChart chart = new BarChart("Hello World"); double[] data = new double[5]; String[] labels = {"Larry", "Curly", "Moe", "Bob", "Dave" }; for(int i=0;i<;data.length;i++) data[i] = Math.random() * 500.; chart.addDataset("Stuff", data, labels); chart.resize(WIDTH, HEIGHT); chart.drawGraph(g); response.setContentType("image/gif"); GifMaker gifMaker = new GifMaker(out, image); gifMaker.write(); } }Undoubtedly, you'll want to use real data sources and perhaps build a more creative chart than random numbers for Larry, Curly, and Moe, but the basic concepts are the same.
If you plan to build servlets for a high traffic environment, we strongly recommend the use of Java 2 with a BufferedImage class and Sun's JPEG encoder. This avoids the use of the PixelGrabber class to obtain image data, and employs Sun's highly optimized encoder for creating image output data. Our tests have shown that over 95% of the time spent in generating chart images is consumed by PixelGrabber. Here's the same simple servlet, rewritten to use Java 2 facilities:
public class SimpleChartServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletOutputStream out = response.getOutputStream(); BufferedImage myImage = new BufferedImage(500, 300, BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); //create the chart BarChart chart = new BarChart("Hello World"); double[] data = new double[5]; String[] labels = {"Larry", "Curly", "Moe", "Bob", "Dave" }; for(int i=0;i<;data.length;i++) data[i] = Math.random() * 500.; chart.addDataset("Stuff", data, labels); chart.resize(WIDTH, HEIGHT); chart.drawGraph(g); response.setContentType("image/jpeg"); JPEGImageEncoder jpg = JPEGCodec.createJPEGEncoder(out); jpg.encode(image); } }
To do this, you will create an instance of a KavaChart servlet bean, just as our own ChartServlet does. Then you'll populate the bean's property list, and get the results of the bean's image generation for use in your own output.
As an example, suppose your servlet starts out like this:
public class NotMagicServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { double[] myData = new double[5]; for(int i=0; i < myData.length; i++){ myData[i] = Math.random(); //random number generators are seldom magical... } response.setContentType("text/plain"); ServletOutputStream out = res.getOutputStream(); out.println("myData is unbearably dull..."); } }You can add a servlet to the mix and generate graphical output like this:
import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; public class MagicServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { double[] myData = new double[5]; for(int i=0; i < myData.length; i++){ myData[i] = Math.random(); //random number generators are seldom magical... } response.setContentType("text/html"); ServletOutputStream out = response.getOutputStream(); out.println("<P>graphical output is so much nicer!"); out.println("<table border=\"1\" cellpadding=\"5\" cellspacing=\"1\"><tr>"); out.println("<td>" + myData[0] + "<//td>"); //instantiate a javachart.servlet.Bean subclass, in this case columnApp: javachart.servlet.columnApp chart = new javachart.servlet.columnApp(); StringBuffer sb = new StringBuffer(); sb.append(Double.toString(myData[0])); for(int i=1; i < myData.length; i++){ sb.append("," + myData[i]); out.println("<td>" + myData[i] + "</td>"); } out.println("</tr></table>"); out.println("<center>"); chart.setProperty("dataset0yValues", sb.toString()); chart.setProperty("writeDirectory", "E:\\jswdk-1.0.1\\webpages"); chart.setProperty("width", "400"); chart.setProperty("height", "400"); chart.setProperty("titleString", "Magic Here..."); //generate our output here... try{ out.println("<img src=\"/" + chart.getFileName() + "\">"); //getting the file name triggers image generation }catch(Exception e){System.out.println("oops");} out.println("</center>"); } }
Here are a few other public Bean methods that will help you make the most of KavaChart in your servlets:
Method | Effect |
Constructor(java.util.Properties) | any bean subclass can be instantiated with a java.util.Properties to provide predefined charting properties. The properties available are all those listed for applets and servlets. |
public void generate() | Generates an image file or ByteArray based on the current property settings. |
public String getFileName() | Retrieves the user specified fileName or the default file name. If the image for this chart has not yet been generated, calls generate(). |
public byte[] getImageBytes() | Retrieves this image as an array of bytes. Generates the image if required. Retrieves the bytes from image cache if available. |
public String getParameter(String) | Retrieves a property, or parameter, if set. |
public Enumeration getParameterNames() | Retrieves the current list of available properties. |
public String getProperty(String) | see getParameter(String). |
public void setProperty(String) | Sets a specified property. |
public String getLinkMap() | Retrieves HTML statements for a client-side imagemap that contains tool-tip information for each Datum (based on the dwellLabel properties above) and a set of hyperlinks (based on the datasetNLinks and datasetNTargets properties above). This string contains the geometries for each data item on the chart (pie slice, bar, line vertex, etc.) and may be quite large for complex charts. |
public ChartInterface getChart() | Retrieves the internal javachart.chart.Chart object from the charting bean for direct customization in java code. |