source: josm/trunk/src/org/openstreetmap/josm/plugins/ReadRemotePluginInformationTask.java @ 5241

Revision 4721, 14.2 KB checked in by bastiK, 5 months ago (diff)

use builder pattern for ImageProvider (see #7192)

  • Property svn:eol-style set to native
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.plugins;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.BufferedReader;
7import java.io.ByteArrayInputStream;
8import java.io.File;
9import java.io.FileOutputStream;
10import java.io.FilenameFilter;
11import java.io.IOException;
12import java.io.InputStream;
13import java.io.InputStreamReader;
14import java.io.OutputStream;
15import java.io.OutputStreamWriter;
16import java.io.PrintWriter;
17import java.io.UnsupportedEncodingException;
18import java.net.HttpURLConnection;
19import java.net.MalformedURLException;
20import java.net.URL;
21import java.util.ArrayList;
22import java.util.Arrays;
23import java.util.Collection;
24import java.util.Collections;
25import java.util.HashSet;
26import java.util.LinkedList;
27import java.util.List;
28
29import org.openstreetmap.josm.Main;
30import org.openstreetmap.josm.data.Version;
31import org.openstreetmap.josm.gui.PleaseWaitRunnable;
32import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
33import org.openstreetmap.josm.gui.progress.ProgressMonitor;
34import org.openstreetmap.josm.io.OsmTransferException;
35import org.openstreetmap.josm.tools.ImageProvider;
36import org.openstreetmap.josm.tools.Utils;
37import org.xml.sax.SAXException;
38
39/**
40 * An asynchronous task for downloading plugin lists from the configured plugin download
41 * sites.
42 *
43 */
44public class ReadRemotePluginInformationTask extends PleaseWaitRunnable{
45
46    private Collection<String> sites;
47    private boolean canceled;
48    private HttpURLConnection connection;
49    private List<PluginInformation> availablePlugins;
50
51    protected enum CacheType {PLUGIN_LIST, ICON_LIST}
52
53    protected void init(Collection<String> sites){
54        this.sites = sites;
55        if (sites == null) {
56            this.sites = Collections.emptySet();
57        }
58        availablePlugins = new LinkedList<PluginInformation>();
59
60    }
61    /**
62     * Creates the task
63     *
64     * @param sites the collection of download sites. Defaults to the empty collection if null.
65     */
66    public ReadRemotePluginInformationTask(Collection<String> sites) {
67        super(tr("Download plugin list..."), false /* don't ignore exceptions */);
68        init(sites);
69    }
70
71    /**
72     * Creates the task
73     *
74     * @param monitor the progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null
75     * @param sites the collection of download sites. Defaults to the empty collection if null.
76     */
77    public ReadRemotePluginInformationTask(ProgressMonitor monitor, Collection<String> sites) {
78        super(tr("Download plugin list..."), monitor == null ? NullProgressMonitor.INSTANCE: monitor, false /* don't ignore exceptions */);
79        init(sites);
80    }
81
82
83    @Override
84    protected void cancel() {
85        canceled = true;
86        synchronized(this) {
87            if (connection != null) {
88                connection.disconnect();
89            }
90        }
91    }
92
93    @Override
94    protected void finish() {}
95
96    /**
97     * Creates the file name for the cached plugin list and the icon cache
98     * file.
99     *
100     * @param site the name of the site
101     * @param type icon cache or plugin list cache
102     * @return the file name for the cache file
103     */
104    protected File createSiteCacheFile(File pluginDir, String site, CacheType type) {
105        String name;
106        try {
107            site = site.replaceAll("%<(.*)>", "");
108            URL url = new URL(site);
109            StringBuilder sb = new StringBuilder();
110            sb.append("site-");
111            sb.append(url.getHost()).append("-");
112            if (url.getPort() != -1) {
113                sb.append(url.getPort()).append("-");
114            }
115            String path = url.getPath();
116            for (int i =0;i<path.length(); i++) {
117                char c = path.charAt(i);
118                if (Character.isLetterOrDigit(c)) {
119                    sb.append(c);
120                } else {
121                    sb.append("_");
122                }
123            }
124            switch (type) {
125            case PLUGIN_LIST:
126                sb.append(".txt");
127                break;
128            case ICON_LIST:
129                sb.append("-icons.zip");
130                break;
131            }
132            name = sb.toString();
133        } catch(MalformedURLException e) {
134            name = "site-unknown.txt";
135        }
136        return new File(pluginDir, name);
137    }
138
139    /**
140     * Downloads the list from a remote location
141     *
142     * @param site the site URL
143     * @param monitor a progress monitor
144     * @return the downloaded list
145     */
146    protected String downloadPluginList(String site, ProgressMonitor monitor) {
147        BufferedReader in = null;
148        StringBuilder sb = new StringBuilder();
149        try {
150            /* replace %<x> with empty string or x=plugins (separated with comma) */
151            String pl = Utils.join(",", Main.pref.getCollection("plugins"));
152            String printsite = site.replaceAll("%<(.*)>", "");
153            if(pl != null && pl.length() != 0) {
154                site = site.replaceAll("%<(.*)>", "$1"+pl);
155            } else {
156                site = printsite;
157            }
158
159            monitor.beginTask("");
160            monitor.indeterminateSubTask(tr("Downloading plugin list from ''{0}''", printsite));
161
162            URL url = new URL(site);
163            synchronized(this) {
164                connection = (HttpURLConnection)url.openConnection();
165                connection.setRequestProperty("Cache-Control", "no-cache");
166                connection.setRequestProperty("User-Agent",Version.getInstance().getAgentString());
167                connection.setRequestProperty("Host", url.getHost());
168                connection.setRequestProperty("Accept-Charset", "utf-8");
169            }
170            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
171            String line;
172            while((line = in.readLine()) != null) {
173                sb.append(line).append("\n");
174            }
175            return sb.toString();
176        } catch(MalformedURLException e) {
177            if (canceled) return null;
178            e.printStackTrace();
179            return null;
180        } catch(IOException e) {
181            if (canceled) return null;
182            e.printStackTrace();
183            return null;
184        } finally {
185            synchronized(this) {
186                if (connection != null) {
187                    connection.disconnect();
188                }
189                connection = null;
190            }
191            Utils.close(in);
192            monitor.finishTask();
193        }
194    }
195
196    /**
197     * Downloads the icon archive from a remote location
198     *
199     * @param site the site URL
200     * @param monitor a progress monitor
201     */
202    protected void downloadPluginIcons(String site, File destFile, ProgressMonitor monitor) {
203        InputStream in = null;
204        OutputStream out = null;
205        try {
206            site = site.replaceAll("%<(.*)>", "");
207
208            monitor.beginTask("");
209            monitor.indeterminateSubTask(tr("Downloading plugin list from ''{0}''", site));
210
211            URL url = new URL(site);
212            synchronized(this) {
213                connection = (HttpURLConnection)url.openConnection();
214                connection.setRequestProperty("Cache-Control", "no-cache");
215                connection.setRequestProperty("User-Agent",Version.getInstance().getAgentString());
216                connection.setRequestProperty("Host", url.getHost());
217            }
218            in = connection.getInputStream();
219            out = new FileOutputStream(destFile);
220            byte[] buffer = new byte[8192];
221            for (int read = in.read(buffer); read != -1; read = in.read(buffer)) {
222                out.write(buffer, 0, read);
223            }
224            out.close();
225            in.close();
226        } catch(MalformedURLException e) {
227            if (canceled) return;
228            e.printStackTrace();
229            return;
230        } catch(IOException e) {
231            if (canceled) return;
232            e.printStackTrace();
233            return;
234        } finally {
235            synchronized(this) {
236                if (connection != null) {
237                    connection.disconnect();
238                }
239                connection = null;
240            }
241            Utils.close(in);
242            monitor.finishTask();
243        }
244        for (PluginInformation pi : availablePlugins) {
245            if (pi.icon == null && pi.iconPath != null) {
246                pi.icon = new ImageProvider(pi.name+".jar/"+pi.iconPath)
247                                .setArchive(destFile)
248                                .setMaxWidth(24)
249                                .setMaxHeight(24)
250                                .setOptional(true).get();
251            }
252        }
253    }
254
255    /**
256     * Writes the list of plugins to a cache file
257     *
258     * @param site the site from where the list was downloaded
259     * @param list the downloaded list
260     */
261    protected void cachePluginList(String site, String list) {
262        PrintWriter writer = null;
263        try {
264            File pluginDir = Main.pref.getPluginsDirectory();
265            if (!pluginDir.exists()) {
266                if (! pluginDir.mkdirs()) {
267                    System.err.println(tr("Warning: failed to create plugin directory ''{0}''. Cannot cache plugin list from plugin site ''{1}''.", pluginDir.toString(), site));
268                }
269            }
270            File cacheFile = createSiteCacheFile(pluginDir, site, CacheType.PLUGIN_LIST);
271            getProgressMonitor().subTask(tr("Writing plugin list to local cache ''{0}''", cacheFile.toString()));
272            writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(cacheFile), "utf-8"));
273            writer.write(list);
274        } catch(IOException e) {
275            // just failed to write the cache file. No big deal, but log the exception anyway
276            e.printStackTrace();
277        } finally {
278            if (writer != null) {
279                writer.flush();
280                writer.close();
281            }
282        }
283    }
284
285    /**
286     * Filter information about deprecated plugins from the list of downloaded
287     * plugins
288     *
289     * @param plugins the plugin informations
290     * @return the plugin informations, without deprecated plugins
291     */
292    protected List<PluginInformation> filterDeprecatedPlugins(List<PluginInformation> plugins) {
293        List<PluginInformation> ret = new ArrayList<PluginInformation>(plugins.size());
294        HashSet<String> deprecatedPluginNames = new HashSet<String>();
295        for (PluginHandler.DeprecatedPlugin p : PluginHandler.DEPRECATED_PLUGINS) {
296            deprecatedPluginNames.add(p.name);
297        }
298        for (PluginInformation plugin: plugins) {
299            if (deprecatedPluginNames.contains(plugin.name)) {
300                continue;
301            }
302            ret.add(plugin);
303        }
304        return ret;
305    }
306
307    /**
308     * Parses the plugin list
309     *
310     * @param site the site from where the list was downloaded
311     * @param doc the document with the plugin list
312     */
313    protected void parsePluginListDocument(String site, String doc) {
314        try {
315            getProgressMonitor().subTask(tr("Parsing plugin list from site ''{0}''", site));
316            InputStream in = new ByteArrayInputStream(doc.getBytes("UTF-8"));
317            List<PluginInformation> pis = new PluginListParser().parse(in);
318            availablePlugins.addAll(filterDeprecatedPlugins(pis));
319        } catch(UnsupportedEncodingException e) {
320            System.err.println(tr("Failed to parse plugin list document from site ''{0}''. Skipping site. Exception was: {1}", site, e.toString()));
321            e.printStackTrace();
322        } catch(PluginListParseException e) {
323            System.err.println(tr("Failed to parse plugin list document from site ''{0}''. Skipping site. Exception was: {1}", site, e.toString()));
324            e.printStackTrace();
325        }
326    }
327
328    @Override
329    protected void realRun() throws SAXException, IOException, OsmTransferException {
330        if (sites == null) return;
331        getProgressMonitor().setTicksCount(sites.size() * 3);
332        File pluginDir = Main.pref.getPluginsDirectory();
333
334        // collect old cache files and remove if no longer in use
335        List<File> siteCacheFiles = new LinkedList<File>();
336        for (String location : PluginInformation.getPluginLocations()) {
337            File [] f = new File(location).listFiles(
338                    new FilenameFilter() {
339                        public boolean accept(File dir, String name) {
340                            return name.matches("^([0-9]+-)?site.*\\.txt$") ||
341                            name.matches("^([0-9]+-)?site.*-icons\\.zip$");
342                        }
343                    }
344            );
345            if(f != null && f.length > 0) {
346                siteCacheFiles.addAll(Arrays.asList(f));
347            }
348        }
349
350        for (String site: sites) {
351            String printsite = site.replaceAll("%<(.*)>", "");
352            getProgressMonitor().subTask(tr("Processing plugin list from site ''{0}''", printsite));
353            String list = downloadPluginList(site, getProgressMonitor().createSubTaskMonitor(0, false));
354            if (canceled) return;
355            siteCacheFiles.remove(createSiteCacheFile(pluginDir, site, CacheType.PLUGIN_LIST));
356            siteCacheFiles.remove(createSiteCacheFile(pluginDir, site, CacheType.ICON_LIST));
357            if(list != null)
358            {
359                getProgressMonitor().worked(1);
360                cachePluginList(site, list);
361                if (canceled) return;
362                getProgressMonitor().worked(1);
363                parsePluginListDocument(site, list);
364                if (canceled) return;
365                getProgressMonitor().worked(1);
366                if (canceled) return;
367            }
368            downloadPluginIcons(site+"-icons.zip", createSiteCacheFile(pluginDir, site, CacheType.ICON_LIST), getProgressMonitor().createSubTaskMonitor(0, false));
369        }
370        for (File file: siteCacheFiles) /* remove old stuff or whole update process is broken */
371        {
372            file.delete();
373        }
374    }
375
376    /**
377     * Replies true if the task was canceled
378     * @return
379     */
380    public boolean isCanceled() {
381        return canceled;
382    }
383
384    /**
385     * Replies the list of plugins described in the downloaded plugin lists
386     *
387     * @return  the list of plugins
388     */
389    public List<PluginInformation> getAvailabePlugins() {
390        return availablePlugins;
391    }
392}
Note: See TracBrowser for help on using the repository browser.