1 /*
2  * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 import java.awt.Color;
25 import java.awt.Graphics2D;
26 import java.awt.image.BufferedImage;
27 import java.io.File;
28 import java.io.FileNotFoundException;
29 import java.io.FileOutputStream;
30 import java.io.IOException;
31 import java.util.Iterator;
32 import java.nio.file.Files;
33 
34 import javax.imageio.ImageIO;
35 import javax.imageio.ImageWriter;
36 import javax.imageio.event.IIOWriteProgressListener;
37 import javax.imageio.spi.IIORegistry;
38 import javax.imageio.spi.ImageWriterSpi;
39 import javax.imageio.stream.ImageOutputStream;
40 
41 import static java.awt.image.BufferedImage.TYPE_BYTE_BINARY;
42 
43 /**
44  * @test
45  * @bug     4952954 8183349
46  * @summary abortFlag must be cleared for every ImageWriter.write operation
47  * @run     main WriteAfterAbort
48  */
49 public final class WriteAfterAbort implements IIOWriteProgressListener {
50 
51     private volatile boolean abortFlag = true;
52     private volatile boolean isAbortCalled;
53     private volatile boolean isCompleteCalled;
54     private volatile boolean isProgressCalled;
55     private volatile boolean isStartedCalled;
56     private static final int WIDTH = 100;
57     private static final int HEIGHT = 100;
58     private static FileOutputStream fos;
59     private static File file;
60 
test(final ImageWriter writer)61     private void test(final ImageWriter writer) throws IOException {
62         try {
63             // Image initialization
64             final BufferedImage imageWrite =
65                     new BufferedImage(WIDTH, HEIGHT, TYPE_BYTE_BINARY);
66             final Graphics2D g = imageWrite.createGraphics();
67             g.setColor(Color.WHITE);
68             g.fillRect(0, 0, WIDTH, HEIGHT);
69             g.dispose();
70 
71             // File initialization
72             file = File.createTempFile("temp", ".img");
73             fos = new SkipWriteOnAbortOutputStream(file);
74             final ImageOutputStream ios = ImageIO.createImageOutputStream(fos);
75             writer.setOutput(ios);
76             writer.addIIOWriteProgressListener(this);
77 
78             // This write will be aborted, and file will not be touched
79             writer.write(imageWrite);
80             if (!isStartedCalled) {
81                 throw new RuntimeException("Started should be called");
82             }
83             if (!isProgressCalled) {
84                 throw new RuntimeException("Progress should be called");
85             }
86             if (!isAbortCalled) {
87                 throw new RuntimeException("Abort should be called");
88             }
89             if (isCompleteCalled) {
90                 throw new RuntimeException("Complete should not be called");
91             }
92             // Flush aborted data
93             ios.flush();
94 
95             /*
96              * This write should be completed successfully and the file should
97              * contain correct image data.
98              */
99             abortFlag = false;
100             isAbortCalled = false;
101             isCompleteCalled = false;
102             isProgressCalled = false;
103             isStartedCalled = false;
104             writer.write(imageWrite);
105 
106             if (!isStartedCalled) {
107                 throw new RuntimeException("Started should be called");
108             }
109             if (!isProgressCalled) {
110                 throw new RuntimeException("Progress should be called");
111             }
112             if (isAbortCalled) {
113                 throw new RuntimeException("Abort should not be called");
114             }
115             if (!isCompleteCalled) {
116                 throw new RuntimeException("Complete should be called");
117             }
118             ios.close();
119 
120             // Validates content of the file.
121             final BufferedImage imageRead = ImageIO.read(file);
122             for (int x = 0; x < WIDTH; ++x) {
123                 for (int y = 0; y < HEIGHT; ++y) {
124                     if (imageRead.getRGB(x, y) != imageWrite.getRGB(x, y)) {
125                         throw new RuntimeException("Test failed.");
126                     }
127                 }
128             }
129         } finally {
130             writer.dispose();
131             if (file != null) {
132                 if (fos != null) {
133                     fos.close();
134                 }
135                 Files.delete(file.toPath());
136             }
137         }
138     }
139 
main(final String[] args)140     public static void main(final String[] args) throws IOException {
141         final IIORegistry registry = IIORegistry.getDefaultInstance();
142         final Iterator<ImageWriterSpi> iter = registry.getServiceProviders(
143                 ImageWriterSpi.class, provider -> true, true);
144 
145         // Validates all supported ImageWriters
146         while (iter.hasNext()) {
147             final WriteAfterAbort writeAfterAbort = new WriteAfterAbort();
148             final ImageWriter writer = iter.next().createWriterInstance();
149             System.out.println("ImageWriter = " + writer);
150             writeAfterAbort.test(writer);
151         }
152         System.out.println("Test passed");
153     }
154 
155     // Callbacks
156 
157     @Override
imageComplete(ImageWriter source)158     public void imageComplete(ImageWriter source) {
159         isCompleteCalled = true;
160     }
161 
162     @Override
imageProgress(ImageWriter source, float percentageDone)163     public void imageProgress(ImageWriter source, float percentageDone) {
164         isProgressCalled = true;
165         if (percentageDone > 50 && abortFlag) {
166             source.abort();
167         }
168     }
169 
170     @Override
imageStarted(ImageWriter source, int imageIndex)171     public void imageStarted(ImageWriter source, int imageIndex) {
172         isStartedCalled = true;
173     }
174 
175     @Override
writeAborted(final ImageWriter source)176     public void writeAborted(final ImageWriter source) {
177         isAbortCalled = true;
178     }
179 
180     @Override
thumbnailComplete(ImageWriter source)181     public void thumbnailComplete(ImageWriter source) {
182     }
183 
184     @Override
thumbnailProgress(ImageWriter source, float percentageDone)185     public void thumbnailProgress(ImageWriter source, float percentageDone) {
186     }
187 
188     @Override
thumbnailStarted(ImageWriter source, int imageIndex, int thumbnailIndex)189     public void thumbnailStarted(ImageWriter source, int imageIndex,
190                                  int thumbnailIndex) {
191     }
192 
193     /**
194      * We need to skip writes on abort, because content of the file after abort
195      * is undefined.
196      */
197     private class SkipWriteOnAbortOutputStream extends FileOutputStream {
198 
SkipWriteOnAbortOutputStream(File file)199         SkipWriteOnAbortOutputStream(File file) throws FileNotFoundException {
200             super(file);
201         }
202 
203         @Override
write(int b)204         public void write(int b) throws IOException {
205             if (!abortFlag) {
206                 super.write(b);
207             }
208         }
209 
210         @Override
write(byte[] b)211         public void write(byte[] b) throws IOException {
212             if (!abortFlag) {
213                 super.write(b);
214             }
215         }
216 
217         @Override
write(byte[] b, int off, int len)218         public void write(byte[] b, int off, int len) throws IOException {
219             if (!abortFlag) {
220                 super.write(b, off, len);
221             }
222         }
223     }
224 }
225 
226