
net.sourceforge.squirrel_sql.client.plugin.PluginManager Maven / Gradle / Ivy
package net.sourceforge.squirrel_sql.client.plugin;
/*
* Copyright (C) 2001-2004 Colin Bell
* [email protected]
*
* Modifications Copyright (c) 2004 Jason Height.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.sourceforge.squirrel_sql.client.ApplicationArguments;
import net.sourceforge.squirrel_sql.client.IApplication;
import net.sourceforge.squirrel_sql.client.gui.db.SQLAlias;
import net.sourceforge.squirrel_sql.client.gui.db.aliasproperties.IAliasPropertiesPanelController;
import net.sourceforge.squirrel_sql.client.gui.desktopcontainer.ISessionWidget;
import net.sourceforge.squirrel_sql.client.gui.desktopcontainer.IWidget;
import net.sourceforge.squirrel_sql.client.gui.desktopcontainer.WidgetAdapter;
import net.sourceforge.squirrel_sql.client.gui.desktopcontainer.WidgetEvent;
import net.sourceforge.squirrel_sql.client.gui.session.ObjectTreeInternalFrame;
import net.sourceforge.squirrel_sql.client.gui.session.SQLInternalFrame;
import net.sourceforge.squirrel_sql.client.session.ISession;
import net.sourceforge.squirrel_sql.client.util.ApplicationFiles;
import net.sourceforge.squirrel_sql.fw.gui.GUIUtils;
import net.sourceforge.squirrel_sql.fw.id.IIdentifier;
import net.sourceforge.squirrel_sql.fw.util.ClassLoaderListener;
import net.sourceforge.squirrel_sql.fw.util.MyURLClassLoader;
import net.sourceforge.squirrel_sql.fw.util.StringManager;
import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
import net.sourceforge.squirrel_sql.fw.util.Utilities;
import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
/**
* Manages plugins for the application.
*
* @author Colin Bell
*/
public class PluginManager implements IPluginManager
{
/** Internationalized strings for this class. */
private static final StringManager s_stringMgr =
StringManagerFactory.getStringManager(PluginManager.class);
/** Logger for this class. */
private static final ILogger s_log = LoggerController.createLogger(PluginManager.class);
/** Application API object. */
private IApplication _app;
/** Classloader used for plugins. */
private MyURLClassLoader _pluginsClassLoader;
/**
* Contains a PluginInfo object for every plugin that we attempted to load.
*/
private final List _plugins = new ArrayList();
/**
* Contains all plugins (IPlugin) successfully loaded. Keyed by
* IPlugin.getInternalName().
*/
private final Map _loadedPlugins = new HashMap();
/**
* Contains a SessionPluginInfo object for evey object in
* _loadedPlugins that is an instance of ISessionPlugin.
*/
private final List _sessionPlugins = new ArrayList();
/**
* Collection of active sessions. Keyed by ISession.getIdentifier() and contains a List
* of active ISessionPlugin objects for the session.
*/
private final Map> _activeSessions =
new HashMap>();
/**
* Collection of PluginLoadInfo objects for the plugins. Stores info about how long it took to
* load each plugin.
*/
private final Map _pluginLoadInfoColl = new HashMap();
private HashMap> _pluginSessionCallbacksBySessionID =
new HashMap>();
/** The class that listens for notifications as archives are being loaded */
private ClassLoaderListener classLoaderListener = null;
/**
* Ctor.
*
* @param app
* Application API.
* @throws IllegalArgumentException.
* Thrown if null IApplication passed.
*/
public PluginManager(IApplication app)
{
super();
if (app == null)
{
throw new IllegalArgumentException("IApplication == null");
}
_app = app;
}
/**
* A new session has been created. At this point the SessionPanel does not exist for the new
* session.
*
* @param session
* The new session.
* @throws IllegalArgumentException
* Thrown if a null ISession passed.
*/
public synchronized void sessionCreated(ISession session)
{
if (session == null)
{
throw new IllegalArgumentException("ISession == null");
}
for (Iterator it = _sessionPlugins.iterator(); it.hasNext();)
{
SessionPluginInfo spi = it.next();
try
{
spi.getSessionPlugin().sessionCreated(session);
} catch (Throwable th)
{
String msg =
s_stringMgr.getString("PluginManager.error.sessioncreated", spi.getPlugin()
.getDescriptiveName());
s_log.error(msg, th);
_app.showErrorDialog(msg, th);
}
}
}
/**
* A new session is starting.
*
* @param session
* The new session.
* @throws IllegalArgumentException
* Thrown if a null ISession passed.
*/
public synchronized void sessionStarted(final ISession session)
{
if (session == null)
{
throw new IllegalArgumentException("ISession == null");
}
final List plugins = new ArrayList();
_activeSessions.put(session.getIdentifier(), plugins);
ArrayList startInFG = new ArrayList();
final ArrayList startInBG = new ArrayList();
for (Iterator it = _sessionPlugins.iterator(); it.hasNext();)
{
SessionPluginInfo spi = it.next();
if (spi.getSessionPlugin().allowsSessionStartedInBackground())
{
startInBG.add(spi);
} else
{
startInFG.add(spi);
}
}
session.setPluginsfinishedLoading(true);
for (Iterator it = startInFG.iterator(); it.hasNext();)
{
SessionPluginInfo spi = it.next();
sendSessionStarted(session, spi, plugins);
}
session.getApplication().getThreadPool().addTask(new Runnable()
{
public void run()
{
for (Iterator it = startInBG.iterator(); it.hasNext();)
{
SessionPluginInfo spi = it.next();
sendSessionStarted(session, spi, plugins);
}
session.setPluginsfinishedLoading(true);
}
});
}
private void sendSessionStarted(ISession session, SessionPluginInfo spi, List plugins)
{
try
{
PluginSessionCallback pluginSessionCallback = spi.getSessionPlugin().sessionStarted(session);
if (null != pluginSessionCallback)
{
List list =
_pluginSessionCallbacksBySessionID.get(session.getIdentifier());
if (null == list)
{
list = new ArrayList();
_pluginSessionCallbacksBySessionID.put(session.getIdentifier(), list);
}
list.add(pluginSessionCallback);
plugins.add(spi);
}
} catch (final Throwable th)
{
final String msg =
s_stringMgr.getString("PluginManager.error.sessionstarted", spi.getPlugin().getDescriptiveName());
s_log.error(msg, th);
GUIUtils.processOnSwingEventThread(new Runnable()
{
public void run()
{
_app.showErrorDialog(msg, th);
}
});
}
}
/**
* A session is ending.
*
* @param session
* The session ending.
* @throws IllegalArgumentException
* Thrown if a null ISession passed.
*/
public synchronized void sessionEnding(ISession session)
{
if (session == null)
{
throw new IllegalArgumentException("ISession == null");
}
List plugins = _activeSessions.remove(session.getIdentifier());
if (plugins != null)
{
for (Iterator it = plugins.iterator(); it.hasNext();)
{
SessionPluginInfo spi = it.next();
try
{
spi.getSessionPlugin().sessionEnding(session);
} catch (Throwable th)
{
String msg =
s_stringMgr.getString("PluginManager.error.sessionended", spi .getPlugin()
.getDescriptiveName());
s_log.error(msg, th);
_app.showErrorDialog(msg, th);
}
}
_pluginSessionCallbacksBySessionID.remove(session.getIdentifier());
}
}
public synchronized void unloadPlugin(String pluginInternalName)
{
for (Iterator it = _loadedPlugins.values().iterator(); it.hasNext();)
{
IPlugin plugin = it.next();
if (plugin.getInternalName().equals(pluginInternalName))
{
plugin.unload();
it.remove();
}
}
for (Iterator it = _sessionPlugins.iterator(); it.hasNext();)
{
SessionPluginInfo plugin = it.next();
if (plugin.getInternalName().equals(pluginInternalName))
{
it.remove();
}
}
}
/**
* Unload all plugins.
*/
public synchronized void unloadPlugins()
{
for (Iterator it = _loadedPlugins.values().iterator(); it.hasNext();)
{
IPlugin plugin = it.next();
try
{
plugin.unload();
} catch (Throwable th)
{
String msg = s_stringMgr.getString("PluginManager.error.unloading", plugin.getInternalName());
s_log.error(msg, th);
_app.showErrorDialog(msg, th);
}
}
}
public synchronized PluginInfo[] getPluginInformation()
{
return _plugins.toArray(new PluginInfo[_plugins.size()]);
}
public synchronized SessionPluginInfo[] getPluginInformation(ISession session)
{
if (session == null)
{
throw new IllegalArgumentException("Null ISession passed");
}
List list = _activeSessions.get(session.getIdentifier());
if (list != null)
{
return list.toArray(new SessionPluginInfo[list.size()]);
}
return new SessionPluginInfo[0];
}
public synchronized IPluginDatabaseObjectType[] getDatabaseObjectTypes(ISession session)
{
List objTypesList = new ArrayList();
List plugins = _activeSessions.get(session.getIdentifier());
if (plugins != null)
{
for (Iterator it = plugins.iterator(); it.hasNext();)
{
SessionPluginInfo spi = it.next();
IPluginDatabaseObjectType[] objTypes = spi.getSessionPlugin().getObjectTypes(session);
if (objTypes != null)
{
for (int i = 0; i < objTypes.length; ++i)
{
objTypesList.add(objTypes[i]);
}
}
}
}
return objTypesList.toArray(new IPluginDatabaseObjectType[objTypesList.size()]);
}
/**
* Retrieve an array of all the URL objects that are used to find plugin classes.
*
* @return URL[].
*/
public URL[] getPluginURLs()
{
return _pluginsClassLoader.getURLs();
}
public PluginStatus[] getPluginStatuses()
{
return _app.getSquirrelPreferences().getPluginStatuses();
}
public synchronized void setPluginStatuses(PluginStatus[] values)
{
_app.getSquirrelPreferences().setPluginStatuses(values);
}
/**
* Retrieve loaded session plugins
*
* @return Iterator over a collection of ISessionPlugin objects.
*/
public Iterator getSessionPluginIterator()
{
return _sessionPlugins.iterator();
}
/**
* TODO: Clean this mess up!!!! Load plugins. Load all plugin jars into class loader.
*/
public void loadPlugins()
{
List pluginUrls = new ArrayList();
File dir = new ApplicationFiles().getPluginsDirectory();
boolean isMac = System.getProperty("os.name").toLowerCase().startsWith("mac");
if (dir.isDirectory())
{
final Map pluginStatuses = new HashMap();
{
final PluginStatus[] ar = getPluginStatuses();
for (int i = 0; i < ar.length; ++i)
{
pluginStatuses.put(ar[i].getInternalName(), ar[i]);
}
}
File[] files = dir.listFiles();
for (int i = 0; i < files.length; ++i)
{
if (files[i].isFile())
{
checkPlugin(files[i], pluginStatuses, pluginUrls, isMac);
}
}
}
Collections.sort(pluginUrls, new PluginLoadOrderComparator());
URL[] urls = pluginUrls.toArray(new URL[pluginUrls.size()]);
if (s_log.isDebugEnabled())
{
for (int i = 0; i < urls.length; ++i)
{
s_log.debug("Plugin class loader URL[" + i + "] = " + urls[i]);
}
}
_pluginsClassLoader = new MyURLClassLoader(urls);
_pluginsClassLoader.addClassLoaderListener(classLoaderListener);
Class>[] classes = _pluginsClassLoader.getAssignableClasses(IPlugin.class, s_log);
for (int i = 0; i < classes.length; ++i)
{
try
{
loadPlugin(classes[i]);
} catch (Throwable th)
{
String msg = s_stringMgr.getString("PluginManager.error.loadpluginclass", classes[i].getName());
th.printStackTrace();
s_log.error(msg, th);
_app.showErrorDialog(msg, th);
}
}
Collections.sort(_plugins, new Comparator()
{
public int compare(PluginInfo arg0, PluginInfo arg1)
{
if (arg0 == null || arg1 == null)
{
throw new NullPointerException("arg1 and arg2 must not be null");
}
return arg0.getInternalName().compareTo(arg1.getInternalName());
}
});
}
private void checkPlugin(File pluginFile, Map pluginStatuses, List pluginUrls,
boolean isMac)
{
final String fileName = pluginFile.getAbsolutePath();
if (!fileName.toLowerCase().endsWith("src.jar")
&& (fileName.toLowerCase().endsWith(".zip") || fileName.toLowerCase().endsWith(".jar")))
{
try
{
if (fileName.toLowerCase().endsWith("jedit.jar"))
{
String msg = s_stringMgr.getString("PluginManager.error.jedit");
_app.showErrorDialog(msg);
return;
}
final String fullFilePath = pluginFile.getAbsolutePath();
final String internalName = Utilities.removeFileNameSuffix(pluginFile.getName());
final PluginStatus ps = pluginStatuses.get(internalName);
if (!isMac && internalName.startsWith("macosx"))
{
s_log.info("Detected MacOS X plugin on non-Mac platform - skipping");
return;
}
if (ps != null && !ps.isLoadAtStartup())
{
// We need this in order to allow the user to see this
// plugin - which isn't loaded - in the plugin summary
// dialog, so that they can enable it.
PluginInfo pi = new PluginInfo();
pi.setPlugin(new MyPlaceHolderPlugin(internalName));
_plugins.add(pi);
} else
{
pluginUrls.add(pluginFile.toURI().toURL());
// See if plugin has any jars in lib dir.
final String pluginDirName = Utilities.removeFileNameSuffix(fullFilePath);
final File libDir = new File(pluginDirName, "lib");
addPluginLibraries(libDir, pluginUrls);
}
} catch (IOException ex)
{
String msg = s_stringMgr.getString("PluginManager.error.loadplugin", fileName);
s_log.error(msg, ex);
_app.showErrorDialog(msg, ex);
}
}
}
private void addPluginLibraries(File libDir, List pluginUrls)
{
if (libDir.exists() && libDir.isDirectory())
{
File[] libDirFiles = libDir.listFiles();
for (int j = 0; j < libDirFiles.length; ++j)
{
if (libDirFiles[j].isFile())
{
final String fn = libDirFiles[j].getAbsolutePath();
if (fn.toLowerCase().endsWith(".zip") || fn.toLowerCase().endsWith(".jar"))
{
try
{
pluginUrls.add(libDirFiles[j].toURI().toURL());
} catch (IOException ex)
{
String msg = s_stringMgr.getString("PluginManager.error.loadlib", fn);
s_log.error(msg, ex);
_app.showErrorDialog(msg, ex);
}
}
}
}
}
}
/**
* Initialize plugins.
*/
public void initializePlugins()
{
_app.getWindowManager().addSessionWidgetListener(new WidgetAdapter()
{
public void widgetOpened(WidgetEvent e)
{
onWidgetOpened(e);
}
});
for (Iterator it = _loadedPlugins.values().iterator(); it.hasNext();)
{
IPlugin plugin = it.next();
try
{
final PluginLoadInfo pli = getPluginLoadInfo(plugin);
pli.startInitializing();
plugin.initialize();
pli.endInitializing();
} catch (Throwable th)
{
String msg = s_stringMgr.getString("PluginManager.error.initplugin", plugin.getInternalName());
s_log.error(msg, th);
_app.showErrorDialog(msg, th);
}
}
}
/**
* Sets the ClassLoaderListener to notify when archive files containing classes are loaded.
*
* @param listener
* a ClassLoaderListener implementation
*/
public void setClassLoaderListener(ClassLoaderListener listener)
{
classLoaderListener = listener;
}
private void onWidgetOpened(WidgetEvent e)
{
IWidget widget = e.getWidget();
if (widget instanceof ISessionWidget)
{
ISession session = ((ISessionWidget) widget).getSession();
List list = _pluginSessionCallbacksBySessionID.get(session.getIdentifier());
if (null != list)
{
for (int i = 0; i < list.size(); i++)
{
PluginSessionCallback psc = list.get(i);
if (widget instanceof SQLInternalFrame)
{
psc.sqlInternalFrameOpened((SQLInternalFrame) widget, session);
} else if (widget instanceof ObjectTreeInternalFrame)
{
psc.objectTreeInternalFrameOpened((ObjectTreeInternalFrame) widget, session);
}
}
}
}
}
/**
* Retrieve information about plugin load times
*
* @return Iterator over a collection of PluginLoadInfo objects.
*/
public Iterator getPluginLoadInfoIterator()
{
return _pluginLoadInfoColl.values().iterator();
}
private void loadPlugin(Class> pluginClass)
{
PluginInfo pi = new PluginInfo(pluginClass.getName());
try
{
final PluginLoadInfo pli = new PluginLoadInfo();
final IPlugin plugin = (IPlugin) pluginClass.newInstance();
pli.pluginCreated(plugin);
_pluginLoadInfoColl.put(plugin.getInternalName(), pli);
pi.setPlugin(plugin);
_plugins.add(pi);
if (validatePlugin(plugin))
{
pli.startLoading();
plugin.load(_app);
pi.setLoaded(true);
_loadedPlugins.put(plugin.getInternalName(), plugin);
if (ISessionPlugin.class.isAssignableFrom(pluginClass))
{
_sessionPlugins.add(new SessionPluginInfo(pi));
}
}
pli.endLoading();
} catch (Throwable th)
{
String msg = s_stringMgr.getString("PluginManager.error.loadpluginclass", pluginClass.getName());
th.printStackTrace();
s_log.error(msg, th);
_app.showErrorDialog(msg, th);
}
}
private boolean validatePlugin(IPlugin plugin)
{
String pluginInternalName = plugin.getInternalName();
if (pluginInternalName == null || pluginInternalName.trim().length() == 0)
{
s_log.error("Plugin " + plugin.getClass().getName() + "doesn't return a valid getInternalName()");
return false;
}
if (_loadedPlugins.get(pluginInternalName) != null)
{
s_log.error("A Plugin with the internal name " + pluginInternalName + " has already been loaded");
return false;
}
return true;
}
private PluginLoadInfo getPluginLoadInfo(IPlugin plugin)
{
return _pluginLoadInfoColl.get(plugin.getInternalName());
}
/**
* Allows plugins to access each other without imports.
*
* @param internalNameOfPlugin
* Is the accessed plugins internal name returned by IPlugin.getInternalName().
* @param toBindTo
* Is an interface that is to bind against the object that the accessed plugin returns by its
* getExternalService() method.
* @return An Object that may be cast to the toBindTo interface and delegates all calls to the object
* returned by the accessed plugin's getExternalService() method. The method signature of the
* methods in the toBintTo interface and external service object must be identical. This method
* returns null if the plugin can not be found / is not loaded.
*/
public Object bindExternalPluginService(String internalNameOfPlugin, Class> toBindTo)
{
IPlugin plugin = _loadedPlugins.get(internalNameOfPlugin);
if (null == plugin)
{
return null;
}
final Object obj = plugin.getExternalService();
if (null == obj)
{
throw new RuntimeException("The plugin " + internalNameOfPlugin
+ " doesn't provide any external service.");
}
InvocationHandler ih = new InvocationHandler()
{
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
Method m = obj.getClass().getMethod(method.getName(), method.getParameterTypes());
return m.invoke(obj, args);
}
};
return Proxy.newProxyInstance(_pluginsClassLoader, new Class[] { toBindTo }, ih);
}
public IAliasPropertiesPanelController[] getAliasPropertiesPanelControllers(SQLAlias alias)
{
ArrayList ret = new ArrayList();
for (Iterator i = _loadedPlugins.values().iterator(); i.hasNext();)
{
IPlugin plugin = i.next();
IAliasPropertiesPanelController[] ctrls = plugin.getAliasPropertiesPanelControllers(alias);
if (null != ctrls)
{
ret.addAll(Arrays.asList(ctrls));
}
}
return ret.toArray(new IAliasPropertiesPanelController[ret.size()]);
}
public void aliasCopied(SQLAlias source, SQLAlias target)
{
for (Iterator i = _loadedPlugins.values().iterator(); i.hasNext();)
{
IPlugin plugin = i.next();
plugin.aliasCopied(source, target);
}
}
public void aliasRemoved(SQLAlias alias)
{
for (Iterator i = _loadedPlugins.values().iterator(); i.hasNext();)
{
IPlugin plugin = i.next();
plugin.aliasRemoved(alias);
}
}
/**
* @see net.sourceforge.squirrel_sql.client.plugin.IPluginManager#loadPluginsFromList(java.util.Iterator)
*/
@Override
public void loadPluginsFromList(List pluginListIterator)
{
for (String className : pluginListIterator)
{
try
{
loadPlugin(Class.forName(className));
}
catch (Exception e)
{
s_log.error("Unable to load plugin class (" + className + ") from the classpath. "
+ "This plugin was specified by one of the following program arguments : "
+ ApplicationArguments.IOptions.PLUGIN_LIST[0] + " or "
+ ApplicationArguments.IOptions.PLUGIN_LIST[1] + ". Either remove this argument, or ensure "
+ "that the plugin is on the CLASSPATH");
}
}
}
/**
* A plugin implementation that serves only to identify plugins that aren't being loaded, but which are
* still installed, and available to load on startup if the user changes the isLoadedOnStartup attribute.
*
* @author rmmannin
*/
private static class MyPlaceHolderPlugin extends DefaultPlugin
{
private String _internalName = null;
public MyPlaceHolderPlugin(String internalName)
{
_internalName = internalName;
}
/*
* (non-Javadoc)
*
* @see net.sourceforge.squirrel_sql.client.plugin.IPlugin#getAuthor()
*/
public String getAuthor()
{
return "";
}
/*
* (non-Javadoc)
*
* @see net.sourceforge.squirrel_sql.client.plugin.IPlugin#getDescriptiveName()
*/
public String getDescriptiveName()
{
return "";
}
/*
* (non-Javadoc)
*
* @see net.sourceforge.squirrel_sql.client.plugin.IPlugin#getInternalName()
*/
public String getInternalName()
{
return _internalName;
}
/*
* (non-Javadoc)
*
* @see net.sourceforge.squirrel_sql.client.plugin.IPlugin#getVersion()
*/
public String getVersion()
{
return "";
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy