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

Last change on this file since 17534 was 17404, checked in by GerdP, 3 years ago

fix #19006: Incorrect plugins path used
revert r3130, only scan directory returned by Preferences.main().getPluginsDirectory()

  • Property svn:eol-style set to native
File size: 8.5 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.HashMap;
14import java.util.List;
15import java.util.Map;
16
17import org.openstreetmap.josm.data.Preferences;
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 availablePlugins.remove(p.name);
187 }
188 }
189
190 protected void filterIrrelevantPlugins() {
191 availablePlugins.entrySet().removeIf(e -> !e.getValue().isForCurrentPlatform());
192 }
193
194 @Override
195 protected void realRun() throws SAXException, IOException, OsmTransferException {
196 getProgressMonitor().setTicksCount(3);
197 if (canceled) return;
198 scanLocalPluginRepository(
199 getProgressMonitor().createSubTaskMonitor(1, false),
200 Preferences.main().getPluginsDirectory()
201 );
202 getProgressMonitor().worked(1);
203 if (canceled) return;
204 analyseInProcessPlugins();
205 getProgressMonitor().worked(1);
206 if (canceled) return;
207 filterOldPlugins();
208 filterIrrelevantPlugins();
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.