PluginManager.java
/**
* VStar: a statistical analysis tool for variable star data.
* Copyright (C) 2010 AAVSO (http://www.aavso.org/)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.aavso.tools.vstar.ui.dialog.plugin.manager;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.prefs.Preferences;
import javax.swing.SwingUtilities;
import org.aavso.tools.vstar.plugin.IPlugin;
import org.aavso.tools.vstar.ui.VStar;
import org.aavso.tools.vstar.ui.dialog.MessageBox;
import org.aavso.tools.vstar.ui.resources.ResourceAccessor;
import org.aavso.tools.vstar.util.Pair;
/**
* This class manages plug-in installation, deletion, and update.
*/
public class PluginManager {
public final static String DEFAULT_PLUGIN_BASE_URL_STR = "https://archive.aavso.org/sites/default/files/vstar-plugins/vstar-plugins-"
+ ResourceAccessor.getVersionString();
// public final static String DEFAULT_PLUGIN_BASE_URL_STR =
// "file:///Users/david/tmp/vstar-plugins/vstar-plugins-"
// + ResourceAccessor.getVersionString();
public final static String PLUGINS_LIST_FILE = ".plugins.lst";
public final static String PLUGINS_DIR = "vstar_plugins";
public final static String PLUGIN_LIBS_DIR = "vstar_plugin_libs";
private final static String PLUGIN_PREFS_PREFIX = "PLUGIN_";
public enum Operation {
INSTALL, UPDATE;
}
private String pluginBaseUrl;
private static Preferences prefs;
static {
// Create preferences node for plug-in management.
try {
prefs = Preferences.userNodeForPackage(PluginManager.class);
} catch (Throwable t) {
// We need VStar to function in the absence of prefs.
}
}
/**
* A mapping from plugin jar name to plugin files available to be installed
* for the current version of VStar.
*/
private Map<String, URL> remotePlugins;
/**
* A mapping from description to remote plugin jar name.
*/
private Map<String, String> remoteDescriptions;
/**
* A mapping from description to remote plugin document names.
*/
private Map<String, String> remoteDocNames;
/**
* A mapping from plugin jar name to plugin files installed locally.
*/
private Map<String, File> localPlugins;
/**
* A mapping from description to local plugin jar name.
*/
private Map<String, String> localDescriptions;
/**
* A mapping from description to local plugin document names.
*/
private Map<String, String> localDocNames;
/**
* A mapping from plugin jar name to dependent library files available to be
* installed for the current version of VStar.
*/
private Map<String, List<URL>> libs;
/**
* A mapping from description to dependent library jar name.
*/
private Map<String, Set<String>> libDescriptions;
/**
* A mapping from dependent library jar name to integers for reference
* counting.
*/
private Map<String, Integer> libRefs;
/**
* A mapping from description to equality of local and remote plugins
* corresponding to the same description.
*/
private Map<String, Boolean> remoteAndLocalPluginEquality;
/**
* Has the current operation been interrupted?
*/
private boolean interrupted;
/**
* Constructor
*/
public PluginManager() {
pluginBaseUrl = getPluginsBaseUrl();
}
/**
* @return the remote plugins map
*/
public Map<String, URL> getRemotePluginsByJarName() {
return remotePlugins;
}
/**
* Initialise manager.
*/
public void init() {
retrieveRemotePluginInfo();
retrieveLocalPluginInfo();
determinePluginEquality();
}
/**
* Should plug-ins be loaded according to preferences?
*/
public static boolean shouldLoadPlugins() {
boolean loadPlugins = true;
try {
loadPlugins = prefs.getBoolean(
PLUGIN_PREFS_PREFIX + "LOAD_PLUGINS", true);
} catch (Throwable t) {
// We need VStar to function in the absence of prefs.
}
return loadPlugins;
}
/**
* Set the load plug-ins preference.
*
* @param state
* The true/false state to set.
*/
public static void setLoadPlugins(boolean state) {
try {
prefs.putBoolean(PLUGIN_PREFS_PREFIX + "LOAD_PLUGINS", state);
} catch (Throwable t) {
// We need VStar to function in the absence of prefs.
}
}
/**
* Should all observation source plug-ins be shown in the file menu
* according to preferences? Defaults to false.
*/
public static boolean shouldAllObsSourcePluginsBeInFileMenu() {
boolean loadPlugins = true;
try {
loadPlugins = prefs.getBoolean(PLUGIN_PREFS_PREFIX
+ "OBS_SOURCE_PLUGINS_IN_FILE_MENU", false);
} catch (Throwable t) {
// We need VStar to function in the absence of prefs.
}
return loadPlugins;
}
/**
* Set whether all observation source plug-ins be shown in the file menu
* according to preferences?
*
* @param state
* The true/false state to set.
*/
public static void setAllObsSourcePluginsInFileMenu(boolean state) {
try {
prefs.putBoolean(PLUGIN_PREFS_PREFIX
+ "OBS_SOURCE_PLUGINS_IN_FILE_MENU", state);
} catch (Throwable t) {
// We need VStar to function in the absence of prefs.
}
}
/**
* Return the plug-ins base URL according to preferences.
*/
public static String getPluginsBaseUrl() {
String baseUrl = DEFAULT_PLUGIN_BASE_URL_STR;
try {
baseUrl = prefs.get(PLUGIN_PREFS_PREFIX + "BASE_URL",
DEFAULT_PLUGIN_BASE_URL_STR);
} catch (Throwable t) {
// We need VStar to function in the absence of prefs.
}
return baseUrl;
}
/**
* Set the plug-ins base URL preference.
*
* @param url
* The URL to set.
*/
public static void setPluginsBaseUrl(String url) {
try {
prefs.put(PLUGIN_PREFS_PREFIX + "BASE_URL", url);
} catch (Throwable t) {
// We need VStar to function in the absence of prefs.
}
}
/**
* @return the remote plugin map
*/
public Map<String, String> getRemoteDescriptionsToJarName() {
return remoteDescriptions;
}
/**
* @return the remote plugin descriptions
*/
public Set<String> getRemoteDescriptions() {
return remoteDescriptions.keySet();
}
/**
* @return the local plugins map
*/
public Map<String, File> getLocalPluginsByJarName() {
return localPlugins;
}
/**
* @return the local descriptions map
*/
public Map<String, String> getLocalDescriptionsToJarName() {
return localDescriptions;
}
/**
* @return the local plugin descriptions
*/
public Set<String> getLocalDescriptions() {
return localDescriptions.keySet();
}
/**
* @return the libs
*/
public Map<String, List<URL>> getLibs() {
return libs;
}
/**
* @return the plugin document name.
*/
public String getPluginDocName(String description) {
String doc_name = localDocNames.get(description);
if (doc_name == null || "".equals(doc_name)) {
doc_name = remoteDocNames.get(description);
}
return doc_name;
}
/**
* Does the plugin description correspond to a remote plugin?
*
* @param description
* The description.
* @return True if the plugin is remote, false if not.
*/
public boolean isRemote(String description) {
return getRemoteDescriptionsToJarName().containsKey(description);
}
/**
* Does the plugin description correspond to a local plugin?
*
* @param description
* The description.
* @return True if the plugin is local, false if not.
*/
public boolean isLocal(String description) {
return getLocalDescriptionsToJarName().containsKey(description);
}
/**
* Does the plugin description correspond to both a remote and local plugin?
*
* @param description
* The description.
* @return True if the plugin is both remote and local, false if not.
*/
public boolean isRemoteAndLocal(String description) {
return isLocal(description) && isRemote(description);
}
/**
* Determine whether plugins that are both local and remote refer to the
* same jar and cache this information.
*/
public void determinePluginEquality() {
// First, create a common set consisting of the intersection of remote
// and local plugins...
Set<String> remoteDescSet = new HashSet<String>(
remoteDescriptions.keySet());
Set<String> localDescSet = new HashSet<String>(
localDescriptions.keySet());
Set<String> commonDescSet = new HashSet<String>();
for (String desc : remoteDescSet) {
if (isRemoteAndLocal(desc)) {
commonDescSet.add(desc);
}
}
for (String desc : localDescSet) {
if (isRemoteAndLocal(desc)) {
commonDescSet.add(desc);
}
}
// ...then populate the remote+local plugin equality map.
remoteAndLocalPluginEquality = new HashMap<String, Boolean>();
for (String desc : commonDescSet) {
String localJarName = null;
String remoteJarName = null;
try {
localJarName = localDescriptions.get(desc);
remoteJarName = remoteDescriptions.get(desc);
URL localUrl = localPlugins.get(localJarName).toURI().toURL();
URL remoteUrl = remotePlugins.get(remoteJarName);
remoteAndLocalPluginEquality.put(desc,
areURLReferentsEqual(localUrl, remoteUrl));
} catch (IOException e) {
String msg = String.format(
"Error comparing remote and local plugins: %s and %s",
remoteJarName, localJarName);
throw new PluginManagerException(msg);
}
}
}
/**
* Does the plugin description for a remote and local plugin refer to the
* same jar?
*
* @param description
* The description.
* @return True iff the equal, false if not.
*/
public boolean arePluginsEqual(String description) {
return remoteAndLocalPluginEquality.containsKey(description)
&& remoteAndLocalPluginEquality.get(description);
}
/**
* Retrieve information about the available remotePlugins for this version
* of VStar.
*
* @param baseUrlStr
* The base URL string from where to obtain remotePlugins.
*/
public void retrieveRemotePluginInfo(String baseUrlStr) {
interrupted = false;
remotePlugins = new TreeMap<String, URL>();
remoteDescriptions = new TreeMap<String, String>();
remoteDocNames = new TreeMap<String, String>();
libs = new TreeMap<String, List<URL>>();
libDescriptions = new TreeMap<String, Set<String>>();
libRefs = new HashMap<String, Integer>();
String[] lines = null;
try {
URL infoUrl = new URL(baseUrlStr + "/" + PLUGINS_LIST_FILE);
URLConnection conn = infoUrl.openConnection();
List<String> lineList = new ArrayList<String>();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
lineList.add(line);
}
}
lines = lineList.toArray(new String[0]);
} catch (Exception e) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
MessageBox.showErrorDialog("Plug-in Manager",
"Error reading remote plug-in information.\nErrror:\n" +
e.getLocalizedMessage());
}
} );
return;
}
String pluginBaseURLStr = baseUrlStr + "/" + PLUGINS_DIR;
String libBaseURLStr = baseUrlStr + "/" + PLUGIN_LIBS_DIR;
ArrayList<String> errors = new ArrayList<String>();
for (String line : lines) {
if (interrupted)
break;
if (line == null)
continue;
line = line.trim();
if (line.length() == 0) {
continue;
}
if (line.charAt(0) == '#') {
continue;
}
// Separate jar file name from dependent libraries, if any exist.
String[] fields = line.split("\\s*=>\\s*");
if (fields.length == 0) {
continue;
}
// Load plugin, store mappings from jar name to plugin
// URL, lib URL, and description.
String pluginJarFileName = fields[0].trim();
// TODO: remove this after 2.6.10 after which no potential
// for colon-prefixed jar names will exist
if (pluginJarFileName.startsWith(":")) {
// if (!ResourceAccessor.getLoginInfo().isMember()) {
// // Member-only accessible plug-ins should be skipped
// // if not appropriately authenticated.
// continue;
// } else {
// Remove leading colon.
pluginJarFileName = pluginJarFileName.substring(1);
// }
}
String plugin_desc = null;
String plugin_doc = null;
URL pluginUrl = null;
String className = pluginJarFileName.replace(".jar", "");
try {
pluginUrl = new URL(pluginBaseURLStr + "/" + pluginJarFileName);
// Max: in general, we also need all dependent libs to load plugin files correctly.
// So we need to get list of plugin's libraries before calling getPluginDescription.
// This partially duplicates code below so, probably, an optimization could be done.
// See also the PluginLoader class.
List<URL> depLibs = new ArrayList<URL>();
if (fields.length > 1) {
for (String libFileStr : fields[1].split("\\s*,\\s*")) {
String libJarFileName = libFileStr.trim();
URL libUrl = new URL(libBaseURLStr + "/" + libJarFileName);
depLibs.add(libUrl);
}
}
Pair<String, String> info = getPluginDescription(pluginUrl, className, depLibs);
plugin_desc = info.first;
plugin_doc = info.second;
} catch (Exception e) {
//MessageBox.showErrorDialog("Plug-in Manager",
// "Error reading remote plug-in information: " + className + ".\nErrror:\n" +
// e.getLocalizedMessage());
errors.add("Plug-in: " + className + ", Error: " + e.getLocalizedMessage());
continue;
} catch (Throwable t) {
//MessageBox.showErrorDialog("Plug-in Manager",
// "Error reading remote plug-in information: " + className + ".\nSevere Error:\n" +
// t.getLocalizedMessage());
errors.add("Plug-in: " + className + ", Error: " + t.getLocalizedMessage());
continue;
}
remoteDescriptions.put(plugin_desc, pluginJarFileName);
remoteDocNames.put(plugin_desc, plugin_doc);
remotePlugins.put(pluginJarFileName, pluginUrl);
// Store dependent libs, if any exist, by plugin key.
if (fields.length > 1) {
if (interrupted)
break;
File pluginPath = new File(System.getProperty("user.home") + File.separator + PLUGINS_DIR);
for (String libFileStr : fields[1].split("\\s*,\\s*")) {
String libJarFileName = libFileStr.trim();
URL libUrl = null;
try {
libUrl = new URL(libBaseURLStr + "/" + libJarFileName);
} catch (Exception e) {
//MessageBox.showErrorDialog("Plug-in Manager",
// "Error reading remote plug-in information.\nErrror:\n" +
// e.getLocalizedMessage());
errors.add("Plug-in: " + className + ", Library: " + libJarFileName + ", Error: " + e.getLocalizedMessage());
break;
}
List<URL> libUrls = libs.get(pluginJarFileName);
if (libUrls == null) {
libUrls = new ArrayList<URL>();
libs.put(pluginJarFileName, libUrls);
}
libUrls.add(libUrl);
// Populate dependent library name and reference
// counting maps. Note that we treat the remote
// plugin information as the source of truth for the
// basis of checking against local library jar
// files. Our reference counting will only be as
// good as this remote/local correspondence.
File locaPluginJarFilePath = new File(pluginPath, pluginJarFileName);
if (locaPluginJarFilePath.exists()) {
if (!libDescriptions.containsKey(plugin_desc)) {
libDescriptions.put(plugin_desc, new HashSet<String>());
}
libDescriptions.get(plugin_desc).add(libJarFileName);
if (!libRefs.containsKey(libJarFileName)) {
libRefs.put(libJarFileName, 1);
} else {
int count = libRefs.get(libJarFileName) + 1;
libRefs.put(libJarFileName, count);
}
}
}
}
}
if (errors.size() > 0) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
String err = String.join("\n", errors);
MessageBox.showErrorDialog("Plug-in Manager",
"Error reading remote plug-in information.\nErrors:\n" + err);
}
} );
}
}
/**
* Retrieve information about the available remotePlugins for this version
* of VStar.
*/
public void retrieveRemotePluginInfo() {
retrieveRemotePluginInfo(pluginBaseUrl);
}
/**
* Retrieve information about locally installed plugins.
*/
public void retrieveLocalPluginInfo() {
interrupted = false;
localPlugins = new TreeMap<String, File>();
localDescriptions = new TreeMap<String, String>();
localDocNames = new TreeMap<String, String>();
FilenameFilter jarFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".jar");
}
};
// Get information about local plugins.
File pluginPath = new File(System.getProperty("user.home")
+ File.separator + PLUGINS_DIR);
File pluginLibPath = new File(System.getProperty("user.home")
+ File.separator + PLUGIN_LIBS_DIR);
ArrayList<String> errors = new ArrayList<String>();
List<URL> depLibs = new ArrayList<URL>();
if (pluginLibPath.exists() && pluginLibPath.isDirectory()) {
for (File file : pluginLibPath.listFiles(jarFilter)) {
try {
depLibs.add(file.toURI().toURL());
} catch (Exception e) {
//MessageBox.showErrorDialog(
// "Plug-in Manager",
// "Invalid plugin library file.\nError:\n" +
// e.getLocalizedMessage());
errors.add("File: " + file.getName() + ", Error: " + e.getLocalizedMessage());
}
}
}
if (pluginPath.exists() && pluginPath.isDirectory()) {
for (File file : pluginPath.listFiles(jarFilter)) {
if (interrupted)
break;
// Load plugin, store mappings from jar name to plugin
// file and description.
String plugin_desc = null;
String plugin_doc = null;
String pluginJarFileName = file.getName();
String className = pluginJarFileName.replace(".jar", "");
try {
localPlugins.put(pluginJarFileName, file);
Pair<String, String> info = getPluginDescription(file.toURI().toURL(), className, depLibs);
plugin_desc = info.first;
plugin_doc = info.second;
} catch (Exception e) {
//MessageBox.showErrorDialog("Plug-in Manager",
// "Error reading local plugin information: " + className + ".\nError:\n" +
// e.getLocalizedMessage());
errors.add("Plug-in class: " + className + ", Error: " + e.getLocalizedMessage());
continue;
} catch (Throwable t) {
//MessageBox.showErrorDialog("Plug-in Manager",
// "Error reading remote plug-in information: " + className + ".\nSevere Error:\n" +
// t.getLocalizedMessage());
errors.add("Plug-in class: " + className + ", Error: " + t.getLocalizedMessage());
continue;
}
localDescriptions.put(plugin_desc, pluginJarFileName);
localDocNames.put(plugin_desc, plugin_doc);
}
}
if (errors.size() > 0) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
String err = String.join("\n", errors);
MessageBox.showErrorDialog("Plug-in Manager",
"Error reading local plug-in information.\nErrors:\n" + err);
}
} );
}
}
/**
* Install the specified plugin and dependent libraries.
*
* @param description
* The plugin description corresponding to the remote plugin and
* dependent libraries to be installed.
* @param op
* The operation (install or update).
*/
public void installPlugin(String description, Operation op) {
interrupted = false;
try {
// Install plugin jars.
File pluginDirPath = new File(System.getProperty("user.home")
+ File.separator + PLUGINS_DIR);
if (!pluginDirPath.exists()) {
pluginDirPath.mkdir();
}
String jarName = remoteDescriptions.get(description);
URL pluginURL = remotePlugins.get(jarName);
String pluginJarName = pluginURL.getPath().substring(
pluginURL.getPath().lastIndexOf("/") + 1);
File pluginJarFile = new File(pluginDirPath, pluginJarName);
boolean pluginExisted = pluginJarFile.exists();
copy(pluginURL.openStream(), pluginJarFile);
// Update maps after copy.
localDescriptions.put(description, jarName);
localPlugins.put(jarName, pluginJarFile);
remoteAndLocalPluginEquality.put(description, true);
// Install dependent jars.
File pluginLibDirPath = new File(System.getProperty("user.home")
+ File.separator + PLUGIN_LIBS_DIR);
if (!pluginLibDirPath.exists()) {
pluginLibDirPath.mkdir();
}
List<URL> libUrls = libs.get(jarName);
if (libUrls != null) {
for (URL libURL : libUrls) {
if (interrupted)
break;
if (libURL != null) {
String libJarName = libURL.getPath().substring(
libURL.getPath().lastIndexOf("/") + 1);
File targetPath = new File(pluginLibDirPath, libJarName);
copy(libURL.openStream(), targetPath);
// Library reference counting.
switch (op) {
case INSTALL:
// Bind libJarName with the plug-in
if (!libDescriptions.containsKey(description)) {
libDescriptions.put(description, new HashSet<String>());
}
libDescriptions.get(description).add(libJarName);
// Implies a new dependency on any library file.
if (!libRefs.keySet().contains(libJarName)) {
// Actually, this should never occur, libRefs should be
// already populated in retrieveRemotePluginInfo
libRefs.put(libJarName, 1);
} else {
// Increment refcount ONLY if the plugin had not existed!
if (!pluginExisted) {
int count = libRefs.get(libJarName) + 1;
libRefs.put(libJarName, count);
}
}
break;
case UPDATE:
// The only dependency is upon libraries that are
// additional to the current update of the plugin
// compared to previous plugin version.
if (!libRefs.keySet().contains(libJarName)) {
// Actually, this should never occur, libRefs should be
// already populated in retrieveRemotePluginInfo
libRefs.put(libJarName, 1);
}
break;
}
}
}
}
} catch (IOException e) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
MessageBox.showErrorDialog("Plug-in Manager",
"An error occurred while installing remotePlugins.");
}
} );
}
}
/**
* Delete the specified plugin and dependent libraries.
*
* @param description
* The plugin description corresponding to the local plugin and
* dependent libraries to be deleted.
*/
public void deletePlugin(String description) {
interrupted = false;
// Delete plugin jar.
File pluginDirPath = new File(System.getProperty("user.home")
+ File.separator + PLUGINS_DIR);
String jarName = localDescriptions.get(description);
File pluginJarPath = new File(pluginDirPath, jarName);
if (pluginJarPath.exists()) {
if (!pluginJarPath.delete()) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
MessageBox.showErrorDialog("Plug-in Manager",
"Unable to delete plug-in " + jarName);
}
} );
} else {
// Update maps after delete.
localDescriptions.remove(description);
localPlugins.remove(jarName);
remoteAndLocalPluginEquality.remove(description);
}
} else {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
MessageBox.showErrorDialog("Plug-in Manager",
"Plug-in " + jarName +
" does not exist so unable to delete");
}
} );
}
// Delete dependent jars for this plug-in.
File pluginLibDirPath = new File(System.getProperty("user.home")
+ File.separator + PLUGIN_LIBS_DIR);
Set<String> libJarNames = libDescriptions.get(description);
if (libJarNames != null) {
for (String libJarName : libJarNames) {
if (interrupted)
break;
assert libRefs.containsKey(libJarName);
libRefs.put(libJarName, libRefs.get(libJarName) - 1);
// If the reference count for a library jar has fallen to
// zero, delete the file.
if (libRefs.get(libJarName) == 0) {
File libJarPath = new File(pluginLibDirPath, libJarName);
// This should be true but may not be, given the
// vagaries of file systems or the possibility of
// concurrent deletion.
if (libJarPath.exists()) {
if (!libJarPath.delete()) {
String errMsg = String.format(
"Unable to delete dependent library %s for plug-in %s",
libJarName, jarName);
SwingUtilities.invokeLater( new Runnable() {
public void run() {
MessageBox.showErrorDialog("Plug-in Manager", errMsg);
}
} );
}
} else {
String errMsg = String.format(
"The dependent library %s "
+ "for the plug-in %s cannot be "
+ "found so cannot be deleted.",
libJarName, jarName);
SwingUtilities.invokeLater( new Runnable() {
public void run() {
MessageBox.showErrorDialog("Plug-in Manager", errMsg);
}
} );
}
}
}
}
// Note that to avoid future lib jar clashes,
// we may need to consider: plugin subdirs in plugin libs dir and a
// separate class loader per plugin! => SF tracker
}
/**
* Delete all locally installed plug-ins.
*/
public void deleteAllPlugins() {
// This method called from Preferences synchronously.
//interrupted = false;
boolean deleteError = false;
//if (MessageBox.showConfirmDialog("Plug-in Manager",
// "Delete all plug-ins?")) {
try {
File pluginDirPath = new File(System.getProperty("user.home")
+ File.separator + PLUGINS_DIR);
File pluginLibDirPath = new File(
System.getProperty("user.home") + File.separator
+ PLUGIN_LIBS_DIR);
if (pluginDirPath.isDirectory()
&& pluginLibDirPath.isDirectory()) {
File[] jarFiles = pluginDirPath.listFiles();
for (File jarFile : jarFiles) {
//if (interrupted) {
// deleteError = true;
// break;
//}
// Check existence, to avoid an insanely unlikely race
// condition.
if (jarFile.exists()) {
if (!jarFile.delete()) {
deleteError = true;
}
}
}
File[] libJarFiles = pluginLibDirPath.listFiles();
for (File libJarFile : libJarFiles) {
if (interrupted) {
deleteError = true;
break;
}
// Check existence, to avoid an insanely unlikely race
// condition.
if (libJarFile.exists()) {
if (!libJarFile.delete()) {
deleteError = true;
}
}
}
// Don't clear collections if null (e.g. as will be the
// case when invoking this method from prefs pane).
if (localDescriptions != null) {
localDescriptions.clear();
localPlugins.clear();
libs.clear();
libDescriptions.clear();
libRefs.clear();
remoteAndLocalPluginEquality.clear();
}
if (!deleteError)
MessageBox.showMessageDialog("Plug-in Manager",
"All installed plug-ins have been deleted.");
else
MessageBox.showErrorDialog("Plug-in Manager",
"Cannot delete some plugins. Try to delete them manually.");
}
} catch (Throwable t) {
MessageBox.showErrorDialog("Plug-in Manager", "Deletion error");
}
//}
}
/**
* Interrupts the current operation.
*/
public void interrupt() {
interrupted = true;
}
// Helpers
private void copy(InputStream in, File file) throws IOException {
try {
OutputStream out = new FileOutputStream(file);
try {
byte[] buf = new byte[4096];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
} finally {
out.close();
}
} finally {
in.close();
}
}
private Pair<String, String> getPluginDescription(URL url, String className, List<URL> depLibs)
throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
List<URL> urlList = new ArrayList<URL>();
urlList.add(url);
urlList.addAll(depLibs);
URL[] urls = urlList.toArray(new URL[0]);
URLClassLoader loader = new URLClassLoader(urls, VStar.class.getClassLoader());
try {
Class<?> clazz = loader.loadClass(className);
IPlugin plugin = (IPlugin) clazz.newInstance();
return new Pair<String, String>(plugin.getDescription(), plugin.getDocName());
} finally {
loader.close();
}
}
private boolean areURLReferentsEqual(URL url1, URL url2) throws IOException {
return getBytesFromURL(url1).equals(getBytesFromURL(url2));
}
private List<Byte> getBytesFromURL(URL url) throws IOException {
List<Byte> byteList = new ArrayList<Byte>();
InputStream stream = url.openStream();
try {
byte[] buf = new byte[4096];
int len;
while ((len = stream.read(buf)) > 0) {
for (int i = 0; i < len; i++) {
byteList.add(buf[i]);
}
}
} finally {
stream.close();
}
return byteList;
}
}