1 /*
   2  * Copyright (c) 2017, 2018, 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 /*
  25  * @test
  26  * @bug 8188083
  27  * @summary The test checks whether applying image filters using
  28  *          FilteredImageSource results in a NullPointerException.
  29  * @run main FilteredImageSourceTest
  30  */
  31 import java.awt.Graphics;
  32 import java.awt.Image;
  33 import java.awt.image.ColorModel;
  34 import java.awt.image.FilteredImageSource;
  35 import java.awt.image.ImageConsumer;
  36 import java.awt.image.ImageFilter;
  37 import java.awt.image.ImageObserver;
  38 import java.awt.image.ImageProducer;
  39 import java.util.Hashtable;
  40 
  41 /*
  42  * An empty image consumer that will be added to the list of consumers
  43  * interested in image data for the filtered image.
  44  */
  45 class EmptyImageConsumer implements ImageConsumer {
  46     @Override
  47     public void setDimensions(int width, int height) {
  48     }
  49 
  50     @Override
  51     public void setProperties(Hashtable<?, ?> props) {
  52     }
  53 
  54     @Override
  55     public void setColorModel(ColorModel colorModel) {
  56     }
  57 
  58     @Override
  59     public void setHints(int hintFlags) {
  60     }
  61 
  62     @Override
  63     public void setPixels(int x, int y, int width, int height,
  64                           ColorModel colorModel, byte[] pixels,
  65                           int offset, int scanSize) {
  66     }
  67 
  68     @Override
  69     public void setPixels(int x, int y, int width, int height,
  70                           ColorModel colorModel, int[] pixels,
  71                           int offset, int scanSize) {
  72     }
  73 
  74     @Override
  75     public void imageComplete(int i) {
  76     }
  77 }
  78 
  79 /*
  80  * An empty image producer whose sole purpose is to provide stub methods
  81  * that will be invoked while preparing filtered image.
  82  */
  83 class EmptyImageProducer implements ImageProducer {
  84     @Override
  85     public void addConsumer(ImageConsumer imageConsumer) {
  86     }
  87 
  88     @Override
  89     public boolean isConsumer(ImageConsumer imageConsumer) {
  90         return false;
  91     }
  92 
  93     @Override
  94     public void removeConsumer(ImageConsumer imageConsumer) {
  95     }
  96 
  97     @Override
  98     public void startProduction(ImageConsumer imageConsumer) {
  99     }
 100 
 101     @Override
 102     public void requestTopDownLeftRightResend(ImageConsumer imageConsumer) {
 103     }
 104 }
 105 
 106 /*
 107  * Typically, an Image object will contain an ImageProducer that prepares
 108  * image data. FilteredImageSource will be set as image producer for images
 109  * that require image filter applied to image data.
 110  */
 111 class EmptyFilteredImage extends Image {
 112     ImageFilter filter = null;
 113     ImageProducer producer = null;
 114 
 115     public EmptyFilteredImage(ImageProducer imgSource) {
 116         filter = new ImageFilter();
 117         producer = new FilteredImageSource(imgSource, filter);
 118     }
 119 
 120     @Override
 121     public int getWidth(ImageObserver observer) {
 122         return 100;
 123     }
 124 
 125     @Override
 126     public int getHeight(ImageObserver observer) {
 127         return 100;
 128     }
 129 
 130     @Override
 131     public ImageProducer getSource() {
 132         return producer;
 133     }
 134 
 135     @Override
 136     public Graphics getGraphics() {
 137         throw new UnsupportedOperationException();
 138     }
 139 
 140     @Override
 141     public Object getProperty(String name, ImageObserver observer) {
 142         return null;
 143     }
 144 }
 145 
 146 public final class FilteredImageSourceTest {
 147     // Minimum test duration in ms
 148     private static final int TEST_MIN_DURATION = 5000;
 149 
 150     /*
 151      * A throwable object that will hold any exception generated while
 152      * executing methods on FilteredImageSource. The test passes if the
 153      * methods execute without any exception
 154      */
 155     private static volatile Throwable fail = null;
 156 
 157     public static void main(final String[] args)
 158             throws InterruptedException {
 159         final ImageConsumer ic = new EmptyImageConsumer();
 160         final ImageProducer ip = new EmptyImageProducer();
 161         final Image image = new EmptyFilteredImage(ip);
 162 
 163         /*
 164          * Simulate the framework's operations on FilteredImageSource by
 165          * invoking the concerned methods in multiple threads and observe
 166          * whether exceptions are generated.
 167          */
 168         Thread t1 = new Thread(() -> {
 169             try {
 170                 while (true) {
 171                     image.getSource().addConsumer(ic);
 172                 }
 173             } catch (Throwable t) {
 174                 fail = t;
 175             }
 176         });
 177         t1.setDaemon(true);
 178 
 179         Thread t2 = new Thread(() -> {
 180             try {
 181                 while (true) {
 182                     image.getSource().removeConsumer(ic);
 183                 }
 184             } catch (Throwable t) {
 185                 fail = t;
 186             }
 187         });
 188         t2.setDaemon(true);
 189 
 190         Thread t3 = new Thread(() -> {
 191             try {
 192                 while (true) {
 193                     image.getSource().startProduction(ic);
 194                 }
 195             } catch (Throwable t) {
 196                 fail = t;
 197             }
 198         });
 199         t3.setDaemon(true);
 200 
 201         // Start the threads
 202         t1.start();
 203         t2.start();
 204         t3.start();
 205 
 206         // Wait on one of the threads for a specific duration.
 207         t1.join(TEST_MIN_DURATION);
 208         if (fail != null) {
 209             throw new RuntimeException("Test failed with exception: ", fail);
 210         }
 211     }
 212 }