PluginManager.java
| Index Score | ||
|---|---|---|
![]() |
![]() |
org.xnap.plugin |
![]() |
![]() |
XNap 3 |
View: Reasons, Metrics, Source Code
These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.
/*
* XNap - A P2P framework and client.
*
* See the file AUTHORS for copyright information.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.xnap.plugin;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import java.util.Properties;
import org.apache.log4j.Logger;
import org.xnap.XNap;
import org.xnap.gui.SplashWindow;
import org.xnap.gui.XNapFrame;
import org.xnap.loader.*;
import org.xnap.pkg.*;
import org.xnap.pkg.PackageInfoReader;
import org.xnap.pkg.PackageInfoWriter;
import org.xnap.pkg.ParseException;
import org.xnap.pkg.UnsatisfiedDependenciesException;
import org.xnap.pkg.XNapPackageManager;
import org.xnap.event.PluginListener;
import org.xnap.util.FileHelper;
import org.xnap.util.Preferences;
import org.xnap.util.UncaughtExceptionManager;
import org.xnap.util.VersionParser;
/**
* This class keeps track of all available and enabled plugins.
*/
public class PluginManager
{
//--- Constant(s) ---
public static final String PLUGIN_FILENAME
= FileHelper.getHomeDir() + "available_plugins";
//--- Data field(s) ---
private static Logger logger = Logger.getLogger(PluginManager.class);
private static Preferences prefs = Preferences.getInstance();
private static PluginManager singleton = new PluginManager();
private Hashtable infoByName = new Hashtable();
private List listeners = new Vector();
//--- Constructor(s) ---
/**
* Discovers available plugins. For each plugin that has satisfied
* dependencies a {@link PluginInfo} object is created and added to the
* plugin manager.
*/
private PluginManager()
{
if (XNap.clearPackageInfos
|| Preferences.getInstance().getUpdatePluginsOnStartup()) {
updateFromPackageManager();
}
File file = new File(PLUGIN_FILENAME);
if (!XNap.clearPackageInfos) {
// read control file from home directory
if (file.exists()) {
try {
read(file);
}
catch (IOException e) {
logger.info("Error reading list of available plugins", e);
}
}
else {
updateFromPackageManager();
}
}
removeBaseConflicts();
}
//--- Method(s) ---
public static PluginManager getInstance()
{
return singleton;
}
/**
* Constructs a {@link PluginInfo} object from <code>props</code>
* and invokes {@link #add(PluginInfo)} if info is valid.
*
* @see #PluginManager()
*/
public PluginInfo add(Properties props)
{
PluginInfo info = new PluginInfo(props);
if (info.isValid() && add(info)) {
return info;
}
return null;
}
/**
* Adds a new plugin information object to the list of available
* plugins.
*
* <p>If the plugin was already known and <code>info</code>
* has a lower version or the plugin was already loaded,
* <code>info</code> is not added.
*
* @return true, if <code>info</code> was added; false, otherwise
*/
public boolean add(PluginInfo info)
{
if (!info.areRequirementsSatisfied()) {
return false;
}
PluginInfo existing = getInfoByName(info.getName());
if (existing != null) {
if (!existing.isLoaded()
&& existing.isAvailable()
&& VersionParser.compare(info.getVersion(),
existing.getVersion()) > 0) {
// replace by newer info
infoByName.put(info.getName(), info);
return true;
}
}
else {
// add new info
infoByName.put(info.getName(), info);
firePluginInfoAdded(info);
return true;
}
return false;
}
/**
* Adds a plugin from package info. The classpath is resolved to
* absolute jar file dependencies.
*
* @return true, if the plugin was added successfully
* @see #add(PluginInfo)
*/
public boolean addFromPackage(PackageInfo p)
throws ParseException, UnsatisfiedDependenciesException
{
PluginInfo info = new PluginInfo(p.getProperties());
info.setClassPath(PluginManager.resolveClassPath(info));
return add(info);
}
/**
* Adds the specified plugin listener to receive plugin events.
*/
public void addPluginListener(PluginListener l)
{
listeners.add(l);
}
/**
* Removes the specified plugin listener.
*/
public void removePluginListener(PluginListener l)
{
listeners.remove(l);
}
/**
* Returns the xnap-core package.
*
* @return null, if package is not found
*/
public PluginInfo getCorePackage()
{
return getInfoByName("XNap");
}
public static String getCoreVersion()
{
PluginInfo info = PluginManager.getInstance().getCorePackage();
return (info != null) ? info.getVersion() : XNapLoader.VERSION;
}
/**
* Returns the number of enabled plugins.
*/
public int getEnabledCount()
{
int count = 0;
for (Iterator i = infos(); i.hasNext();) {
PluginInfo info = (PluginInfo)i.next();
if (info.isEnabled()) {
count++;
}
}
return count;
}
/**
* Invoked by {@link org.xnap.gui.XNapFrame}.
*/
public void guiStarted()
{
int count = getEnabledCount();
int increaseBy = (count > 0)
? (95 - SplashWindow.getProgress()) / count
: 0;
for (Iterator i = infos(); i.hasNext();) {
PluginInfo info = (PluginInfo)i.next();
if (info.isEnabled()) {
SplashWindow.incProgress
(increaseBy, XNap.tr("Setting up {0}", info.getName()));
try {
info.getPlugin().startGUI();
}
catch(Throwable t) {
logger.error("could not start plugin gui", t);
UncaughtExceptionManager.getInstance().notify(t);
}
}
}
}
/**
* Invoked by {@link org.xnap.gui.XNapFrame}.
*/
public void guiStopped()
{
for (Iterator i = infos(); i.hasNext();) {
PluginInfo info = (PluginInfo)i.next();
if (info.isEnabled()) {
try {
info.getPlugin().stopGUI();
}
catch(Throwable t) {
logger.error("could not stop plugin gui", t);
UncaughtExceptionManager.getInstance().notify(t);
}
}
}
}
/**
* Returns the information record for plugin with name.
*
* @param name the plugin's name
*/
public PluginInfo getInfoByName(String name)
{
return (PluginInfo)infoByName.get(name);
}
/**
* Return a sorted iterator over a copy of all {@link PluginInfo} objects.
* Changes made to the iterator are not reflected.
*/
public Iterator infos()
{
LinkedList list = new LinkedList(infoByName.values());
Collections.sort(list, new PluginInfoComparator());
return list.iterator();
}
/**
* Loads the plugin described by <code>info</code>. All required jar
* files are added to the class loader and the plugin class is
* instantiated.
*
* @param info the plugin to be loaded
* @return the instantiated plugin object
*/
public Plugin load(PluginInfo info) throws Exception
{
if (!info.isLoaded()) {
ClassLoader loader = getClass().getClassLoader();
if (loader == null) {
loader = XNapClassLoader.getInstance();
}
XNapClassLoader.getInstance().add(info.getClassPath());
Class c = loader.loadClass(info.getClassName());
Plugin p = (Plugin)c.newInstance();
info.setPlugin(p);
p.setInfo(info);
return p;
}
else {
return info.getPlugin();
}
}
/**
* Reads information about installed plugins from file.
*
* @param alreadyLoaded if true, the plugin is already installed in
* the classpath
*/
private void read(File file) throws IOException
{
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(file));
Properties p;
while ((p = PackageInfoReader.readNext(in)) != null) {
add(p);
}
}
finally {
if (in != null) {
try {
in.close();
}
catch (IOException e) {
}
}
}
}
/**
* Removes all plugins that conflict with the installed base
* packages. Currently only xnap-core is considered to be part of
* base.
*/
private void removeBaseConflicts()
{
// remove conflicting plugins
try {
PluginInfo baseInfo = getCorePackage();
if (baseInfo == null) {
logger.warn("Required base package XNap not found!");
return;
}
PackageInfo[] conflicts
= XNapPackageManager.getInstance().getConflicts(baseInfo);
if (conflicts != null) {
for (int i = 0; i < conflicts.length; i++) {
PluginInfo info = getInfoByName(conflicts[i].getName());
if (info != null && info.equals(conflicts[i])) {
// plugin conflicts with base
remove(info);
}
}
}
}
catch (ParseException e) {
logger.warn("Error parsing base conflicts");
}
}
/**
* Restores the state of the plugin manager from the {@link Preferences}.
*/
public void restore()
{
String[] names = prefs.getEnabledPlugins();
for (int i = 0; i < names.length; i++) {
PluginInfo info = getInfoByName(names[i]);
if (info != null) {
info.setEnableOnStartup(true);
try {
SplashWindow.incProgress(2, XNap.tr("Loading {0} plugin",
info.getName()));
Plugin p = info.getPlugin();
if (p == null) {
p = load(info);
}
setEnabled(p, true);
}
catch (PluginInitializeException e) {
logger.error("could not restore plugin " + names[i], e);
}
catch (ClassNotFoundException e) {
logger.error("could not restore plugin " + names[i], e);
}
catch (Throwable t) {
logger.error("could not restore plugin " + names[i], t);
UncaughtExceptionManager.getInstance().notify(t);
}
}
else {
logger.error("plugin not found " + names[i]);
}
}
}
/**
* Disables all plugins and saves the state of the PluginManager.
*/
public void save()
{
List enabledPlugins = new LinkedList();
for (Iterator i = infos(); i.hasNext();) {
PluginInfo info = (PluginInfo)i.next();
if (info.isEnabled()) {
try {
setEnabled(info.getPlugin(), false, true);
if (info.getEnableOnStartup()) {
enabledPlugins.add(info.getName());
}
}
catch(Throwable t) {
logger.error("could not disable plugin", t);
UncaughtExceptionManager.getInstance().notify(t);
}
}
}
// save the names of the successfully disabled plugins
prefs.setEnabledPlugins((String[])enabledPlugins.toArray(new String[0]));
}
/**
* Enables or disables <code>plugin</code>
*/
private void setEnabled(Plugin plugin, boolean enable, boolean force)
throws Exception
{
if (enable) {
if (!plugin.getInfo().isEnabled()) {
plugin.start();
if (XNapFrame.getInstance() != null) {
plugin.startGUI();
}
plugin.getInfo().setEnabled(true);
firePluginEnabled(plugin);
}
}
else {
if (!plugin.getInfo().canDisable() && !force) {
throw new Exception(XNap.tr("Can not disable plugin. Restart XNap to disable plugin."));
}
if (plugin.getInfo().isEnabled()) {
if (XNapFrame.getInstance() != null) {
plugin.stopGUI();
}
plugin.stop();
plugin.getInfo().setEnabled(false);
firePluginDisabled(plugin);
}
}
}
public void setEnabled(Plugin plugin, boolean enable)
throws Exception
{
setEnabled(plugin, enable, false);
}
/**
* Returns the number of plugins.
*/
public int size()
{
return infoByName.values().size();
}
public void remove(PluginInfo info)
{
infoByName.remove(info.getName());
firePluginInfoRemoved(info);
}
/**
* Resolves the class path of info to absolute filenames.
*/
public static String[] resolveClassPath(PluginInfo info)
throws ParseException, UnsatisfiedDependenciesException
{
List list = new LinkedList();
PackageInfo[] depends
= XNapPackageManager.getInstance().getDependencies(info);
for (int j = 0; j < depends.length; j++) {
String classPath[] = depends[j].getClassPath();
for (int i = 0; i < classPath.length; i++) {
File f = depends[j].getFile(classPath[i].trim());
list.add(f.getAbsolutePath());
}
}
return (String[])list.toArray(new String[0]);
}
/**
* Updates the list of available plugins from {@link
* XNapPackageManager}.
*/
public void updateFromPackageManager()
{
XNapPackageManager.getInstance().initialize();
for (Iterator i = XNapPackageManager.getInstance().packages();
i.hasNext();) {
PackageInfo p = (PackageInfo)i.next();
try {
if (p.isPlugin() || p.isBase()) {
addFromPackage(p);
}
}
catch (ParseException e) {
logger.debug("Could not resolve dependencies for "
+ p.getPackage() + " " + p.getVersion()
+ ": " + e.getMessage());
continue;
}
catch (UnsatisfiedDependenciesException e) {
logger.debug("Unsatisfied dependency for "
+ p.getPackage() + " " + p.getVersion()
+ ": " + e.getMessage());
continue;
}
}
write();
}
public void write()
{
try {
write(new File(PLUGIN_FILENAME));
}
catch (IOException e) {
logger.debug("Error writing list of available plugins", e);
}
}
private void write(File file) throws IOException
{
BufferedWriter out = new BufferedWriter(new FileWriter(file));
try {
for (Iterator i = infoByName.values().iterator(); i.hasNext();) {
PluginInfo info = (PluginInfo)i.next();
PackageInfoWriter.write(out, info.getProperties());
}
}
finally {
try {
out.close();
}
catch (IOException e) {
}
}
}
private void firePluginEnabled(Plugin plugin)
{
Object[] l = listeners.toArray();
if (l != null) {
for (int i = l.length - 1; i >= 0; i--) {
((PluginListener)l[i]).pluginEnabled(plugin);
}
}
}
private void firePluginDisabled(Plugin plugin)
{
Object[] l = listeners.toArray();
if (l != null) {
for (int i = l.length - 1; i >= 0; i--) {
((PluginListener)l[i]).pluginDisabled(plugin);
}
}
}
private void firePluginInfoAdded(PluginInfo info)
{
Object[] l = listeners.toArray();
if (l != null) {
for (int i = l.length - 1; i >= 0; i--) {
((PluginListener)l[i]).pluginInfoAdded(info);
}
}
}
private void firePluginInfoRemoved(PluginInfo info)
{
Object[] l = listeners.toArray();
if (l != null) {
for (int i = l.length - 1; i >= 0; i--) {
((PluginListener)l[i]).pluginInfoRemoved(info);
}
}
}
//--- Inner Classes ---
/**
* Compares {@link PluginInfo} objects by name.
*/
public static class PluginInfoComparator implements Comparator
{
public int compare(Object o1, Object o2)
{
return ((PluginInfo)o1).getName().compareToIgnoreCase
(((PluginInfo)o2).getName());
}
}
}
The table below shows all metrics for PluginManager.java.




