Going From Component to Buffered Image in headless environment.

S

Sandman

Hello all,

I've been working on this problem for about 2 days now. Here it is:
I have a servlet programmed up that will operate in a headless environment.
I'm using j2sdk1.4.2 and apache tomcat 4.1.24. I have x-windows installed
on my linux server but it does not run all the time (only when i really need
it). What i want my servlet to do is to create a JButton (or any swing
Component--heavy or lightweight) and convert it to a Buffered Image and then
output that image as a image/png. Here is the code that I have currently
that works fine when I have x-windows running and isHeadless=false:


package graphics;


import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

import java.awt.*;
import java.awt.image.*;
import javax.imageio.*;

import javax.swing.*;


public class SwingGeneratorServlet extends HttpServlet
{

// this is the converter method. it throws the exception
public static BufferedImage createImage(Component cmp) {
Dimension d = cmp.getSize();
d.setSize(100, 100);

BufferedImage bi = new BufferedImage((int) d.getWidth(),
(int) d.getHeight(),
BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bi.createGraphics();

//temporary container
JFrame jf = new JFrame();
jf.getContentPane().add(cmp);
jf.pack();

SwingUtilities.paintComponent(g2d, cmp, jf.getContentPane(),
0, 0, (int) d.getWidth(), (int)
d.getHeight());
return bi;
}



// this is the servlet's io method. it generates, converts, then
outputs the image
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
JButton bu = new JButton("Swing Servlet");
BufferedImage bi = createImage(bu);

response.setContentType("image/png");
ServletOutputStream out = response.getOutputStream();
ImageIO.write(bi, "png", out);
out.close();
}



// this just directs to doGet(...); nothing interesting here
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
doGet(request, response);
}

}



Now, when isHeadless=true (regardless of whether I am running x-windows or
not), an exception gets thrown:

HTTP Status 500 -

----------------------------------------------------------------------------
----

type Exception report

message

description The server encountered an internal error () that prevented it
from fulfilling this request.

exception

java.awt.HeadlessException
at java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:121)
at java.awt.Window.(Window.java:274)
at java.awt.Frame.(Frame.java:401)
at java.awt.Frame.(Frame.java:366)
at javax.swing.JFrame.(JFrame.java:154)
at
graphics.SwingGeneratorServlet.createImage(SwingGeneratorServlet.java:26)
at graphics.SwingGeneratorServlet.doGet(SwingGeneratorServlet.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:740)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(Application
FilterChain.java:247)
at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterCh
ain.java:193)
at
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.ja
va:256)
at
org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invok
eNext(StandardPipeline.java:643)
at
org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.ja
va:191)
at
org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invok
eNext(StandardPipeline.java:643)
at
org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at
org.apache.catalina.core.StandardContext.invoke(StandardContext.java:2415)
at
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:180
)
at
org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invok
eNext(StandardPipeline.java:643)
at
org.apache.catalina.valves.ErrorDispatcherValve.invoke(ErrorDispatcherValve.
java:171)
at
org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invok
eNext(StandardPipeline.java:641)
at
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:172
)
at
org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invok
eNext(StandardPipeline.java:641)
at
org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java
:174)
at
org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invok
eNext(StandardPipeline.java:643)
at
org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.coyote.tomcat4.CoyoteAdapter.service(CoyoteAdapter.java:223)
at
org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:594)
at
org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConne
ction(Http11Protocol.java:392)
at
org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:565)
at
org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.jav
a:619)
at java.lang.Thread.run(Thread.java:534)


----------------------------------------------------------------------------
----

Apache Tomcat/4.1.24

This is to be expected, since Sun's new additions to 1.4.1 specify that the
isHeadless() check will be done on all heavyweight Components that do not
have lightweight peers (the last part of that statement I only somewhat
understand). Now, I have looked into many suggestions (such as the PJA
Toolkit -- which doesn't support java 1.4.2), but none have worked. My
thinking is that if Sun is going to add a special case for Java programmers
who wish to generate images in a headless environment, then they should've
also made a way for Java programmers who wish to generate images from Swing
components (such as realtime charts and graphs) in a headless environment.
I know of no such way, though. And while I have given it the old college
try for the past two-three days, I have yielded no results. Is there
anybody that can connect the dots for me? Show me the way to bypass the
HeadlessException problem and generate a BufferedImage out of a Component?
In this case, code is the best explanation--I've already reviewed the java
tutorials, the sdk specs, the javadocs, and many websites that talk about it
in general. It's the specifics that I am having trouble with.

THANKS TO ALL WHO HELP IN ANY WAY.
--sandman
 
L

Lynn Robinson

You cannot use a heavyweight component such as a JFrame in a headless
environment. Use a lightweight component instead such as a JPanel.
You must explicitly call "addNotify()" on the component to get it to
render: e.g.

(I call this in the event dispatch thread via
SwingUtilities.invokeAndWait())

JPanel panel = new JPanel();
panel.addNotify();
panel.setOpaque(true);
panel.setDoubleBuffered(false); // for better performance
// add your subcomponents here via panel.add(whatever);

Later on, I call this, again in the event dispatch thread:

public static BufferedImage componentToBufferedImage(JComponent
component,
Dimension dim) throws IOException {

BufferedImage backBuffer = new BufferedImage (dim.width,
dim.height, BufferedImage.TYPE_INT_ARGB);
Graphics g = backBuffer.createGraphics();

// g.setColor(java.awt.Color.white); // if want a white bg
g.fillRect(0, 0, dim.width, dim.height);
g.setClip(0, 0, dim.width, dim.height);
component.validate(); // this might not be necessary
component.printAll(g); // this might not be necessary
return backBuffer;

} // end componentToBufferedImage method


Later, I call this method which uses the Acme GIF package (I call it
outside the event dispatch thread and I haven't had problems so far):

public static byte[] imageToGif(BufferedImage backBuffer)
throws IOException {

ByteArrayOutputStream byteStream = new ByteArrayOutputStream();

encoder.GifEncoder ge = new encoder.GifEncoder(backBuffer,
byteStream);
ge.encode();
byte [] gifBytes = byteStream.toByteArray();
byteStream.close();

// this assumes we are done using this BufferedImage
Graphics g = backBuffer.getGraphics();
if (g != null) g.dispose();

return (gifBytes);

} // end imageToGif method



This works for me to render my Swing components in my headless servlet
(which I translate into BufferedImages and then to GIFs to send to a
browser).

Hope this helps,
Lynn
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top