Hello, recently, we found a bug in Suns native sun.awt.image.codec.JPEGImageEncoderImpl.writeJPEGStream(), causing the VM to crash when streams where images are being encoded to are somehow disconnected (e.g., user pressed stop button, connection reset by peer, ...) before the encoding has ended. Sun has confirmed this to be a new bug under Bug Id 4502892, which can be seen under http://developer.java.sun.com/developer/bugParade/bugs/4502892.html (you may have to register with Suns Java Developer Connection, but this is free). As far as we were able to test it, Suns JDK 1.3.1, JDK 1.4 beta and IBMs JDK 1.3.0 are affected under both Windows 2000 and Linux 2.2.
Attached please find the Test.java, allowing you to test whether your plattform is affected by this bug, and a slightly modified org.apache.batik.transcoder.image.JPEGEncoder.java which works around the issue by buffering the encoded image and thereby isolating the faulty native method from problematic stream exceptions. To test whether your plattform is affected, have a look at Test.java and test your plattform with the attached Java class by typing "javac Test.java", followed by "java Test". When this application is being run, it silently loops printing dots every 500 ms, having one thread encode a JPEG image and another one read only some of it and then closing the connection. We rarely saw more than 5-6 dots being printed without the VM crashing. An unpatched Batik running on an affected JVM cannot reliably being used in production systems for server-side image generation; we were running a Win2K server with Tomcat 4 and Cocoon2 using Batik for dynamic image generation, and it took us more than 2 days to isolate this and write the workaround. If you are registered with Suns Java Developer Connection and have a spare Bug Vote slot, please be so kind to vote for this bug. Best regards, Michael Hartle, Hartle & Klug GbR
/***************************************************************************** * Copyright (C) The Apache Software Foundation. All rights reserved. * * ------------------------------------------------------------------------- * * This software is published under the terms of the Apache Software License * * version 1.1, a copy of which has been included with this distribution in * * the LICENSE file. * *****************************************************************************/ package org.apache.batik.transcoder.image; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGEncodeParam; import com.sun.image.codec.jpeg.JPEGImageEncoder; import java.awt.Color; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.OutputStream; import java.io.ByteArrayOutputStream; import org.apache.batik.transcoder.TranscoderException; import org.apache.batik.transcoder.TranscoderOutput; import org.apache.batik.transcoder.TranscodingHints; import org.apache.batik.transcoder.image.resources.Messages; /** * This class is an <tt>ImageTranscoder</tt> that produces a JPEG image. * * @author <a href="mailto:[EMAIL PROTECTED]">Thierry Kormann</a> * @version $Id: JPEGTranscoder.java,v 1.4 2001/03/28 06:51:24 tkormann Exp $ */ public class JPEGTranscoder extends ImageTranscoder { /** * Constructs a new transcoder that produces jpeg images. */ public JPEGTranscoder() { hints.put(ImageTranscoder.KEY_BACKGROUND_COLOR, Color.white); } /** * Creates a new ARGB image with the specified dimension. * @param width the image width in pixels * @param height the image height in pixels */ public BufferedImage createImage(int width, int height) { return new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); } /** * Writes the specified image to the specified output. * @param img the image to write * @param output the output where to store the image * @param TranscoderException if an error occured while storing the image */ public void writeImage(BufferedImage img, TranscoderOutput output) throws TranscoderException { OutputStream ostream = output.getOutputStream(); ByteArrayOutputStream bstream = new ByteArrayOutputStream(); if (ostream == null) { throw new TranscoderException( Messages.formatMessage("jpeg.badoutput", null)); } float quality; if (hints.containsKey(KEY_QUALITY)) { quality = ((Float)hints.get(KEY_QUALITY)).floatValue(); } else { handler.error(new TranscoderException( Messages.formatMessage("jpeg.unspecifiedQuality", null))); quality = 1f; } try { JPEGImageEncoder jpegEncoder = JPEGCodec.createJPEGEncoder(bstream); JPEGEncodeParam params = JPEGCodec.getDefaultJPEGEncodeParam(img); params.setQuality(quality, true); jpegEncoder.encode(img, params); ostream.write(bstream.toByteArray()); } catch (IOException ex) { throw new TranscoderException(ex); } } // -------------------------------------------------------------------- // Keys definition // -------------------------------------------------------------------- /** * The encoder quality factor key. * <TABLE BORDER="0" CELLSPACING="0" CELLPADDING="1"> * <TR> * <TH VALIGN="TOP" ALIGN="RIGHT"><P ALIGN="RIGHT">Key: </TH> * <TD VALIGN="TOP">KEY_QUALITY</TD></TR> * <TR> * <TH VALIGN="TOP" ALIGN="RIGHT"><P ALIGN="RIGHT">Value: </TH> * <TD VALIGN="TOP">Float (between 0 and 1)</TD></TR> * <TR> * <TH VALIGN="TOP" ALIGN="RIGHT"><P ALIGN="RIGHT">Default: </TH> * <TD VALIGN="TOP">1 (no lossy)</TD></TR> * <TR> * <TH VALIGN="TOP" ALIGN="RIGHT"><P ALIGN="RIGHT">Required: </TH> * <TD VALIGN="TOP">Recommended</TD></TR> * <TR> * <TH VALIGN="TOP" ALIGN="RIGHT"><P ALIGN="RIGHT">Description: </TH> * <TD VALIGN="TOP">Specify the JPEG image encoding quality.</TD></TR> * </TABLE> */ public static final TranscodingHints.Key KEY_QUALITY = new QualityKey(); /** * A transcoding Key represented the JPEG image quality. */ private static class QualityKey extends TranscodingHints.Key { public boolean isCompatibleValue(Object v) { if (v instanceof Float) { float q = ((Float)v).floatValue(); return (q > 0 && q <= 1f); } else { return false; } } } }
import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGEncodeParam; import com.sun.image.codec.jpeg.JPEGImageEncoder; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.OutputStream; class WriterThread implements Runnable { Thread thread; PipedOutputStream out; public WriterThread(PipedOutputStream postream) { out = postream; } public void start() { if (thread == null) { thread = new Thread(this, "WriterThread"); thread.start(); } } public void run() { try { // Do the stuff and write it. BufferedImage image = new BufferedImage(512, 512, BufferedImage.TYPE_INT_RGB); JPEGImageEncoder jpegEncoder = JPEGCodec.createJPEGEncoder(out); JPEGEncodeParam params = JPEGCodec.getDefaultJPEGEncodeParam(image); params.setQuality(1f, true); jpegEncoder.encode(image, params); } catch (IOException e) { // e.printStackTrace(); } } } class ReaderThread implements Runnable { Thread thread; PipedInputStream in; public ReaderThread(PipedInputStream pistream) { in = pistream; } public void start() { if (thread == null) { thread = new Thread(this, "ReaderThread"); thread.start(); } } public void run() { byte[] buffer = new byte[128]; try { // Read 1 byte and close the connection. // Watch the VM go *bang* in.read(); in.close(); } catch (IOException e) { // e.printStackTrace(); } } } public class Test { public static void main(String[] args) { while(true) { try { PipedInputStream pistream = new PipedInputStream(); PipedOutputStream postream = new PipedOutputStream(pistream); WriterThread writer = new WriterThread(postream); ReaderThread reader = new ReaderThread(pistream); writer.start(); reader.start(); } catch (IOException e) { // e.printStackTrace(); } System.out.print("."); try { Thread.currentThread().sleep(500); } catch (Exception e) { } } } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]