Ignore:
Timestamp:
2010-01-11T21:06:49+01:00 (16 years ago)
Author:
Gubaer
Message:

fixed #3063: Downloading a plugin yields 3 dialogs at the same time: Downloading plugin / You should restart JOSM / Plugin downloaded
fixed #3628: JOSM blocking itself updating broken plugin
fixed #4187: JOSM deleted random files from disk after start (data loss)
fixed #4199: new version - plugins update vs josm start [should be fixed. Be careful if you have two JOSM instances running. Auto-update of plugins in the second instance will fail because plugin files are locked by the first instance]
fixed #4034: JOSM should auto-download plugin list when it hasn't been downloaded before [JOSM now displays a hint]

fixed: splash screen showing again even if plugins are auto-updated
new: progress indication integrated in splash screen
new: cancelable, asynchronous download of plugins from preferences
new: cancelable, asynchronous download of plugin list from plugin download sites
new: asynchronous loading of plugin information, launch of preferences dialog accelerated
refactored: clean up, documentation of plugin management code (PluginHandler)

Location:
trunk/src/org/openstreetmap/josm/plugins
Files:
6 added
2 deleted
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/plugins/Plugin.java

    r1169 r2817  
    5353     */
    5454    public final String getPluginDir() {
    55         return new File(Main.pref.getPluginsDirFile(), info.name).getPath();
     55        return new File(Main.pref.getPluginsDirectory(), info.name).getPath();
    5656    }
    5757
     
    8181        String pluginDirName = Main.pref.getPreferencesDir()+"plugins/"+info.name+"/";
    8282        File pluginDir = new File(pluginDirName);
    83         if (!pluginDir.exists())
     83        if (!pluginDir.exists()) {
    8484            pluginDir.mkdirs();
     85        }
    8586        FileOutputStream out = new FileOutputStream(pluginDirName+to);
    8687        InputStream in = getClass().getResourceAsStream(from);
    8788        byte[] buffer = new byte[8192];
    88         for(int len = in.read(buffer); len > 0; len = in.read(buffer))
     89        for(int len = in.read(buffer); len > 0; len = in.read(buffer)) {
    8990            out.write(buffer, 0, len);
     91        }
    9092        in.close();
    9193        out.close();
  • trunk/src/org/openstreetmap/josm/plugins/PluginException.java

    r2512 r2817  
    1111 * @author Immanuel.Scholz
    1212 */
    13 public class PluginException extends RuntimeException {
     13public class PluginException extends Exception {
    1414    public final PluginProxy plugin;
    1515    public final String name;
     
    2020        this.name = name;
    2121    }
     22
     23    public PluginException(String name, String message) {
     24        super(message);
     25        this.plugin = null;
     26        this.name = name;
     27    }
     28
     29    public PluginException(String name, Throwable cause) {
     30        super(tr("An error occurred in plugin {0}", name), cause);
     31        this.plugin = null;
     32        this.name = name;
     33    }
    2234}
  • trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java

    r2584 r2817  
    44import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    55import static org.openstreetmap.josm.tools.I18n.tr;
     6import static org.openstreetmap.josm.tools.I18n.trn;
    67
    78import java.awt.Font;
    89import java.awt.GridBagLayout;
    910import java.awt.event.ActionEvent;
     11import java.io.File;
     12import java.io.FilenameFilter;
    1013import java.net.URL;
    1114import java.net.URLClassLoader;
     
    1417import java.util.Collection;
    1518import java.util.Collections;
     19import java.util.Comparator;
     20import java.util.HashMap;
     21import java.util.HashSet;
     22import java.util.Iterator;
    1623import java.util.LinkedList;
    1724import java.util.List;
    18 import java.util.SortedMap;
    19 import java.util.TreeMap;
     25import java.util.Map;
     26import java.util.Set;
    2027import java.util.Map.Entry;
     28import java.util.concurrent.ExecutionException;
     29import java.util.concurrent.ExecutorService;
     30import java.util.concurrent.Executors;
     31import java.util.concurrent.Future;
    2132
    2233import javax.swing.AbstractAction;
     
    3748import org.openstreetmap.josm.gui.download.DownloadSelection;
    3849import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
     50import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
     51import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     52import org.openstreetmap.josm.tools.CheckParameterUtil;
    3953import org.openstreetmap.josm.tools.GBC;
    4054import org.openstreetmap.josm.tools.ImageProvider;
    4155
     56/**
     57 * PluginHandler is basically a collection of static utility functions used to bootstrap
     58 * and manage the loaded plugins.
     59 *
     60 */
    4261public class PluginHandler {
    4362
    44     public static String [] oldplugins = new String[] {"mappaint", "unglueplugin",
    45                 "lang-de", "lang-en_GB", "lang-fr", "lang-it", "lang-pl", "lang-ro",
    46                 "lang-ru", "ewmsplugin", "ywms", "tways-0.2", "geotagged", "landsat",
    47                 "namefinder", "waypoints", "slippy_map_chooser", "tcx-support", "usertools",
    48                 "AgPifoJ", "utilsplugin"};
    49     public static String [] unmaintained = new String[] {"gpsbabelgui", "Intersect_way"};
     63    final public static String [] DEPRECATED_PLUGINS = new String[] {"mappaint", "unglueplugin",
     64        "lang-de", "lang-en_GB", "lang-fr", "lang-it", "lang-pl", "lang-ro",
     65        "lang-ru", "ewmsplugin", "ywms", "tways-0.2", "geotagged", "landsat",
     66        "namefinder", "waypoints", "slippy_map_chooser", "tcx-support", "usertools",
     67        "AgPifoJ", "utilsplugin"};
     68
     69    final public static String [] UNMAINTAINED_PLUGINS = new String[] {"gpsbabelgui", "Intersect_way"};
    5070
    5171    /**
     
    5373     */
    5474    public final static Collection<PluginProxy> pluginList = new LinkedList<PluginProxy>();
    55     /**
    56      * Load all plugins specified in preferences. If the parameter is
    57      * <code>true</code>, all early plugins are loaded (before constructor).
    58      */
    59     public static void loadPlugins(boolean early) {
    60         List<String> plugins = new LinkedList<String>();
    61         Collection<String> cp = Main.pref.getCollection("plugins", null);
    62         if (cp != null) {
    63             plugins.addAll(cp);
    64         }
    65         if (System.getProperty("josm.plugins") != null) {
    66             plugins.addAll(Arrays.asList(System.getProperty("josm.plugins").split(",")));
    67         }
    68 
    69         for (String p : oldplugins) {
     75
     76
     77    /**
     78     * Removes deprecated plugins from a collection of plugins. Modifies the
     79     * collection <code>plugins</code>.
     80     *
     81     * Also notifies the user about removed deprecated plugins
     82     *
     83     * @param plugins the collection of plugins
     84     */
     85    private static void filterDeprecatedPlugins(Collection<String> plugins) {
     86        Set<String> removedPlugins = new HashSet<String>();
     87        for (String p : DEPRECATED_PLUGINS) {
    7088            if (plugins.contains(p)) {
    7189                plugins.remove(p);
    7290                Main.pref.removeFromCollection("plugins", p);
    73                 JOptionPane.showMessageDialog(
    74                         Main.parent,
    75                         tr("Plugin {0} is no longer necessary and has been deactivated.", p),
    76                         tr("Warning"),
    77                         JOptionPane.WARNING_MESSAGE
    78                 );
    79             }
    80         }
    81         if(early)
    82         {
    83             for (String p : unmaintained) {
    84                 if (plugins.contains(p) && disablePlugin(tr("<html>Loading of {0} plugin was requested."
    85                         +"<br>This plugin is no longer developed and very likely will produce errors."
    86                         +"<br>It should be disabled.<br>Delete from preferences?</html>", p), p)) {
    87                     plugins.remove(p);
    88                 }
    89             }
    90         }
    91 
    92         if (plugins.isEmpty())
     91                removedPlugins.add(p);
     92            }
     93        }
     94        if (removedPlugins.isEmpty())
    9395            return;
    9496
    95         if(early)
    96         {
    97             String doUpdate = null;
    98             String check = null;
    99             int v = Version.getInstance().getVersion();
    100             if(Main.pref.getInteger("pluginmanager.version", 0) < v)
    101             {
    102                 doUpdate = tr("You updated your JOSM software.\nTo prevent problems the plugins should be updated as well.\n"
    103                         + "Update plugins now?");
    104                 check = "pluginmanger.version";
    105             }
    106             else
    107             {
    108                 long tim = System.currentTimeMillis();
    109                 long last = Main.pref.getLong("pluginmanager.lastupdate", 0);
    110                 Integer maxTime = Main.pref.getInteger("pluginmanager.warntime", 60);
    111                 long d = (tim - last)/(24*60*60*1000l);
    112                 if ((last <= 0) || (maxTime <= 0)) {
    113                     Main.pref.put("pluginmanager.lastupdate",Long.toString(tim));
    114                 } else if (d > maxTime) {
    115                     doUpdate = tr("Last plugin update more than {0} days ago.", d);
    116                     check = "pluginmanager.time";
    117                 }
    118             }
    119             if(doUpdate != null)
    120             {
    121                 ExtendedDialog dialog = new ExtendedDialog(
    122                         Main.parent,
    123                         tr("Update plugins"),
    124                         new String[] {tr("Update plugins"), tr("Skip update")}
    125                 );
    126                 dialog.setContent(doUpdate);
    127                 dialog.toggleEnable(check);
    128                 dialog.setButtonIcons( new String[] {"dialogs/refresh.png", "cancel.png"});
    129                 dialog.configureContextsensitiveHelp(ht("/Plugin/AutomaticUpdate"), true /* show help button */);
    130                 dialog.showDialog();
    131                 if(dialog.getValue() == 1) {
    132                     new PluginSelection().update();
    133                 }
    134             }
    135         }
    136 
    137         SortedMap<Integer, Collection<PluginInformation>> p = new TreeMap<Integer, Collection<PluginInformation>>();
    138         for (String pluginName : plugins) {
    139             PluginInformation info = PluginInformation.findPlugin(pluginName);
    140             if (info != null) {
    141                 if (info.early != early) {
    142                     continue;
    143                 }
    144                 int josmVersion = Version.getInstance().getVersion();
    145                 if (info.mainversion > josmVersion && josmVersion != Version.JOSM_UNKNOWN_VERSION) {
    146                     JOptionPane.showMessageDialog(
    147                             Main.parent,
    148                             tr("Plugin {0} requires JOSM update to version {1}.", pluginName,
    149                                     info.mainversion),
    150                                     tr("Warning"),
    151                                     JOptionPane.WARNING_MESSAGE
    152                     );
    153                     continue;
    154                 }
    155 
    156                 if(info.requires != null)
    157                 {
    158                     String warn = null;
    159                     for(String n : info.requires.split(";"))
    160                     {
    161                         if(!plugins.contains(n))
    162                         { warn = n; break; }
    163                     }
    164                     if(warn != null)
    165                     {
    166                         JOptionPane.showMessageDialog(Main.parent,
    167                                 tr("Plugin {0} is required by plugin {1} but was not found.",
    168                                         warn, pluginName),
    169                                         tr("Error"),
    170                                         JOptionPane.ERROR_MESSAGE
    171                         );
    172                         continue;
    173                     }
    174                 }
    175                 if (!p.containsKey(info.stage)) {
    176                     p.put(info.stage, new LinkedList<PluginInformation>());
    177                 }
    178                 p.get(info.stage).add(info);
    179             } else if(early) {
    180                 JOptionPane.showMessageDialog(
    181                         Main.parent,
    182                         tr("Plugin not found: {0}.", pluginName),
    183                         tr("Error"),
    184                         JOptionPane.ERROR_MESSAGE
    185                 );
    186             }
    187         }
    188 
     97        // notify user about removed deprecated plugins
     98        //
     99        StringBuffer sb = new StringBuffer();
     100        sb.append("<html>");
     101        sb.append(trn(
     102                "The following plugin is no longer necessary and has been deactivated:",
     103                "The following plugins are no longer necessary and have been deactivated:",
     104                removedPlugins.size()
     105        ));
     106        sb.append("<ul>");
     107        for (String name: removedPlugins) {
     108            sb.append("<li>").append(name).append("</li>");
     109        }
     110        sb.append("</ul>");
     111        sb.append("</html>");
     112        JOptionPane.showMessageDialog(
     113                Main.parent,
     114                sb.toString(),
     115                tr("Warning"),
     116                JOptionPane.WARNING_MESSAGE
     117        );
     118    }
     119
     120    /**
     121     * Removes unmaintained plugins from a collection of plugins. Modifies the
     122     * collection <code>plugins</code>. Also removes the plugin from the list
     123     * of plugins in the preferences, if necessary.
     124     *
     125     * Asks the user for every unmaintained plugin whether it should be removed.
     126     *
     127     * @param plugins the collection of plugins
     128     */
     129    private static void filterUnmaintainedPlugins(Collection<String> plugins) {
     130        for (String unmaintained : UNMAINTAINED_PLUGINS) {
     131            if (!plugins.contains(unmaintained)) {
     132                continue;
     133            }
     134            String msg =  tr("<html>Loading of {0} plugin was requested."
     135                    + "<br>This plugin is no longer developed and very likely will produce errors."
     136                    +"<br>It should be disabled.<br>Delete from preferences?</html>", unmaintained);
     137            if (confirmDisablePlugin(msg,unmaintained)) {
     138                Main.pref.removeFromCollection("plugins", unmaintained);
     139                plugins.remove(unmaintained);
     140            }
     141        }
     142    }
     143
     144    /**
     145     * Checks whether the locally available plugins should be updated and
     146     * asks the user if running an update is OK. An update is advised if
     147     * JOSM was updated to a new version since the last plugin updates or
     148     * if the plugins were last updated a long time ago.
     149     *
     150     * @return true if a plugin update should be run; false, otherwise
     151     */
     152    public static boolean checkAndConfirmPluginUpdate() {
     153        String message = null;
     154        String togglePreferenceKey = null;
     155        int v = Version.getInstance().getVersion();
     156        if (Main.pref.getInteger("pluginmanager.version", 0) < v) {
     157            message = tr("<html>You updated your JOSM software.<br>"
     158                    + "To prevent problems the plugins should be updated as well.<br><br>"
     159                    + "Update plugins now?"
     160                    + "</html>"
     161            );
     162            togglePreferenceKey = "pluginmanger.version";
     163        }  else {
     164            long tim = System.currentTimeMillis();
     165            long last = Main.pref.getLong("pluginmanager.lastupdate", 0);
     166            Integer maxTime = Main.pref.getInteger("pluginmanager.warntime", 60);
     167            long d = (tim - last) / (24 * 60 * 60 * 1000l);
     168            if ((last <= 0) || (maxTime <= 0)) {
     169                Main.pref.put("pluginmanager.lastupdate", Long.toString(tim));
     170            } else if (d > maxTime) {
     171                message = tr("Last plugin update more than {0} days ago.", d);
     172                togglePreferenceKey = "pluginmanager.time";
     173            }
     174        }
     175        if (message == null) return false;
     176
     177        // ask whether update is fine
     178        //
     179        ExtendedDialog dialog = new ExtendedDialog(
     180                Main.parent,
     181                tr("Update plugins"),
     182                new String[] {
     183                    tr("Update plugins"), tr("Skip update")
     184                }
     185        );
     186        dialog.setContent(message);
     187        dialog.toggleEnable(togglePreferenceKey);
     188        dialog.setButtonIcons( new String[] {"dialogs/refresh.png", "cancel.png"});
     189        dialog.configureContextsensitiveHelp(ht("/Plugin/AutomaticUpdate"), true /* show help button */);
     190        dialog.showDialog();
     191        return dialog.getValue() == 1;
     192    }
     193
     194    /**
     195     * Alerts the user if a plugin required by another plugin is missing
     196     *
     197     * @param plugin the the plugin
     198     * @param missingRequiredPlugin the missing required plugin
     199     */
     200    private static void alertMissingRequiredPlugin(String plugin, Set<String> missingRequiredPlugin) {
     201        StringBuilder sb = new StringBuilder();
     202        sb.append("<html>");
     203        sb.append(trn("A required plugin for plugin {0} was not found. The required plugin is:",
     204                "{1} required plugins for plugin {0} were not found. The required plugins are:",
     205                missingRequiredPlugin.size(),
     206                plugin,
     207                missingRequiredPlugin.size()
     208        ));
     209        sb.append("<ul>");
     210        for (String p: missingRequiredPlugin) {
     211            sb.append("<li>").append(p).append("</li>");
     212        }
     213        sb.append("</ul>").append("</html>");
     214        JOptionPane.showMessageDialog(
     215                Main.parent,
     216                sb.toString(),
     217                tr("Error"),
     218                JOptionPane.ERROR_MESSAGE
     219        );
     220    }
     221
     222    /**
     223     * Checks whether all preconditions for loading the plugin <code>plugin</code> are met. The
     224     * current JOSM version must be compatible with the plugin and no other plugins this plugin
     225     * depends on should be missing.
     226     *
     227     * @param plugins the collection of all loaded plugins
     228     * @param plugin the plugin for which preconditions are checked
     229     * @return true, if the preconditions are met; false otherwise
     230     */
     231    public static boolean checkLoadPreconditions(Collection<PluginInformation> plugins, PluginInformation plugin) {
     232
     233        // make sure the plugin is compatible with the current JOSM version
     234        //
     235        int josmVersion = Version.getInstance().getVersion();
     236        if (plugin.mainversion > josmVersion && josmVersion != Version.JOSM_UNKNOWN_VERSION) {
     237            JOptionPane.showMessageDialog(
     238                    Main.parent,
     239                    tr("Plugin {0} requires JOSM update to version {1}.", plugin.name,
     240                            plugin.mainversion),
     241                            tr("Warning"),
     242                            JOptionPane.WARNING_MESSAGE
     243            );
     244            return false;
     245        }
     246
     247        // make sure the dependencies to other plugins are not broken
     248        //
     249        if(plugin.requires != null){
     250            Set<String> pluginNames = new HashSet<String>();
     251            for (PluginInformation pi: plugins) {
     252                pluginNames.add(pi.name);
     253            }
     254            Set<String> missingPlugins = new HashSet<String>();
     255            for (String requiredPlugin : plugin.requires.split(";")) {
     256                if (!pluginNames.contains(requiredPlugin)) {
     257                    missingPlugins.add(requiredPlugin);
     258                }
     259            }
     260            if (!missingPlugins.isEmpty()) {
     261                alertMissingRequiredPlugin(plugin.name, missingPlugins);
     262                return false;
     263            }
     264        }
     265        return true;
     266    }
     267
     268    /**
     269     * Creates a class loader for loading plugin code.
     270     *
     271     * @param plugins the collection of plugins which are going to be loaded with this
     272     * class loader
     273     * @return the class loader
     274     */
     275    public static ClassLoader createClassLoader(Collection<PluginInformation> plugins) {
    189276        // iterate all plugins and collect all libraries of all plugins:
    190         List<URL> allPluginLibraries = new ArrayList<URL>();
    191         for (Collection<PluginInformation> c : p.values()) {
    192             for (PluginInformation info : c) {
    193                 allPluginLibraries.addAll(info.libraries);
    194             }
    195         }
     277        List<URL> allPluginLibraries = new LinkedList<URL>();
     278        File pluginDir = Main.pref.getPluginsDirectory();
     279        for (PluginInformation info : plugins) {
     280            if (info.libraries == null) {
     281                continue;
     282            }
     283            allPluginLibraries.addAll(info.libraries);
     284            File pluginJar = new File(pluginDir, info.name + ".jar");
     285            URL pluginJarUrl = PluginInformation.fileToURL(pluginJar);
     286            allPluginLibraries.add(pluginJarUrl);
     287        }
     288
    196289        // create a classloader for all plugins:
    197290        URL[] jarUrls = new URL[allPluginLibraries.size()];
    198291        jarUrls = allPluginLibraries.toArray(jarUrls);
    199292        URLClassLoader pluginClassLoader = new URLClassLoader(jarUrls, Main.class.getClassLoader());
    200         ImageProvider.sources.add(0, pluginClassLoader);
    201 
    202         for (Collection<PluginInformation> c : p.values()) {
    203             for (PluginInformation info : c) {
    204                 try {
    205                     Class<?> klass = info.loadClass(pluginClassLoader);
    206                     if (klass != null) {
    207                         System.out.println("loading "+info.name);
    208                         pluginList.add(info.load(klass));
     293        return pluginClassLoader;
     294    }
     295
     296    /**
     297     * Loads and instantiates the plugin described by <code>plugin</code> using
     298     * the class loader <code>pluginClassLoader</code>.
     299     *
     300     * @param plugin the plugin
     301     * @param pluginClassLoader the plugin class loader
     302     */
     303    public static void loadPlugin(PluginInformation plugin, ClassLoader pluginClassLoader) {
     304        try {
     305            Class<?> klass = plugin.loadClass(pluginClassLoader);
     306            if (klass != null) {
     307                System.out.println(tr("loading plugin ''{0}''", plugin.name));
     308                pluginList.add(plugin.load(klass));
     309            }
     310        } catch (Throwable e) {
     311            e.printStackTrace();
     312            String msg = tr("Could not load plugin {0}. Delete from preferences?", plugin.name);
     313            if (confirmDisablePlugin(msg, plugin.name)) {
     314                Main.pref.removeFromCollection("plugins", plugin.name);
     315            }
     316        }
     317    }
     318
     319    /**
     320     * Loads the plugin in <code>plugins</code> from locally available jar files into
     321     * memory.
     322     *
     323     * @param plugins the list of plugins
     324     * @param monitor the progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null.
     325     */
     326    public static void loadPlugins(Collection<PluginInformation> plugins, ProgressMonitor monitor) {
     327        if (monitor == null) {
     328            monitor = NullProgressMonitor.INSTANCE;
     329        }
     330        try {
     331            monitor.beginTask(tr("Loading plugins ..."));
     332            List<PluginInformation> toLoad = new LinkedList<PluginInformation>();
     333            // sort the plugins according to their "staging" equivalence class. The
     334            // lower the value of "stage" the earlier the plugin should be loaded.
     335            //
     336            Collections.sort(
     337                    toLoad,
     338                    new Comparator<PluginInformation>() {
     339                        public int compare(PluginInformation o1, PluginInformation o2) {
     340                            if (o1.stage < o2.stage) return -1;
     341                            if (o1.stage == o2.stage) return 0;
     342                            return 1;
     343                        }
    209344                    }
    210                 } catch (Throwable e) {
    211                     e.printStackTrace();
    212                     disablePlugin(tr("Could not load plugin {0}. Delete from preferences?", info.name), info.name);
    213                 }
    214             }
    215         }
    216     }
    217     public static boolean disablePlugin(String reason, String name)
    218     {
     345            );
     346            monitor.subTask(tr("Checking plugin preconditions..."));
     347            for (PluginInformation pi: plugins) {
     348                if (checkLoadPreconditions(plugins, pi)) {
     349                    toLoad.add(pi);
     350                }
     351            }
     352            if (toLoad.isEmpty())
     353                return;
     354
     355            ClassLoader pluginClassLoader = createClassLoader(toLoad);
     356            ImageProvider.sources.add(0, pluginClassLoader);
     357            monitor.setTicksCount(toLoad.size());
     358            for (PluginInformation info : toLoad) {
     359                monitor.setExtraText(tr("Loading plugin ''{0}''...", info.name));
     360                loadPlugin(info, pluginClassLoader);
     361                monitor.worked(1);
     362            }
     363        } finally {
     364            monitor.finishTask();
     365        }
     366    }
     367
     368    /**
     369     * Loads plugins from <code>plugins</code> which have the flag {@see PluginInformation#early}
     370     * set to true.
     371     *
     372     * @param plugins the collection of plugins
     373     * @param monitor the progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null.
     374     */
     375    public static void loadEarlyPlugins(Collection<PluginInformation> plugins, ProgressMonitor monitor) {
     376        List<PluginInformation> earlyPlugins = new ArrayList<PluginInformation>(plugins.size());
     377        for (PluginInformation pi: plugins) {
     378            if (pi.early) {
     379                earlyPlugins.add(pi);
     380            }
     381        }
     382        loadPlugins(earlyPlugins, monitor);
     383    }
     384
     385    /**
     386     * Loads plugins from <code>plugins</code> which have the flag {@see PluginInformation#early}
     387     * set to false.
     388     *
     389     * @param plugins the collection of plugins
     390     * @param monitor the progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null.
     391     */
     392    public static void loadLatePlugins(Collection<PluginInformation> plugins, ProgressMonitor monitor) {
     393        List<PluginInformation> latePlugins = new ArrayList<PluginInformation>(plugins.size());
     394        for (PluginInformation pi: plugins) {
     395            if (!pi.early) {
     396                latePlugins.add(pi);
     397            }
     398        }
     399        loadPlugins(latePlugins, monitor);
     400    }
     401
     402    /**
     403     * Loads locally available plugin information from local plugin jars and from cached
     404     * plugin lists.
     405     *
     406     * @param monitor the progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null.
     407     * @return the list of locally available plugin information
     408     *
     409     */
     410    private static Map<String, PluginInformation> loadLocallyAvailablePluginInformation(ProgressMonitor monitor) {
     411        if (monitor == null) {
     412            monitor = NullProgressMonitor.INSTANCE;
     413        }
     414        try {
     415            ReadLocalPluginInformationTask task = new ReadLocalPluginInformationTask(monitor);
     416            ExecutorService service = Executors.newSingleThreadExecutor();
     417            Future<?> future = service.submit(task);
     418            try {
     419                future.get();
     420            } catch(ExecutionException e) {
     421                e.printStackTrace();
     422                return null;
     423            } catch(InterruptedException e) {
     424                e.printStackTrace();
     425                return null;
     426            }
     427            HashMap<String, PluginInformation> ret = new HashMap<String, PluginInformation>();
     428            for (PluginInformation pi: task.getAvailablePlugins()) {
     429                ret.put(pi.name, pi);
     430            }
     431            return ret;
     432        } finally {
     433            monitor.finishTask();
     434        }
     435    }
     436
     437    private static void alertMissingPluginInformation(Collection<String> plugins) {
     438        StringBuilder sb = new StringBuilder();
     439        sb.append("<html>");
     440        sb.append(trn("JOSM could not find information about the following plugin:",
     441                "JOSM could not find information about the following plugins:",
     442                plugins.size()));
     443        sb.append("<ul>");
     444        for (String plugin: plugins) {
     445            sb.append("<li>").append(plugin).append("</li>");
     446        }
     447        sb.append("</ul>");
     448        sb.append(trn("The plugin is not going to be loaded.",
     449                "The plugins are not going to be loaded.",
     450                plugins.size()));
     451        sb.append("</html>");
     452        JOptionPane.showMessageDialog(
     453                Main.parent,
     454                sb.toString(),
     455                tr("Warning"),
     456                JOptionPane.WARNING_MESSAGE
     457        );
     458    }
     459
     460    /**
     461     * Builds the set of plugins to load. Deprecated and unmaintained plugins are filtered
     462     * out. This involves user interaction. This method displays alert and confirmation
     463     * messages.
     464     *
     465     * @param monitor the progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null.
     466     * @return the set of plugins to load (as set of plugin names)
     467     */
     468    public static List<PluginInformation> buildListOfPluginsToLoad(ProgressMonitor monitor) {
     469        if (monitor == null) {
     470            monitor = NullProgressMonitor.INSTANCE;
     471        }
     472        try {
     473            monitor.beginTask(tr("Determine plugins to load..."));
     474            Set<String> plugins = new HashSet<String>();
     475            plugins.addAll(Main.pref.getCollection("plugins",  new LinkedList<String>()));
     476            if (System.getProperty("josm.plugins") != null) {
     477                plugins.addAll(Arrays.asList(System.getProperty("josm.plugins").split(",")));
     478            }
     479            monitor.subTask(tr("Removing deprecated plugins..."));
     480            filterDeprecatedPlugins(plugins);
     481            monitor.subTask(tr("Removing umaintained plugins..."));
     482            filterUnmaintainedPlugins(plugins);
     483            Map<String, PluginInformation> infos = loadLocallyAvailablePluginInformation(monitor.createSubTaskMonitor(1,false));
     484            List<PluginInformation> ret = new LinkedList<PluginInformation>();
     485            for (Iterator<String> it = plugins.iterator(); it.hasNext();) {
     486                String plugin = it.next();
     487                if (infos.containsKey(plugin)) {
     488                    ret.add(infos.get(plugin));
     489                    it.remove();
     490                }
     491            }
     492            if (!plugins.isEmpty()) {
     493                alertMissingPluginInformation(plugins);
     494            }
     495            return ret;
     496        } finally {
     497            monitor.finishTask();
     498        }
     499    }
     500
     501    private static void alertFailedPluginUpdate(Collection<PluginInformation> plugins) {
     502        StringBuffer sb = new StringBuffer();
     503        sb.append("<html>");
     504        sb.append(trn(
     505                "Updating the following plugin has failed:",
     506                "Updating the following plugins has failed:",
     507                plugins.size()
     508        )
     509        );
     510        sb.append("<ul>");
     511        for (PluginInformation pi: plugins) {
     512            sb.append("<li>").append(pi.name).append("</li>");
     513        }
     514        sb.append("</ul>");
     515        sb.append(tr("Please open the Preference Dialog after JOSM has started and try to update them manually."));
     516        sb.append("</html>");
     517        JOptionPane.showMessageDialog(
     518                Main.parent,
     519                sb.toString(),
     520                tr("Plugin update failed"),
     521                JOptionPane.ERROR_MESSAGE
     522        );
     523    }
     524
     525    /**
     526     * Updates the plugins in <code>plugins</code>.
     527     *
     528     * @param plugins the collection of plugins to update. Must not be null.
     529     * @param monitor the progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null.
     530     * @throws IllegalArgumentException thrown if plugins is null
     531     */
     532    public static void updatePlugins(Collection<PluginInformation> plugins, ProgressMonitor monitor) throws IllegalArgumentException{
     533        CheckParameterUtil.ensureParameterNotNull(plugins, "plugins");
     534        if (monitor == null) {
     535            monitor = NullProgressMonitor.INSTANCE;
     536        }
     537        try {
     538            PluginDownloadTask task = new PluginDownloadTask(
     539                    monitor,
     540                    plugins,
     541                    tr("Update plugins")
     542            );
     543            ExecutorService service = Executors.newSingleThreadExecutor();
     544            Future<?> future = service.submit(task);
     545            try {
     546                future.get();
     547            } catch(ExecutionException e) {
     548                e.printStackTrace();
     549            } catch(InterruptedException e) {
     550                e.printStackTrace();
     551            }
     552            if (! task.getFailedPlugins().isEmpty()) {
     553                alertFailedPluginUpdate(task.getFailedPlugins());
     554                return;
     555            }
     556        } finally {
     557            monitor.finishTask();
     558        }
     559        // remember the update because it was successful
     560        //
     561        Main.pref.putInteger("pluginmanager.version", Version.getInstance().getVersion());
     562        Main.pref.put("pluginmanager.lastupdate", Long.toString(System.currentTimeMillis()));
     563    }
     564
     565    /**
     566     * Ask the user for confirmation that a plugin shall be disabled.
     567     *
     568     * @param reason the reason for disabling the plugin
     569     * @param name the plugin name
     570     * @return true, if the plugin shall be disabled; false, otherwise
     571     */
     572    public static boolean confirmDisablePlugin(String reason, String name) {
    219573        ExtendedDialog dialog = new ExtendedDialog(
    220574                Main.parent,
    221575                tr("Disable plugin"),
    222                 new String[] {tr("Disable plugin"), tr("Keep plugin")}
     576                new String[] {
     577                    tr("Disable plugin"), tr("Keep plugin")
     578                }
    223579        );
    224580        dialog.setContent(reason);
    225         dialog.setButtonIcons( new String[] {"dialogs/delete.png", "cancel.png"});
     581        dialog.setButtonIcons(new String[] { "dialogs/delete.png", "cancel.png" });
    226582        dialog.showDialog();
    227         int result = dialog.getValue();
    228 
    229         if(result == 1)
    230         {
    231             Main.pref.removeFromCollection("plugins", name);
    232             return true;
    233         }
    234         return false;
    235     }
    236 
    237     public static void setMapFrame(MapFrame old, MapFrame map) {
     583        return dialog.getValue() == 1;
     584    }
     585
     586    /**
     587     * Notified loaded plugins about a new map frame
     588     *
     589     * @param old the old map frame
     590     * @param map the new map frame
     591     */
     592    public static void notifyMapFrameChanged(MapFrame old, MapFrame map) {
    238593        for (PluginProxy plugin : pluginList) {
    239594            plugin.mapFrameInitialized(old, map);
     
    248603    }
    249604
    250     public static void addDownloadSelection(List<DownloadSelection> downloadSelections)
    251     {
     605    public static void addDownloadSelection(List<DownloadSelection> downloadSelections) {
    252606        for (PluginProxy p : pluginList) {
    253607            p.addDownloadSelection(downloadSelections);
    254608        }
    255609    }
    256     public static void getPreferenceSetting(Collection<PreferenceSettingFactory> settings)
    257     {
     610
     611    public static void getPreferenceSetting(Collection<PreferenceSettingFactory> settings) {
    258612        for (PluginProxy plugin : pluginList) {
    259613            settings.add(new PluginPreferenceFactory(plugin));
     
    261615    }
    262616
    263     public static void earlyCleanup()
    264     {
    265         if (!PluginDownloader.moveUpdatedPlugins()) {
    266             JOptionPane.showMessageDialog(
    267                     Main.parent,
    268                     tr("Activating the updated plugins failed. Check if JOSM has the permission to overwrite the existing ones."),
    269                     tr("Plugins"), JOptionPane.ERROR_MESSAGE);
    270         }
    271     }
    272     public static Boolean checkException(Throwable e)
     617    /**
     618     * Installs downloaded plugins. Moves files with the suffix ".jar.new" to the corresponding
     619     * ".jar" files.
     620     *
     621     */
     622    public static void installDownloadedPlugins() {
     623        File pluginDir = Main.pref.getPluginsDirectory();
     624        if (! pluginDir.exists() || ! pluginDir.isDirectory() || ! pluginDir.canWrite())
     625            return;
     626
     627        final File[] files = pluginDir.listFiles(new FilenameFilter() {
     628            public boolean accept(File dir, String name) {
     629                return name.endsWith(".jar.new");
     630            }});
     631
     632        for (File updatedPlugin : files) {
     633            final String filePath = updatedPlugin.getPath();
     634            File plugin = new File(filePath.substring(0, filePath.length() - 4));
     635            String pluginName = updatedPlugin.getName().substring(0, updatedPlugin.getName().length() - 8);
     636            if (plugin.exists()) {
     637                if (!plugin.delete()) {
     638                    System.err.println(tr("Warning: failed to delete outdated plugin ''{0}''.", plugin.toString()));
     639                    System.err.println(tr("Warning: failed to install already downloaded plugin ''{0}''. Skipping installation. JOSM still going to load the old plugin version.", pluginName));
     640                    continue;
     641                }
     642            }
     643            if (!updatedPlugin.renameTo(plugin)) {
     644                System.err.println(tr("Warning: failed to install plugin ''{0}'' from temporary download file ''{1}''. Renaming failed.", plugin.toString(), updatedPlugin.toString()));
     645                System.err.println(tr("Warning: failed to install already downloaded plugin ''{0}''. Skipping installation. JOSM still going to load the old plugin version.", pluginName));
     646            }
     647        }
     648        return;
     649    }
     650
     651    public static boolean checkException(Throwable e)
    273652    {
    274653        PluginProxy plugin = null;
     
    350729        return false;
    351730    }
    352     public static String getBugReportText()
    353     {
     731
     732    public static String getBugReportText() {
    354733        String text = "";
    355734        String pl = Main.pref.getCollectionAsString("plugins");
    356         if(pl != null && pl.length() != 0) {
    357             text += "Plugins: "+pl+"\n";
     735        if (pl != null && pl.length() != 0) {
     736            text += "Plugins: " + pl + "\n";
    358737        }
    359738        for (final PluginProxy pp : pluginList) {
    360             text += "Plugin " + pp.info.name + (pp.info.version != null && !pp.info.version.equals("") ? " Version: "+pp.info.version+"\n" : "\n");
     739            text += "Plugin "
     740                + pp.info.name
     741                + (pp.info.version != null && !pp.info.version.equals("") ? " Version: " + pp.info.version + "\n"
     742                        : "\n");
    361743        }
    362744        return text;
    363745    }
    364     public static JPanel getInfoPanel()
    365     {
     746
     747    public static JPanel getInfoPanel() {
    366748        JPanel pluginTab = new JPanel(new GridBagLayout());
    367749        for (final PluginProxy p : pluginList) {
    368             String name = p.info.name + (p.info.version != null && !p.info.version.equals("") ? " Version: "+p.info.version : "");
     750            String name = p.info.name
     751            + (p.info.version != null && !p.info.version.equals("") ? " Version: " + p.info.version : "");
    369752            pluginTab.add(new JLabel(name), GBC.std());
    370753            pluginTab.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
    371             pluginTab.add(new JButton(new AbstractAction(tr("Information")){
     754            pluginTab.add(new JButton(new AbstractAction(tr("Information")) {
    372755                public void actionPerformed(ActionEvent event) {
    373756                    StringBuilder b = new StringBuilder();
    374                     for (Entry<String,String> e : p.info.attr.entrySet()) {
     757                    for (Entry<String, String> e : p.info.attr.entrySet()) {
    375758                        b.append(e.getKey());
    376759                        b.append(": ");
     
    378761                        b.append("\n");
    379762                    }
    380                     JTextArea a = new JTextArea(10,40);
     763                    JTextArea a = new JTextArea(10, 40);
    381764                    a.setEditable(false);
    382765                    a.setText(b.toString());
    383                     JOptionPane.showMessageDialog(
    384                             Main.parent,
    385                             new JScrollPane(a),
    386                             tr("Plugin information"),
    387                             JOptionPane.INFORMATION_MESSAGE
    388                     );
     766                    JOptionPane.showMessageDialog(Main.parent, new JScrollPane(a), tr("Plugin information"),
     767                            JOptionPane.INFORMATION_MESSAGE);
    389768                }
    390769            }), GBC.eol());
    391770
    392             JTextArea description = new JTextArea((p.info.description==null? tr("no description available"):p.info.description));
     771            JTextArea description = new JTextArea((p.info.description == null ? tr("no description available")
     772                    : p.info.description));
    393773            description.setEditable(false);
    394774            description.setFont(new JLabel().getFont().deriveFont(Font.ITALIC));
    395775            description.setLineWrap(true);
    396776            description.setWrapStyleWord(true);
    397             description.setBorder(BorderFactory.createEmptyBorder(0,20,0,0));
     777            description.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0));
    398778            description.setBackground(UIManager.getColor("Panel.background"));
    399779
  • trunk/src/org/openstreetmap/josm/plugins/PluginInformation.java

    r2620 r2817  
    4343    public int stage = 50;
    4444    public String version = null;
     45    public String localversion = null;
    4546    public String downloadlink = null;
    4647    public List<URL> libraries = new LinkedList<URL>();
     
    6061     * @param file the plugin jar file.
    6162     */
    62     public PluginInformation(File file) {
     63    public PluginInformation(File file) throws PluginException{
    6364        this(file, file.getName().substring(0, file.getName().length()-4));
    6465    }
    6566
    66     public PluginInformation(File file, String name) {
     67    public PluginInformation(File file, String name) throws PluginException{
    6768        this.name = name;
    6869        this.file = file;
     70        JarInputStream jar = null;
    6971        try {
    70             JarInputStream jar = new JarInputStream(new FileInputStream(file));
     72            jar = new JarInputStream(new FileInputStream(file));
    7173            Manifest manifest = jar.getManifest();
    7274            if (manifest == null)
    73                 throw new IOException(file+" contains no manifest.");
     75                throw new PluginException(name, tr("The plugin file ''{0}'' doesn't include a Manifest.", file.toString()));
    7476            scanManifest(manifest, false);
    7577            libraries.add(0, fileToURL(file));
    76             jar.close();
    7778        } catch (IOException e) {
    78             throw new PluginException(null, name, e);
    79         }
    80     }
    81 
    82     public PluginInformation(InputStream manifestStream, String name, String url) {
     79            throw new PluginException(name, e);
     80        } finally {
     81            if (jar != null) {
     82                try {
     83                    jar.close();
     84                } catch(IOException e) { /* ignore */ }
     85            }
     86        }
     87    }
     88
     89    public PluginInformation(InputStream manifestStream, String name, String url) throws PluginException {
    8390        this.name = name;
    8491        try {
     
    9097            scanManifest(manifest, url != null);
    9198        } catch (IOException e) {
    92             throw new PluginException(null, name, e);
     99            throw new PluginException(name, e);
    93100        }
    94101    }
     
    167174    }
    168175
    169     public String getLinkDescription()
    170     {
    171         String d = description == null ? tr("no description available") : description;
    172         if(link != null) {
    173             d += " <A HREF=\""+link+"\">"+tr("More details")+"</A>";
    174         }
    175         return d;
     176    /**
     177     * Replies the description as HTML document, including a link to a web page with
     178     * more information, provided such a link is available.
     179     *
     180     * @return the description as HTML document
     181     */
     182    public String getDescriptionAsHtml() {
     183        StringBuilder sb = new StringBuilder();
     184        sb.append("<html><body>");
     185        sb.append(description == null ? tr("no description available") : description);
     186        if (link != null) {
     187            sb.append(" <a href=\"").append(link).append("\">").append(tr("More info...")).append("</a>");
     188        }
     189        sb.append("</body></html>");
     190        return sb.toString();
    176191    }
    177192
     
    179194     * Load and instantiate the plugin
    180195     */
    181     public PluginProxy load(Class<?> klass) {
     196    public PluginProxy load(Class<?> klass) throws PluginException{
    182197        try {
    183198            currentPluginInitialization = this;
    184199            return new PluginProxy(klass.newInstance(), this);
    185         } catch (Exception e) {
    186             throw new PluginException(null, name, e);
     200        } catch(IllegalAccessException e) {
     201            throw new PluginException(name, e);
     202        } catch (InstantiationException e) {
     203            throw new PluginException(name, e);
    187204        }
    188205    }
     
    191208     * Load the class of the plugin
    192209     */
    193     public Class<?> loadClass(ClassLoader classLoader) {
     210    public Class<?> loadClass(ClassLoader classLoader) throws PluginException {
    194211        if (className == null)
    195212            return null;
    196         try {
     213        try{
    197214            Class<?> realClass = Class.forName(className, true, classLoader);
    198215            return realClass;
    199         } catch (Exception e) {
    200             throw new PluginException(null, name, e);
     216        } catch (ClassNotFoundException e) {
     217            throw new PluginException(name, e);
    201218        }
    202219    }
     
    272289        return all;
    273290    }
     291
     292    /**
     293     * Replies true if the plugin with the given information is most likely outdated with
     294     * respect to the referenceVersion.
     295     *
     296     * @param referenceVersion the reference version. Can be null if we don't know a
     297     * reference version
     298     *
     299     * @return true, if the plugin needs to be updated; false, otherweise
     300     */
     301    public boolean isUpdateRequired(String referenceVersion) {
     302        if (this.version == null && referenceVersion!= null)
     303            return true;
     304        if (this.version != null && !this.version.equals(referenceVersion))
     305            return true;
     306        return false;
     307    }
     308
     309    /**
     310     * Replies true if this this plugin should be updated/downloaded because either
     311     * it is not available locally (its local version is null) or its local version is
     312     * older than the available version on the server.
     313     *
     314     * @return true if the plugin should be updated
     315     */
     316    public boolean isUpdateRequired() {
     317        if (this.localversion == null) return false;
     318        return isUpdateRequired(this.localversion);
     319    }
     320
     321    protected boolean matches(String filter, String value) {
     322        if (filter == null) return true;
     323        if (value == null) return false;
     324        return value.toLowerCase().contains(filter.toLowerCase());
     325    }
     326
     327    /**
     328     * Replies true if either the name, the description, or the version match (case insensitive)
     329     * one of the words in filter. Replies true if filter is null.
     330     *
     331     * @param filter the filter expression
     332     * @return true if this plugin info matches with the filter
     333     */
     334    public boolean matches(String filter) {
     335        if (filter == null) return true;
     336        String words[] = filter.split("\\s+");
     337        for (String word: words) {
     338            if (matches(word, name)
     339                    || matches(word, description)
     340                    || matches(word, version)
     341                    || matches(word, localversion))
     342                return true;
     343        }
     344        return false;
     345    }
     346
     347    /**
     348     * Replies the name of the plugin
     349     */
     350    public String getName() {
     351        return name;
     352    }
     353
     354    /**
     355     * Sets the name
     356     * @param name
     357     */
     358    public void setName(String name) {
     359        this.name = name;
     360    }
     361
    274362}
Note: See TracChangeset for help on using the changeset viewer.