This document (like the plugin API itself) is in a very early stage and is lagging
behind the plugin API. Its suggested that you check the existing plugins in CVS to
see how they use the API.
What Licence should I Use?
Please bear in mind when you are reading this section that I am not a lawyer. All
my comments reflect how I believe licencing of free and open source software
works. If you have any questions I suggest you consult a lawyer.
If you're just writing a plugin for your personal use then licencing doesn't matter but if you intend to distibute your plugin (either through the SourceForge release system in the SQuirreL SQL Client project or in any other way) then the licence you select should be compatible with the GNU General Public Licence that SQuirreL is distributed under.
I believe (although as I said I am not a lawyer) that as long as your plugin is dynamically linked into SQuirreL (as it will be because thats how the plugin API works) then any OSI approved licence should be appropriate.
Unless you have a philosophical dislike of the GNU GPL I suggest you release your plugin under it so that I don't wonder if your choice of licence is going to cause me any problems <grin/>.
For more information about licences please see The Free
Software Foundation, the Open Source Initiative
or the
documents at SouceForge about selecting a licence.
The Internal Name
A plugins internal name is used to uniquely identify it and so
must be different to that of any other plugin. It is supplied by
implementing the IPlugin.getInternalName() method. As the
internal name is used to name files and directories it should
only consist of characters valid on the different platforms that SQuirreL
can run on. As well the character "-" is reserved for internal
use. The internal name "app" is reserved for the use of
the core SQuirrel code.
As an example the internal name for the Look and Feel plugin is laf.
To reserve an internal name just email a request to the SQuirreL developers mailing list (details at http://lists.sourceforge.net/lists/listinfo/squirrel-sql-develop) specifying the internal name that you'd like and a quick description of your plugin.
The directory <squirrel_app>/plugins/<internal_name> is reserved for the exclusive use of the plugin. All non-user specific other files required by the plugin should be placed in here.
The directory <user_home>/.squirrel-sql/plugins/<internal_name> is also reserved for the exclusive use of the plugin. All "per-user" files should be placed in here.
IPlugin.initialize() is called as the last part of the SQuirreL startup prior to showing the main window. This is where initialization of the plugin should occur. Please try to keep the amount of code executed here to a minimum as it will affect the startup time of SQuirreL. If possible do plugin initialization when a plugin is first requested by the user.
The configuration file for logging is passed in via the -loggingConfigFile application argument. E.G. -loggingConfigFile=log4j.properties. The standard configuration file specifies the appender SquirrelAppender which writes out to the text file <user-home>\squirrel-sql\squirrel-sql.log
To use logging in your classes you need to import the logging classes:
import net.sourceforge.squirrel_sql.fw.util.log.ILogger; import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
Create a static logging object:
private static ILogger s_log = LoggerController.createLogger(<your-class>.class);
And then you can use the logger:
s_log.error("Error occurred loading Look and Feel: " + lafClassName, ex);
Giving the output:
12468 [main] ERROR net.sourceforge.squirrel_sql.plugins.laf.LAFRegister - Error occurred loading Look and Feel: com.l2fprod.gui.plaf.skin.LinuxLookAndFeel com.l2fprod.gui.plaf.skin.impl.gtk.GtkSkinNotFoundException at com.l2fprod.gui.plaf.skin.impl.gtk.GtkSkin.getDefaultSkinLocation(GtkSkin.java) at com.l2fprod.gui.plaf.skin.LinuxLookAndFeel.(LinuxLookAndFeel.java) at java.lang.Class.newInstance0(Native Method) at java.lang.Class.newInstance(Class.java:254) at net.sourceforge.squirrel_sql.plugins.laf.LAFRegister.installLookAndFeels(LAFRegister.java:289) at net.sourceforge.squirrel_sql.plugins.laf.LAFRegister. (LAFRegister.java:147) at net.sourceforge.squirrel_sql.plugins.laf.LAFPlugin.load(LAFPlugin.java:140) at net.sourceforge.squirrel_sql.client.plugin.PluginManager.loadPlugin(PluginManager.java:243) at net.sourceforge.squirrel_sql.client.plugin.PluginManager.loadPlugins(PluginManager.java:206) at net.sourceforge.squirrel_sql.client.Application.startup(Application.java:122) at net.sourceforge.squirrel_sql.client.Main.main(Main.java:41)
net.sourceforge.squirrel_sql.fw.sql.DatabaseObjectTypeThe Object tree displays a popup menu when the user right-clicks (ctrl-click on Mac OS) on an object in the tree. The SQuirreL API that allows plugins to add items to this menu for all objects (or objects of a specific type) is found in the IObjectTreeAPI interface for which an implementation can be obtained as follows:
IObjectTreeAPI api = session.getObjectTreeAPIOfActiveSessionWindow();You can find examples in the existing plugins where this is done in the method:
PluginSessionCallback sessionStarted(ISession session);The sessionStarted method is called on all loaded plugins each time a new database session window is created (by the user clicking the "Connect" button while selecting an alias to connect to). The net.sourceforge.squirrel_sql.client.plugin.PluginManager class is responsible for providing this notification to the loaded plugins.
Some useful methods for adding menu items in the IObjectTreeAPI interface are:
void addToPopup(DatabaseObjectType dboType, Action action); void addToPopup(DatabaseObjectType dboType, JMenu menu);These methods both take a DatabaseObjectType to determine which type of object to add the menu item to. DatabaseObjectType defines all public constants that are used to identify types of objects found in the object tree. For example there are:
DatabaseObjectType.FOREIGN_KEY DatabaseObjectType.FUNCTION DatabaseObjectType.INDEX DatabaseObjectType.PRIMARY_KEY DatabaseObjectType.PROCEDURE DatabaseObjectType.SCHEMA DatabaseObjectType.SEQUENCE DatabaseObjectType.TABLE DatabaseObjectType.UDT DatabaseObjectType.VIEWThese are the ones most commonly used by plugins, but there are still more. Any object shown in the tree has a corresponding DatabaseObjectType constant that represents it type.
IGlobalPreferencesPanel.initialize(IApplication) or ISessionPropertiesPanel.initialize(IApplication, ISession) are called so that the options panel can initialise itself.
String getTitle() is called to retrieve the title for the panel.
String getHint() is called to get the tooltip hint for the panel.
Component getPanelComponent() is called to get the component that is actually placed in the dialog.
void applyChanges()
is called when the user presses the OK button in the dialog. You should save your changes at this point.ISQLExecutionListener specifies one method:
public String statementExecuting(String sql)
This method is called for every statement to be executed in the SQL panel. E.G. If the user enters the following in the SQL panel:
select * from table1; select * from table2
and requests them both to be executed then statementExecuting() will be called twice. Once with "select * from table1" passed and the second time with "select * from table2".
Whatever you return from the method (allowing for any changes that other plugins may make) is what will be executed. If you return null then no other listeners will be called and the statement will not be executed.
This would be useful for macro expansion etc.
This is an example of the XML file that stores JDBC driver configuration information.:
<Beans> <Bean Class="net.sourceforge.squirrel_sql.fw.sql.SQLDriver"> <jarFileName/> <url>jdbc:postgresql:<//host>:<port>/<database></url> <identifier Class="net.sourceforge.squirrel_sql.fw.id.UidIdentifier"> <string>-7</string> </identifier> <name>PostgreSQL</name> <driverClassName>org.postgresql.Driver</driverClassName> <usesClassPath>true</usesClassPath> </Bean> </Beans>
The following example shows how two javabeans can be saved to an XML file.
XMLBeanWriter wtr = new XMLBeanWriter(bean1); wtr.addToRoot(bean2); wtr.save("javabeans.xml");
This example shows the loading of javabeans from an XML File:
XMLBeanReader rdr = new XMLBeanReader(); rdr.load("javabeans.xml", getClass().getClassLoader()); for (Iterator it = rdr.iterator(); it.hasNext();) { final Object Bean = it.next(); ... }
Because the XMLBeanReader has to instantiate the javabean it needs to know the class loader that it used for the javabean's class files. As plugins are loaded by a custom class loader this would normally be the class loader used for the plugin classes.
The processing supplied by the classes in net.sourceforge.squirrel_sql.fw.xml is fairly rudimentary and has some limitations. Chief amongst them is that is has no concept of multiple references to the same object, it will save and restore multiple objects in this case.
The properties of the javabean to be saved/restored can be builtin types, instances of String or other javabeans. If you have a property that doesn't fit this list then you can write a wrapper class to turn it into a javabean. See the package net.sourceforge.squirrel_sql.fw.util.beanwrapper for some examples.
Array properties are handled but currently not for builtin types nor String objects. For these you need to use a wrapper class.
To add a tab to the Table tabbed panel (the one displayed when you select a table in the object tree) use the method ISession.addTablePanelTab(ITablePanelTab). For the Procedure tabbed panel use the method ISession.addProcedurePanelTab(IProcedurePanelTab).
You should create a class that implements either net.sourceforge.squirrel_sql.client.session.objectstree.tablepanel.ITablePanelTab or net.sourceforge.squirrel_sql.client.session.objectstree.procedurepanel.IProcedurePanelTab in order to create a tab. The methods getTitle() and getHint() define the title and the flyover hint respectively. Override getComponent() to provide the actual component to be displayed in the tab (remember to wrap it in a javax.swing.JScrollPane if the contents could be larger than the tab).
ITablePanelTab.setTableInfo(ITableInfo) will be called whenever the currently selected table changes. For the procedure tab IProcedurePanelTabsetProcedureInfo(IProcedureInfo) will be called when the procedure changes. For performance reasons please don't refresh the panel at this point. Use select() for this. select() is called whenever the your tab is to be displayed. Remember that this could be called multiple times without a different table or procedure being selected in the object tree so you only need to refresh your component the first time it is called.
As a convenience the classes net.sourceforge.squirrel_sql.client.session.objectstree.tablepanel.BaseTableTab and net.sourceforge.squirrel_sql.client.session.objectstree.procedurepanel.BaseProcedureTab have been provided. You would normally use these classes (which implement ITablePanelTab and IProcedureTab) as the base for any tabs you create.
BaseTableTab and BaseProcedureTab provides the methods getSession(), and getTableInfo()/getProcedureInfo(). They also provide the method refreshComponent which will be called the first time a tab is displayed for a table or procedure and is the appropriate place to load the information to be displayed.
void addResultTabListener(IResultTabListener lis); void removeResultTabListener(IResultTabListener lis);
IResultTabListener gives you:
void resultTabAdded(ResultTabEvent evt); void resultTabRemoved(ResultTabEvent evt); void resultTabTornOff(ResultTabEvent evt); void tornOffResultTabReturned(ResultTabEvent evt); /** Not yet implemented*/