source: josm/trunk/src/org/openstreetmap/josm/plugins/ReadLocalPluginInformationTask.java@ 13790

Last change on this file since 13790 was 13647, checked in by Don-vip, 6 years ago

see #16204 - Allow to start and close JOSM in WebStart sandbox mode (where every external access is denied). This was very useful to reproduce some very tricky bugs that occured in real life but were almost impossible to diagnose.

  • Property svn:eol-style set to native
File size: 8.6 KB
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.File;
7import java.io.FilenameFilter;
8import java.io.IOException;
9import java.io.InputStream;
10import java.nio.file.Files;
11import java.nio.file.InvalidPathException;
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.HashMap;
15import java.util.List;
16import java.util.Map;
17
18import org.openstreetmap.josm.gui.PleaseWaitRunnable;
19import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
20import org.openstreetmap.josm.gui.progress.ProgressMonitor;
21import org.openstreetmap.josm.io.OsmTransferException;
22import org.openstreetmap.josm.tools.Logging;
23import org.xml.sax.SAXException;
24
25/**
26 * This is an asynchronous task for reading plugin information from the files
27 * in the local plugin repositories.
28 *
29 * It scans the files in the local plugins repository (see {@link org.openstreetmap.josm.data.Preferences#getPluginsDirectory()}
30 * and extracts plugin information from three kind of files:
31 * <ul>
32 * <li>.jar files, assuming that they represent plugin jars</li>
33 * <li>.jar.new files, assuming that these are downloaded but not yet installed plugins</li>
34 * <li>cached lists of available plugins, downloaded for instance from
35 * <a href="https://josm.openstreetmap.de/pluginicons">https://josm.openstreetmap.de/pluginicons</a></li>
36 * </ul>
37 *
38 */
39public class ReadLocalPluginInformationTask extends PleaseWaitRunnable {
40 private final Map<String, PluginInformation> availablePlugins;
41 private boolean canceled;
42
43 /**
44 * Constructs a new {@code ReadLocalPluginInformationTask}.
45 */
46 public ReadLocalPluginInformationTask() {
47 super(tr("Reading local plugin information.."), false);
48 availablePlugins = new HashMap<>();
49 }
50
51 /**
52 * Constructs a new {@code ReadLocalPluginInformationTask}.
53 * @param monitor progress monitor
54 */
55 public ReadLocalPluginInformationTask(ProgressMonitor monitor) {
56 super(tr("Reading local plugin information.."), monitor, false);
57 availablePlugins = new HashMap<>();
58 }
59
60 @Override
61 protected void cancel() {
62 canceled = true;
63 }
64
65 @Override
66 protected void finish() {
67 // Do nothing
68 }
69
70 protected void processJarFile(File f, String pluginName) throws PluginException {
71 PluginInformation info = new PluginInformation(
72 f,
73 pluginName
74 );
75 if (!availablePlugins.containsKey(info.getName())) {
76 info.updateLocalInfo(info);
77 availablePlugins.put(info.getName(), info);
78 } else {
79 PluginInformation current = availablePlugins.get(info.getName());
80 current.updateFromJar(info);
81 }
82 }
83
84 private static File[] listFiles(File pluginsDirectory, final String regex) {
85 return pluginsDirectory.listFiles((FilenameFilter) (dir, name) -> name.matches(regex));
86 }
87
88 protected void scanSiteCacheFiles(ProgressMonitor monitor, File pluginsDirectory) {
89 File[] siteCacheFiles = listFiles(pluginsDirectory, "^([0-9]+-)?site.*\\.txt$");
90 if (siteCacheFiles == null || siteCacheFiles.length == 0)
91 return;
92 monitor.subTask(tr("Processing plugin site cache files..."));
93 monitor.setTicksCount(siteCacheFiles.length);
94 for (File f: siteCacheFiles) {
95 String fname = f.getName();
96 monitor.setCustomText(tr("Processing file ''{0}''", fname));
97 try {
98 processLocalPluginInformationFile(f);
99 } catch (PluginListParseException e) {
100 Logging.warn(tr("Failed to scan file ''{0}'' for plugin information. Skipping.", fname));
101 Logging.error(e);
102 }
103 monitor.worked(1);
104 }
105 }
106
107 protected void scanPluginFiles(ProgressMonitor monitor, File pluginsDirectory) {
108 File[] pluginFiles = pluginsDirectory.listFiles(
109 (FilenameFilter) (dir, name) -> name.endsWith(".jar") || name.endsWith(".jar.new")
110 );
111 if (pluginFiles == null || pluginFiles.length == 0)
112 return;
113 monitor.subTask(tr("Processing plugin files..."));
114 monitor.setTicksCount(pluginFiles.length);
115 for (File f: pluginFiles) {
116 String fname = f.getName();
117 monitor.setCustomText(tr("Processing file ''{0}''", fname));
118 try {
119 if (fname.endsWith(".jar")) {
120 String pluginName = fname.substring(0, fname.length() - 4);
121 processJarFile(f, pluginName);
122 } else if (fname.endsWith(".jar.new")) {
123 String pluginName = fname.substring(0, fname.length() - 8);
124 processJarFile(f, pluginName);
125 }
126 } catch (PluginException e) {
127 Logging.log(Logging.LEVEL_WARN, "PluginException: ", e);
128 Logging.warn(tr("Failed to scan file ''{0}'' for plugin information. Skipping.", fname));
129 }
130 monitor.worked(1);
131 }
132 }
133
134 protected void scanLocalPluginRepository(ProgressMonitor progressMonitor, File pluginsDirectory) {
135 if (pluginsDirectory == null)
136 return;
137 ProgressMonitor monitor = progressMonitor != null ? progressMonitor : NullProgressMonitor.INSTANCE;
138 try {
139 monitor.beginTask("");
140 try {
141 scanSiteCacheFiles(monitor, pluginsDirectory);
142 } catch (SecurityException e) {
143 Logging.log(Logging.LEVEL_ERROR, "Unable to scan site cache files", e);
144 }
145 try {
146 scanPluginFiles(monitor, pluginsDirectory);
147 } catch (SecurityException e) {
148 Logging.log(Logging.LEVEL_ERROR, "Unable to scan plugin files", e);
149 }
150 } finally {
151 monitor.setCustomText("");
152 monitor.finishTask();
153 }
154 }
155
156 protected void processLocalPluginInformationFile(File file) throws PluginListParseException {
157 try (InputStream fin = Files.newInputStream(file.toPath())) {
158 List<PluginInformation> pis = new PluginListParser().parse(fin);
159 for (PluginInformation pi : pis) {
160 // we always keep plugin information from a plugin site because it
161 // includes information not available in the plugin jars Manifest, i.e.
162 // the download link or localized descriptions
163 //
164 availablePlugins.put(pi.name, pi);
165 }
166 } catch (IOException | InvalidPathException e) {
167 throw new PluginListParseException(e);
168 }
169 }
170
171 protected void analyseInProcessPlugins() {
172 for (PluginProxy proxy : PluginHandler.pluginList) {
173 PluginInformation info = proxy.getPluginInformation();
174 if (canceled) return;
175 if (!availablePlugins.containsKey(info.name)) {
176 availablePlugins.put(info.name, info);
177 } else {
178 availablePlugins.get(info.name).localversion = info.localversion;
179 }
180 }
181 }
182
183 protected void filterOldPlugins() {
184 for (PluginHandler.DeprecatedPlugin p : PluginHandler.DEPRECATED_PLUGINS) {
185 if (canceled) return;
186 if (availablePlugins.containsKey(p.name)) {
187 availablePlugins.remove(p.name);
188 }
189 }
190 }
191
192 @Override
193 protected void realRun() throws SAXException, IOException, OsmTransferException {
194 Collection<String> pluginLocations = PluginInformation.getPluginLocations();
195 getProgressMonitor().setTicksCount(pluginLocations.size() + 2);
196 if (canceled) return;
197 for (String location : pluginLocations) {
198 scanLocalPluginRepository(
199 getProgressMonitor().createSubTaskMonitor(1, false),
200 new File(location)
201 );
202 getProgressMonitor().worked(1);
203 if (canceled) return;
204 }
205 analyseInProcessPlugins();
206 getProgressMonitor().worked(1);
207 if (canceled) return;
208 filterOldPlugins();
209 getProgressMonitor().worked(1);
210 }
211
212 /**
213 * Replies information about available plugins detected by this task.
214 *
215 * @return information about available plugins detected by this task.
216 */
217 public List<PluginInformation> getAvailablePlugins() {
218 return new ArrayList<>(availablePlugins.values());
219 }
220
221 /**
222 * Replies true if the task was canceled by the user
223 *
224 * @return true if the task was canceled by the user
225 */
226 public boolean isCanceled() {
227 return canceled;
228 }
229}
Note: See TracBrowser for help on using the repository browser.