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
26package sun.net;
27
28import java.util.ArrayList;
29import java.util.Iterator;
30import 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 */
37public 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 */
239class 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}
258