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         int numFailures = 0;
147         while (iter.hasNext()) {
148             final WriteAfterAbort writeAfterAbort = new WriteAfterAbort();
149             final ImageWriter writer = iter.next().createWriterInstance();
150             System.out.println("ImageWriter = " + writer);
151             try {
152                 writeAfterAbort.test(writer);
153             } catch (Exception e) {
154                 System.err.println("Test failed for \""
155                     + writer.getOriginatingProvider().getFormatNames()[0]
156                     + "\" format.");
157                 numFailures++;
158             }
159         }
160         if (numFailures == 0) {
161             System.out.println("Test passed.");
162         } else {
163             throw new RuntimeException("Test failed.");
164         }
165     }
166 
167     // Callbacks
168 
169     @Override
imageComplete(ImageWriter source)170     public void imageComplete(ImageWriter source) {
171         isCompleteCalled = true;
172     }
173 
174     @Override
imageProgress(ImageWriter source, float percentageDone)175     public void imageProgress(ImageWriter source, float percentageDone) {
176         isProgressCalled = true;
177         if (percentageDone > 50 && abortFlag) {
178             source.abort();
179         }
180     }
181 
182     @Override
imageStarted(ImageWriter source, int imageIndex)183     public void imageStarted(ImageWriter source, int imageIndex) {
184         isStartedCalled = true;
185     }
186 
187     @Override
writeAborted(final ImageWriter source)188     public void writeAborted(final ImageWriter source) {
189         isAbortCalled = true;
190     }
191 
192     @Override
thumbnailComplete(ImageWriter source)193     public void thumbnailComplete(ImageWriter source) {
194     }
195 
196     @Override
thumbnailProgress(ImageWriter source, float percentageDone)197     public void thumbnailProgress(ImageWriter source, float percentageDone) {
198     }
199 
200     @Override
thumbnailStarted(ImageWriter source, int imageIndex, int thumbnailIndex)201     public void thumbnailStarted(ImageWriter source, int imageIndex,
202                                  int thumbnailIndex) {
203     }
204 
205     /**
206      * We need to skip writes on abort, because content of the file after abort
207      * is undefined.
208      */
209     private class SkipWriteOnAbortOutputStream extends FileOutputStream {
210 
SkipWriteOnAbortOutputStream(File file)211         SkipWriteOnAbortOutputStream(File file) throws FileNotFoundException {
212             super(file);
213         }
214 
215         @Override
write(int b)216         public void write(int b) throws IOException {
217             if (!abortFlag) {
218                 super.write(b);
219             }
220         }
221 
222         @Override
write(byte[] b)223         public void write(byte[] b) throws IOException {
224             if (!abortFlag) {
225                 super.write(b);
226             }
227         }
228 
229         @Override
write(byte[] b, int off, int len)230         public void write(byte[] b, int off, int len) throws IOException {
231             if (!abortFlag) {
232                 super.write(b, off, len);
233             }
234         }
235     }
236 }
237 
238