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      */
getDefault()42     public static synchronized ProgressMonitor getDefault() {
43         return pm;
44     }
45 
46     /**
47      * Change default ProgressMonitor implementation.
48      */
setDefault(ProgressMonitor m)49     public static synchronized void setDefault(ProgressMonitor m)   {
50         if (m != null)
51             pm = m;
52     }
53 
54     /**
55      * Change progress metering policy.
56      */
setMeteringPolicy(ProgressMeteringPolicy policy)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      */
getProgressSources()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      */
getProgressUpdateThreshold()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      */
shouldMeterInput(URL url, String method)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      */
registerSource(ProgressSource pi)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      */
unregisterSource(ProgressSource pi)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      */
updateProgress(ProgressSource pi)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      */
addProgressListener(ProgressListener l)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      */
removeProgressListener(ProgressListener l)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      */
shouldMeterInput(URL url, String method)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      */
getProgressUpdateThreshold()253     public int getProgressUpdateThreshold() {
254         // 8K - same as default I/O buffer size
255         return 8192;
256     }
257 }
258