NIFI-6686 - Improved exception handling in ResizeImage processor.

This closes #3922.

Signed-off-by: Mark Payne <markap14@hotmail.com>
This commit is contained in:
Matthew Knight 2019-09-19 09:54:26 -04:00 committed by Mark Payne
parent 452ca98c29
commit 29e7adb2b4
4 changed files with 107 additions and 49 deletions

View File

@ -51,7 +51,6 @@ import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessSession;
import org.apache.nifi.processor.Relationship;
import org.apache.nifi.processor.exception.ProcessException;
import org.apache.nifi.processor.io.StreamCallback;
import org.apache.nifi.processor.util.StandardValidators;
import org.apache.nifi.util.StopWatch;
@ -150,57 +149,62 @@ public class ResizeImage extends AbstractProcessor {
}
final StopWatch stopWatch = new StopWatch(true);
try {
flowFile = session.write(flowFile, new StreamCallback() {
@Override
public void process(final InputStream rawIn, final OutputStream out) throws IOException {
try (final BufferedInputStream in = new BufferedInputStream(rawIn)) {
final ImageInputStream iis = ImageIO.createImageInputStream(in);
if (iis == null) {
throw new ProcessException("FlowFile is not in a valid format");
}
final Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
if (!readers.hasNext()) {
throw new ProcessException("FlowFile is not in a valid format");
}
final BufferedImage image;
final String formatName;
final ImageReader reader = readers.next();
final String formatName = reader.getFormatName();
reader.setInput(iis, true);
final BufferedImage image = reader.read(0);
final Image scaledImage = image.getScaledInstance(width, height, hints);
final BufferedImage scaledBufferedImg;
if (scaledImage instanceof BufferedImage) {
scaledBufferedImg = (BufferedImage) scaledImage;
} else {
// Determine image type, since calling image.getType may return 0
int imageType = BufferedImage.TYPE_INT_ARGB;
if(image.getTransparency() == Transparency.OPAQUE) {
imageType = BufferedImage.TYPE_INT_RGB;
}
scaledBufferedImg = new BufferedImage(scaledImage.getWidth(null), scaledImage.getHeight(null), imageType);
final Graphics2D graphics = scaledBufferedImg.createGraphics();
try {
graphics.drawImage(scaledImage, 0, 0, null);
} finally {
graphics.dispose();
}
}
ImageIO.write(scaledBufferedImg, formatName, out);
}
try (final InputStream rawIn = session.read(flowFile)) {
try (final BufferedInputStream in = new BufferedInputStream(rawIn)) {
final ImageInputStream iis = ImageIO.createImageInputStream(in);
if (iis == null) {
throw new ProcessException("FlowFile is not in a valid format");
}
});
session.getProvenanceReporter().modifyContent(flowFile, stopWatch.getElapsed(TimeUnit.MILLISECONDS));
session.transfer(flowFile, REL_SUCCESS);
} catch (final ProcessException pe) {
getLogger().error("Failed to resize {} due to {}", new Object[] { flowFile, pe });
final Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
if (!readers.hasNext()) {
throw new ProcessException("FlowFile is not in a valid format");
}
final ImageReader reader = readers.next();
formatName = reader.getFormatName();
reader.setInput(iis, true);
image = reader.read(0);
}
} catch (final IOException | IllegalArgumentException | ProcessException ex) {
getLogger().error("Failed to read {} due to {}", new Object[] { flowFile, ex });
session.transfer(flowFile, REL_FAILURE);
return;
}
}
try (final OutputStream out = session.write(flowFile)) {
final Image scaledImage = image.getScaledInstance(width, height, hints);
final BufferedImage scaledBufferedImg;
if (scaledImage instanceof BufferedImage) {
scaledBufferedImg = (BufferedImage) scaledImage;
} else {
// Determine image type, since calling image.getType may return 0
int imageType = BufferedImage.TYPE_INT_ARGB;
if(image.getTransparency() == Transparency.OPAQUE) {
imageType = BufferedImage.TYPE_INT_RGB;
}
scaledBufferedImg = new BufferedImage(scaledImage.getWidth(null), scaledImage.getHeight(null), imageType);
final Graphics2D graphics = scaledBufferedImg.createGraphics();
try {
graphics.drawImage(scaledImage, 0, 0, null);
} finally {
graphics.dispose();
}
}
ImageIO.write(scaledBufferedImg, formatName, out);
} catch (final IOException | NegativeArraySizeException ex) {
getLogger().error("Failed to write {} due to {}", new Object[] { flowFile, ex });
session.transfer(flowFile, REL_FAILURE);
return;
}
session.getProvenanceReporter().modifyContent(flowFile, stopWatch.getElapsed(TimeUnit.MILLISECONDS));
session.transfer(flowFile, REL_SUCCESS);
}
}

