Ticket #18277: 18277.5.patch
File 18277.5.patch, 10.6 KB (added by , 4 years ago) |
---|
-
src/org/openstreetmap/josm/gui/preferences/plugin/PluginPreference.java
55 55 import org.openstreetmap.josm.gui.util.GuiHelper; 56 56 import org.openstreetmap.josm.gui.widgets.FilterField; 57 57 import org.openstreetmap.josm.plugins.PluginDownloadTask; 58 import org.openstreetmap.josm.plugins.PluginHandler; 58 59 import org.openstreetmap.josm.plugins.PluginInformation; 59 60 import org.openstreetmap.josm.plugins.ReadLocalPluginInformationTask; 60 61 import org.openstreetmap.josm.plugins.ReadRemotePluginInformationTask; … … 324 325 List<String> l = new LinkedList<>(model.getSelectedPluginNames()); 325 326 Collections.sort(l); 326 327 Config.getPref().putList("plugins", l); 327 if (!model.getNewlyDeactivatedPlugins().isEmpty()) 328 return true; 328 List<PluginInformation> deactivatedPlugins = model.getNewlyDeactivatedPlugins(); 329 if (!deactivatedPlugins.isEmpty()) { 330 boolean requiresRestart = PluginHandler.removePlugins(deactivatedPlugins); 331 if (requiresRestart) 332 return requiresRestart; 333 } 329 334 for (PluginInformation pi : model.getNewlyActivatedPlugins()) { 330 335 if (!pi.canloadatruntime) 331 336 return true; -
src/org/openstreetmap/josm/plugins/PluginHandler.java
68 68 import org.openstreetmap.josm.io.OfflineAccessException; 69 69 import org.openstreetmap.josm.io.OnlineResource; 70 70 import org.openstreetmap.josm.spi.preferences.Config; 71 import org.openstreetmap.josm.tools.Destroyable; 71 72 import org.openstreetmap.josm.tools.GBC; 72 73 import org.openstreetmap.josm.tools.I18n; 73 74 import org.openstreetmap.josm.tools.ImageProvider; … … 1168 1169 pluginsToDownload, 1169 1170 tr("Update plugins") 1170 1171 ); 1171 1172 1172 try { 1173 1173 pluginDownloadTask.run(); 1174 1174 } catch (RuntimeException e) { // NOPMD … … 1332 1332 URL oldPluginURL = updatedPlugin.toURI().toURL(); 1333 1333 pluginsToLoad.stream().filter(x -> x.libraries.contains(oldPluginURL)).forEach( 1334 1334 x -> Collections.replaceAll(x.libraries, oldPluginURL, newPluginURL)); 1335 1336 // Attempt to update loaded plugin (must implement Destroyable) 1337 PluginInformation tInfo = pluginsToLoad.parallelStream() 1338 .filter(x -> x.libraries.contains(newPluginURL)).findAny().orElse(null); 1339 if (tInfo != null) { 1340 Object tUpdatedPlugin = getPlugin(tInfo.name); 1341 if (tUpdatedPlugin instanceof Destroyable) { 1342 ((Destroyable) tUpdatedPlugin).destroy(); 1343 PluginHandler.loadPlugins(getInfoPanel(), Collections.singleton(tInfo), 1344 NullProgressMonitor.INSTANCE); 1345 } 1346 } 1335 1347 } catch (MalformedURLException e) { 1336 1348 Logging.warn(e); 1337 1349 } … … 1643 1655 return cbDontShowAgain.isSelected(); 1644 1656 } 1645 1657 } 1658 1659 /** 1660 * Remove deactivated plugins, returning true if JOSM should restart 1661 * 1662 * @param deactivatedPlugins The plugins to deactivate 1663 * 1664 * @return true if there was a plugin that requires a restart 1665 */ 1666 public static boolean removePlugins(List<PluginInformation> deactivatedPlugins) { 1667 List<Destroyable> noRestart = deactivatedPlugins.parallelStream() 1668 .map(info -> PluginHandler.getPlugin(info.name)).filter(Destroyable.class::isInstance) 1669 .map(Destroyable.class::cast).collect(Collectors.toList()); 1670 boolean restartNeeded; 1671 try { 1672 noRestart.forEach(Destroyable::destroy); 1673 new ArrayList<>(pluginList).stream().filter(proxy -> noRestart.contains(proxy.getPlugin())) 1674 .forEach(pluginList::remove); 1675 restartNeeded = deactivatedPlugins.size() != noRestart.size(); 1676 } catch (Exception e) { 1677 Logging.error(e); 1678 restartNeeded = true; 1679 } 1680 return restartNeeded; 1681 } 1646 1682 } -
test/unit/org/openstreetmap/josm/plugins/PluginHandlerTestIT.java
7 7 8 8 import java.awt.GraphicsEnvironment; 9 9 import java.awt.HeadlessException; 10 import java.io.IOException; 11 import java.util.ArrayList; 10 12 import java.util.Arrays; 11 13 import java.util.Collection; 12 14 import java.util.HashMap; … … 13 15 import java.util.Iterator; 14 16 import java.util.List; 15 17 import java.util.Map; 18 import java.util.Map.Entry; 16 19 import java.util.Set; 17 20 import java.util.function.Consumer; 18 21 import java.util.stream.Collectors; 19 22 20 23 import org.apache.commons.lang3.exception.ExceptionUtils; 21 import org.junit.Rule; 24 import org.junit.BeforeClass; 25 import org.junit.ClassRule; 22 26 import org.junit.Test; 27 import org.openstreetmap.josm.TestUtils; 23 28 import org.openstreetmap.josm.data.Preferences; 24 29 import org.openstreetmap.josm.data.gpx.GpxData; 25 30 import org.openstreetmap.josm.data.osm.DataSet; … … 30 35 import org.openstreetmap.josm.gui.progress.NullProgressMonitor; 31 36 import org.openstreetmap.josm.spi.preferences.Config; 32 37 import org.openstreetmap.josm.testutils.JOSMTestRules; 38 import org.openstreetmap.josm.tools.Destroyable; 33 39 import org.openstreetmap.josm.tools.Utils; 34 40 35 41 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; … … 39 45 */ 40 46 public class PluginHandlerTestIT { 41 47 48 private static List<String> errorsToIgnore = new ArrayList<>(); 42 49 /** 43 50 * Setup test. 44 51 */ 45 @ Rule52 @ClassRule 46 53 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") 47 public JOSMTestRules test = new JOSMTestRules().main().projection().preferences().https().timeout(10*60*1000); 54 public static JOSMTestRules test = new JOSMTestRules().main().projection().preferences().https() 55 .timeout(10 * 60 * 1000); 48 56 49 57 /** 58 * Setup test 59 * 60 * @throws IOException in case of I/O error 61 */ 62 @BeforeClass 63 public static void beforeClass() throws IOException { 64 errorsToIgnore.addAll(TestUtils.getIgnoredErrorMessages(PluginHandlerTestIT.class)); 65 } 66 67 /** 50 68 * Test that available plugins rules can be loaded. 51 69 */ 52 70 @Test … … 74 92 testPlugin(MainApplication.getLayerManager()::removeLayer, layer, layerExceptions, loadedPlugins); 75 93 } 76 94 95 Map<String, Throwable> noRestartExceptions = new HashMap<>(); 96 testCompletelyRestartlessPlugins(loadedPlugins, noRestartExceptions); 97 77 98 debugPrint(invalidManifestEntries); 78 99 debugPrint(loadingExceptions); 79 100 debugPrint(layerExceptions); 101 debugPrint(noRestartExceptions); 102 103 invalidManifestEntries = filterKnownErrors(invalidManifestEntries); 104 loadingExceptions = filterKnownErrors(loadingExceptions); 105 layerExceptions = filterKnownErrors(layerExceptions); 106 noRestartExceptions = filterKnownErrors(noRestartExceptions); 107 80 108 String msg = Arrays.toString(invalidManifestEntries.entrySet().toArray()) + '\n' + 81 109 Arrays.toString(loadingExceptions.entrySet().toArray()) + '\n' + 82 Arrays.toString(layerExceptions.entrySet().toArray()); 110 Arrays.toString(layerExceptions.entrySet().toArray()) + '\n' 111 + Arrays.toString(noRestartExceptions.entrySet().toArray()); 83 112 assertTrue(msg, invalidManifestEntries.isEmpty() && loadingExceptions.isEmpty() && layerExceptions.isEmpty()); 84 113 } 85 114 115 private static void testCompletelyRestartlessPlugins(List<PluginInformation> loadedPlugins, 116 Map<String, Throwable> noRestartExceptions) { 117 try { 118 List<PluginInformation> restartable = loadedPlugins.parallelStream() 119 .filter(info -> PluginHandler.getPlugin(info.name) instanceof Destroyable) 120 .collect(Collectors.toList()); 121 // ensure good plugin behavior with regards to Destroyable (i.e., they can be 122 // removed and readded) 123 for (int i = 0; i < 2; i++) { 124 assertFalse(PluginHandler.removePlugins(restartable)); 125 assertTrue(restartable.stream().noneMatch(info -> PluginHandler.getPlugins().contains(info))); 126 loadPlugins(restartable); 127 } 128 129 assertTrue(PluginHandler.removePlugins(loadedPlugins)); 130 assertTrue(restartable.parallelStream().noneMatch(info -> PluginHandler.getPlugins().contains(info))); 131 } catch (Exception | LinkageError t) { 132 Throwable root = ExceptionUtils.getRootCause(t); 133 root.printStackTrace(); 134 noRestartExceptions.put(findFaultyPlugin(loadedPlugins, root), root); 135 } 136 } 137 138 private static <T> Map<String, T> filterKnownErrors(Map<String, T> errorMap) { 139 return errorMap.entrySet().parallelStream() 140 .filter(entry -> !errorsToIgnore.contains(convertEntryToString(entry))) 141 .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); 142 } 143 86 144 private static void debugPrint(Map<String, ?> invalidManifestEntries) { 87 145 System.out.println(invalidManifestEntries.entrySet() 88 146 .stream() 89 .map(e -> e.getKey() + "=\"" + e.getValue() + "\"")147 .map(e -> convertEntryToString(e)) 90 148 .collect(Collectors.joining(", "))); 91 149 } 92 150 151 private static String convertEntryToString(Entry<String, ?> entry) { 152 return entry.getKey() + "=\"" + entry.getValue() + "\""; 153 } 154 93 155 /** 94 156 * Downloads and loads all JOSM plugins. 95 157 */ … … 133 195 // Download plugins 134 196 downloadPlugins(plugins); 135 197 198 loadPlugins(plugins); 199 } 200 201 static void loadPlugins(List<PluginInformation> plugins) { 136 202 // Load early plugins 137 203 PluginHandler.loadEarlyPlugins(null, plugins, null); 138 204