1 /*
   2  * Copyright (c) 2004, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.net;
  27 
  28 import java.util.ArrayList;
  29 import java.util.Iterator;
  30 import java.net.URL;
  31 
  32 /**
  33  * ProgressMonitor is a class for monitoring progress in network input stream.
  34  *
  35  * @author Stanley Man-Kit Ho
  36  */
  37 public class ProgressMonitor
  38 {
  39     /**
  40      * Return default ProgressMonitor.
  41      */
  42     public static synchronized ProgressMonitor getDefault() {
  43         return pm;
  44     }
  45 
  46     /**
  47      * Change default ProgressMonitor implementation.
  48      */
  49     public static synchronized void setDefault(ProgressMonitor m)   {
  50         if (m != null)
  51             pm = m;
  52     }
  53 
  54     /**
  55      * Change progress metering policy.
  56      */
  57     public static synchronized void setMeteringPolicy(ProgressMeteringPolicy policy)    {
  58         if (policy != null)
  59             meteringPolicy = policy;
  60     }
  61 
  62 
  63     /**
  64      * Return a snapshot of the ProgressSource list
  65      */
  66     public ArrayList<ProgressSource> getProgressSources()    {
  67         ArrayList<ProgressSource> snapshot = new ArrayList<>();
  68 
  69         try {
  70             synchronized(progressSourceList)    {
  71                 for (Iterator<ProgressSource> iter = progressSourceList.iterator(); iter.hasNext();)    {
  72                     ProgressSource pi = iter.next();
  73 
  74                     // Clone ProgressSource and add to snapshot
  75                     snapshot.add((ProgressSource)pi.clone());
  76                 }
  77             }
  78         }
  79         catch(CloneNotSupportedException e) {
  80             e.printStackTrace();
  81         }
  82 
  83         return snapshot;
  84     }
  85 
  86     /**
  87      * Return update notification threshold
  88      */
  89     public synchronized int getProgressUpdateThreshold()    {
  90         return meteringPolicy.getProgressUpdateThreshold();
  91     }
  92 
  93     /**
  94      * Return true if metering should be turned on
  95      * for a particular URL input stream.
  96      */
  97     public boolean shouldMeterInput(URL url, String method) {
  98         return meteringPolicy.shouldMeterInput(url, method);
  99     }
 100 
 101     /**
 102      * Register progress source when progress is began.
 103      */
 104     public void registerSource(ProgressSource pi) {
 105 
 106         synchronized(progressSourceList)    {
 107             if (progressSourceList.contains(pi))
 108                 return;
 109 
 110             progressSourceList.add(pi);
 111         }
 112 
 113         // Notify only if there is at least one listener
 114         if (progressListenerList.size() > 0)
 115         {
 116             // Notify progress listener if there is progress change
 117             ArrayList<ProgressListener> listeners = new ArrayList<>();
 118 
 119             // Copy progress listeners to another list to avoid holding locks
 120             synchronized(progressListenerList) {
 121                 for (Iterator<ProgressListener> iter = progressListenerList.iterator(); iter.hasNext();) {
 122                     listeners.add(iter.next());
 123                 }
 124             }
 125 
 126             // Fire event on each progress listener
 127             for (Iterator<ProgressListener> iter = listeners.iterator(); iter.hasNext();) {
 128                 ProgressListener pl = iter.next();
 129                 ProgressEvent pe = new ProgressEvent(pi, pi.getURL(), pi.getMethod(), pi.getContentType(), pi.getState(), pi.getProgress(), pi.getExpected());
 130                 pl.progressStart(pe);
 131             }
 132         }
 133     }
 134 
 135     /**
 136      * Unregister progress source when progress is finished.
 137      */
 138     public void unregisterSource(ProgressSource pi) {
 139 
 140         synchronized(progressSourceList) {
 141             // Return if ProgressEvent does not exist
 142             if (progressSourceList.contains(pi) == false)
 143                 return;
 144 
 145             // Close entry and remove from map
 146             pi.close();
 147             progressSourceList.remove(pi);
 148         }
 149 
 150         // Notify only if there is at least one listener
 151         if (progressListenerList.size() > 0)
 152         {
 153             // Notify progress listener if there is progress change
 154             ArrayList<ProgressListener> listeners = new ArrayList<>();
 155 
 156             // Copy progress listeners to another list to avoid holding locks
 157             synchronized(progressListenerList) {
 158                 for (Iterator<ProgressListener> iter = progressListenerList.iterator(); iter.hasNext();) {
 159                     listeners.add(iter.next());
 160                 }
 161             }
 162 
 163             // Fire event on each progress listener
 164             for (Iterator<ProgressListener> iter = listeners.iterator(); iter.hasNext();) {
 165                 ProgressListener pl = iter.next();
 166                 ProgressEvent pe = new ProgressEvent(pi, pi.getURL(), pi.getMethod(), pi.getContentType(), pi.getState(), pi.getProgress(), pi.getExpected());
 167                 pl.progressFinish(pe);
 168             }
 169         }
 170     }
 171 
 172     /**
 173      * Progress source is updated.
 174      */
 175     public void updateProgress(ProgressSource pi)   {
 176 
 177         synchronized (progressSourceList)   {
 178             if (progressSourceList.contains(pi) == false)
 179                 return;
 180         }
 181 
 182         // Notify only if there is at least one listener
 183         if (progressListenerList.size() > 0)
 184         {
 185             // Notify progress listener if there is progress change
 186             ArrayList<ProgressListener> listeners = new ArrayList<>();
 187 
 188             // Copy progress listeners to another list to avoid holding locks
 189             synchronized(progressListenerList)  {
 190                 for (Iterator<ProgressListener> iter = progressListenerList.iterator(); iter.hasNext();) {
 191                     listeners.add(iter.next());
 192                 }
 193             }
 194 
 195             // Fire event on each progress listener
 196             for (Iterator<ProgressListener> iter = listeners.iterator(); iter.hasNext();) {
 197                 ProgressListener pl = iter.next();
 198                 ProgressEvent pe = new ProgressEvent(pi, pi.getURL(), pi.getMethod(), pi.getContentType(), pi.getState(), pi.getProgress(), pi.getExpected());
 199                 pl.progressUpdate(pe);
 200             }
 201         }
 202     }
 203 
 204     /**
 205      * Add progress listener in progress monitor.
 206      */
 207     public void addProgressListener(ProgressListener l) {
 208         synchronized(progressListenerList) {
 209             progressListenerList.add(l);
 210         }
 211     }
 212 
 213     /**
 214      * Remove progress listener from progress monitor.
 215      */
 216     public void removeProgressListener(ProgressListener l) {
 217         synchronized(progressListenerList) {
 218             progressListenerList.remove(l);
 219         }
 220     }
 221 
 222     // Metering policy
 223     private static ProgressMeteringPolicy meteringPolicy = new DefaultProgressMeteringPolicy();
 224 
 225     // Default implementation
 226     private static ProgressMonitor pm = new ProgressMonitor();
 227 
 228     // ArrayList for outstanding progress sources
 229     private ArrayList<ProgressSource> progressSourceList = new ArrayList<ProgressSource>();
 230 
 231     // ArrayList for progress listeners
 232     private ArrayList<ProgressListener> progressListenerList = new ArrayList<ProgressListener>();
 233 }
 234 
 235 
 236 /**
 237  * Default progress metering policy.
 238  */
 239 class DefaultProgressMeteringPolicy implements ProgressMeteringPolicy  {
 240     /**
 241      * Return true if metering should be turned on for a particular network input stream.
 242      */
 243     public boolean shouldMeterInput(URL url, String method)
 244     {
 245         // By default, no URL input stream is metered for
 246         // performance reason.
 247         return false;
 248     }
 249 
 250     /**
 251      * Return update notification threshold.
 252      */
 253     public int getProgressUpdateThreshold() {
 254         // 8K - same as default I/O buffer size
 255         return 8192;
 256     }
 257 }