View File

@ -30,6 +30,7 @@ import java.io.IOException;
import java.nio.file.Paths;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class TestResizeImage {
@ -71,6 +72,60 @@ public class TestResizeImage {
ImageIO.write(img, "JPG", out);
}
@Test
public void testReadImageFailure() throws IOException {
final TestRunner runner = TestRunners.newTestRunner(new ResizeImage());
runner.setProperty(ResizeImage.IMAGE_HEIGHT, "64");
runner.setProperty(ResizeImage.IMAGE_WIDTH, "64");
runner.setProperty(ResizeImage.SCALING_ALGORITHM, ResizeImage.RESIZE_FAST);
runner.enqueue(Paths.get("src/test/resources/peppers.jpg"));
runner.run();
// Should return REL_FAILURE and log an IllegalArgumentException
runner.assertAllFlowFilesTransferred(ResizeImage.REL_FAILURE, 1);
assertEquals(1, runner.getLogger().getErrorMessages().size());
assertEquals(4, runner.getLogger().getErrorMessages().get(0).getArgs().length);
assertTrue(runner.getLogger().getErrorMessages().get(0).getArgs()[3].toString()
.startsWith("java.lang.IllegalArgumentException"));
}
@Test
public void testNonImageInput() throws IOException {
final TestRunner runner = TestRunners.newTestRunner(new ResizeImage());
runner.setProperty(ResizeImage.IMAGE_HEIGHT, "64");
runner.setProperty(ResizeImage.IMAGE_WIDTH, "64");
runner.setProperty(ResizeImage.SCALING_ALGORITHM, ResizeImage.RESIZE_FAST);
runner.enqueue(Paths.get("src/test/resources/notImage.txt"));
runner.run();
// Should return REL_FAILURE and log a ProcessException
runner.assertAllFlowFilesTransferred(ResizeImage.REL_FAILURE, 1);
assertEquals(1, runner.getLogger().getErrorMessages().size());
assertEquals(4, runner.getLogger().getErrorMessages().get(0).getArgs().length);
assertTrue(runner.getLogger().getErrorMessages().get(0).getArgs()[3].toString()
.startsWith("org.apache.nifi.processor.exception.ProcessException"));
}
@Test
public void testWriteFailure() throws IOException {
final TestRunner runner = TestRunners.newTestRunner(new ResizeImage());
runner.setProperty(ResizeImage.IMAGE_HEIGHT, "1000000");
runner.setProperty(ResizeImage.IMAGE_WIDTH, "1000000");
runner.setProperty(ResizeImage.SCALING_ALGORITHM, ResizeImage.RESIZE_SMOOTH);
runner.enqueue(Paths.get("src/test/resources/photoshop-8x12-32colors-alpha.gif"));
runner.run();
// Should return REL_FAILURE and log a NegativeArraySizeException
runner.assertAllFlowFilesTransferred(ResizeImage.REL_FAILURE, 1);
assertEquals(1, runner.getLogger().getErrorMessages().size());
assertEquals(4, runner.getLogger().getErrorMessages().get(0).getArgs().length);
assertTrue(runner.getLogger().getErrorMessages().get(0).getArgs()[3].toString()
.startsWith("java.lang.NegativeArraySizeException"));
}
@Test
public void testEnlarge() throws IOException {
final TestRunner runner = TestRunners.newTestRunner(new ResizeImage());
@ -92,7 +147,6 @@ public class TestResizeImage {
ImageIO.write(img, "PNG", out);
}
@Test
public void testResizePNG() throws IOException {
final TestRunner runner = TestRunners.newTestRunner(new ResizeImage());

View File

@ -1 +1 @@
This file is not an image and is used for testing the image metadata extractor.
This file is not an image and is used for testing the image metadata extractor and resizer.

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 KiB