 | Level: Intermediate Fernando Lozano, Independent consultant
30 Sep 2003 This article shows how to develop a plug-in that embeds the HSQLDB pure-Java relational database server into the Eclipse Workbench. Although not as powerful as DB2 and not as popular as MySQL, HSQLDB (the hypersonic SQL database) can satisfy the needs of a wide range of Java applications, because of its extensibility and low memory/processor requirements.
The hypersonic SQL database, whose official name was later changed to HSQLDB, is a pure-Java embedded relational database server that you can use in stand-alone mode (using direct file access) or in client/server mode, accepting many concurrent users. Although not as powerful as DB2 and not as popular as MySQL, HSQLDB can satisfy the needs of a wide range of Java applications, because of its extensibility and low memory/processor requirements.
HSQLDB is a convenient Java development database because it features a rich subset of Structured Query Language (SQL), and because Java programmers won't need to install a processor, memory, and disk-hungry database server into their development workstation. It's an ideal tool for integration into the Eclipse IDE, providing useful tools for both novice and experienced developers.
This article and the ones to follow in this series will show you how to build a set of Eclipse plug-ins that bring HSQLDB into the Eclipse Workbench. You'll see a real-world example of how to develop such a plug-in, considering
API and user interface (UI) issues, and also how to evaluate alternative ways to bring desired functionality to a user. This article assumes you are using the Eclipse SDK distribution, not the Platform Runtime-Binary plus JDT. The latter would be well suited if the goal were just to develop a regular Java application.
In this series, we'll create and expand the plug-in set using three steps, according to the rationale described in "Levels of Integration" (see the Resources later in this article for a link):
- Launching existing tools from Eclipse, providing easy access to desired pre-existing tools
from Workbench menus
- Exploring how other Eclipse features can be used to add value to the pre-existing set of tools,
improving Java developer productivity
- Rewriting the tools using SWT for seamless integration into the Eclipse
Workbench
Get to know HSQLDB
You can download HSQLDB, including sources and documentation, from
SourceForge (hsqldb.sourceforge.net; see the link in Resources).
This should not be confused with the original SourceForge project, hsql.sourceforge.net, which has been frozen.
The binary distribution is a standard ZIP file, and all you need to do is to unzip it somewhere on your
hard drive. All HSQLDB components -- the database engine, server processes, JDBC driver, documentation, and a handful of
utilities -- are provided in a single JAR package, installed in lib/hsqldb.jar, and about 260 KB. The database engine needs just
170 KB of RAM to run, making it adequate for PDAs, such as Zaurus from Sharp, and the entire
download, including sources and documentation, is small enough to fit on a standard 1.44 MB floppy disk.
You can start the database server and utilities from the command prompt by invoking convenience classes
such as org.hsqldb.Server and
org.hsqldb.util.DatabaseManager, each of which accepts
a small set of command-line
options like "-url" (for remote connections), "-database" (for direct file access), and "-user".
Also accepted is the "-?" option to provide help on valid command-line syntax.
The key to HSQLDB's simplicity is the serialization of SQL statement execution. That is,
although many concurrent users can be connected to the database (when running in server mode), all SQL
statements are placed in a queue and executed one at a time. Thus, there is no need to implement complicated locking and synchronization algorithms. In spite of that, HSQLB implements ACID (Atomicity, Consistency, Isolation, and Durability) semantics. In other words, it is a transactional database, but only at the read uncommitted level, without transaction isolation. HSQLDB was really created for embedded applications, not for the corporate data center.
If you want triggers, aggregated functions, outer joins, views, and other SQL features, they are all there
(something you won't get from most lightweight relational databases). You can implement stored procedures
by adding your Java classes do the HSQLDB classpath. Then you issue a
CREATE FUNCTION statement and you're
done. In fact, many standard SQL functions like SQRT and ABS
are implemented as direct mappings to standard Java classes such as
java.lang.Math.
HSQLDB modes of operation
The HSQLDB engine can be run in many modes to suit different application scenarios:
In-memory mode
All database tables and indexes are kept in memory and never saved to disk.
Before you ask why anyone would
want a database that is lost at application termination, think about having a
local cache for database data that you can query, sort, group, and update using standard SQL statements.
Stand-alone mode
The application creates a database connection using JDBC, and the HSQLDB engine runs inside
the application, accessing the database files directly. There can be no concurrent users (the
application has exclusive access to the
database files), but there are no additional threads nor TCP connection overhead. Stand-alone is the preferred
mode for many embedded applications.
Server mode
This is the standard client/server database configuration similar to other relational databases,
allowing concurrent connections using TCP sockets.
Most developers will want this mode as it allows any JDBC client to connect and query/update tables
while the main application is still running.
Web server mode
HSQLDB can act as a Web server, accepting SQL queries though HTTP,
or can run as a servlet inside any standard Web container, passing though firewalls
or installed on a Web hosting service without involving the provider
support team (and expensive database hosting options). As HTTP is stateless,
there are no transactions in this mode.
HSQLDB database file structure
HSQLDB keeps all table and index data in memory, saving all SQL statements issued into a file
named database.script, which also acts as the transaction log.
When the engine is initialized,
this file is read and all SQL statements are processed, thus recreating the entire database.
During shutdown, the HSQLDB engine will generate a new database.script
file with the minimum set of statements for fast database startup.
Besides the default memory tables, HSQLDB also supports "cached" and "text" tables.
Data for all cached tables is kept in a file named database.data,
while text table data is kept in any delimited text
file (like CSV files) named by the set table source
non-standard SQL statement.
While cached tables provide support for data sets bigger than available RAM,
text tables are a convenient way to import and export data.
Besides the database.script and database.data files, any HSQLDB database may include a
database.properties file, on which the administrator can set many parameters that affect
ANSI SQL compatibility. All database files (except for text table data files) must be kept in
the same directory.
There's no explicit way to create HSQLDB databases. If you ask the engine to open a non-existent
database file (using either server mode -database option or a stand-alone mode JDBC URL),
the file and containing directory will be created. So, if you are sure there was data in that
empty database, check for typing errors.
Let's now start developing the plug-in!
Creating the HSQLDB Eclipse set of plug-ins
Putting an existing application inside a powerful tool like Eclipse is not a trivial task.
Fortunately, both HSQLDB and Eclipse ease the task, since HSQLDB is embeddable
in other applications, and since Eclipse provides a clean, easy-to-understand plug-in infrastructure and a
robust development environment for creating new plug-ins, the PDE. Even if you have no previous exposure to Eclipse plug-in development,
the PDE makes the task very easy. See the articles covering Eclipse basics in the Resources section later in this article.
These instructions assume you are using the Eclipse SDK distribution, not the Platform Runtime-Binary
plus JDT. The latter would be well suited if your goal were just to develop a regular Java application.
You can use your preferred operating system, as we'll only use Java code and no native code at all.
We'll start with the minimum effort to create a useful set of plug-ins and write the
smallest amount of code. Later, in the next parts of this series, we'll see how Eclipse
features can be leveraged to provide added value to HSQLDB.
In this article, we'll focus on the following functionality for our code:
- Start the HSQLDB engine in server mode, so both the user application and an SQL console
(like HSQLDB's own DatabaseManager utility) can run SQL statements at the same time.
- Stop the HSQLDB database cleanly.
- Invoke the DatabaseManager utility so developers can enter SQL statements interactively
from the Workbench.
- Run SQL script files using the HSQLDB ScriptTool utility. Over the years, I have kept many *.sql files in
my project folders, to create database tables and insert test data, and have long wished
for an easy way to run them.
- Configure HSQLDB connection properties, such as TCP port and administrator password.
How will these functions be made available to the Workbench?
The easiest way to accomplish the first three is to provide an actionSet, added to a new top-level menu and
also accessible from its own toolbar. The action to run SQL script files must be tied only to files
with a "*.sql" extension, so this will be an objectContribution, added by the Workbench to the pop-up
menus on any views that display these files. Finally, the connection parameters fit nicely into
a plug-in preferences page, accessible from the Workbench Window menu.
Figure 1 shows the new menu and toolbar, while Figure 2 shows the new entry in the
pop-up menu for the Navigator view, and Figure 3 shows the property page, so you can get
a glimpse of what the first release of our plug-in set will look like.
Figure 1. HSQLDB menu and associated toolbar
Figure 2. Pop-up menu item added to *.sql files
Figure 3. HSQLDB connection properties
Turning HSQLDB into an Eclipse plug-in
The first step in building our set of plug-ins is to package HSQLDB itself as an Eclipse plug-in.
This plug-in will contain only hsqldb.jar and the needed plugin.xml file required
by the Workbench. If you've browsed the standard Eclipse plug-ins directory, you've already found
that JUnit, Xerces, Tomcat, and other popular Java packages are isolated in their own plug-ins,
unchanged, and that all Eclipse specifics are
encapsulated in other plug-ins. This compartmentalization makes it easy to update these third-party tools, which don't necessarily require changes to Eclipse itself. Another benefit is the ease of sharing these
common libraries with many plug-ins.
Open your Eclipse SDK installation and create a new plug-in project; name it hsqldb.core.
(I know the recommended name would be org.hsqldb.core, but I won't pretend to take the HSQLDB
namespace. Perhaps HSQLDB developers reading this will like this idea and recommend it as the "official" Eclipse integration plug-in; in that case the name would probably be changed.) Be sure to select the "Empty Plugin"
option instead of any plug-in template; otherwise, you'll get a useless top-level plug-in class, which
can be safely deleted if you were anxious and already created it.
Copy hsqldb.jar from your HSQLDB installation to the project directory and add it
to the Runtime of the project. You can do this by using the PDE Plugin Manifest Editor
or by simply copying the contents from Listing 1.
Listing 1. plugin.xml manifest file for the hsqldb.core plug-in
<?xml version="1.0" encoding="UTF-8"?>
<plugin
id="hsqldb.core"
name="Hsqldb Core Plug-in"
version="0.0.1"
provider-name="Fernando Lozano (www.lozano.eti.br)">
<runtime>
<library name="hsqldb.jar">
<export name="*"/>
</library>
</runtime>
</plugin>
|
Adding Workbench extensions
Next, create a second plug-in project named hsqldb.ui. This will contain all Eclipse extensions
for the first revision of the plug-in set: an action set containing three actions, an object
contribution associated to *.sql files, and a property page.
Name its main class (Plugin class)
PluginUi, accepting the default
package name hsqldb.ui.
Open the plugin.xml file using the Plugin Manifest Editor, and select the Extensions tab.
Rename the contributed menu to HSQLDB and change the sample action so it displays the label
"Runs HSQLDB Database Manager". Add two other actions to the same actionSet with labels
"Stops HSQLDB database server" and "Starts HSQLDB database server", providing each with a
unique action id and implementation class.
Please note that the actions will be displayed on the menu and toolbar in the reverse order
of creation (that is the reverse order of appearance in the plugin manifest file).
Click the Add button to
add two new extensions using the Extension templates, a pop-up menu and a property page.
The pop-up menu should be associated with the *.sql file pattern, but the property page
fields will be set programmatically.
Figure 4 shows how the Plugin Manifest editor should appear at the end, displaying
all plug-in extensions; Figure 5 displays the properties for the object contribution
(note the filter for *.sql files), and Figure 6 shows the properties for
the "Run SQL Script" action for the file resources pop-up menu.
The icons are in the source code for this article (see Resources), but I'm sure
you know a graphics expert who can draw better ones!
Figure 4. HSQLDB actions on the manifest editor
Figure 5. HSQLDB object contribution on the manifest editor
Figure 6. Run SQL Script action on the manifest editor
Before code can be added to our actions, we need to tell this UI plug-in about the corresponding
core plug-in, otherwise it won't have access to HSQLDB classes. Change to the "Dependencies" page on
the Plugin Manifest Editor and add a plug-in dependency, as shown in Figure 7. Some
dependencies, like "eclipse.ui", were configured automagically by the PDE, while others, like
"org.eclipse.debug.core" will be added when the actions are coded.
If you prefer to edit XML code directly, Listing 2 shows the complete plugin.xml file for
the hsqldb.ui plug-in.
To complete the plug-in setup, add a new Class to the hsqldb.ui package,
and name it HsqldbUtil. This class will contain all code that
deals directly with HSQLDB, keeping the contributed extensions code simple.
Figure 7. hsqldb.ui plug-in dependencies
Listing 2. plugin.xml manifest file for the hsqldb.ui plug-in
<?xml version="1.0" encoding="UTF-8"?>
<plugin
id="hsqldb.ui"
name="Hsqldb Ui Plug-in"
version="0.0.1"
provider-name="Fernando Lozano (www.lozano.eti.br)"
class="hsqldb.ui.PluginUi">
<runtime>
<library name="ui.jar"/>
</runtime>
<requires>
<import plugin="org.eclipse.core.resources"/>
<import plugin="org.eclipse.ui"/>
<import plugin="hsqldb.core"/>
<import plugin="org.eclipse.debug.core"/>
<import plugin="org.eclipse.jdt.launching"/>
<import plugin="org.eclipse.debug.ui"/>
</requires>
<extension
point="org.eclipse.ui.actionSets">
<actionSet
label="Hsqldb"
visible="true"
id="hsqldb.ui.actionSet">
<menu
label="Hsql&db"
id="hsqldbMenu">
<separator
name="dbServerGroup">
</separator>
</menu>
<action
label="Run Hsql &Database Manager"
icon="icons/dbman.gif"
tooltip="Runs the Hsql database manager"
class="hsqldb.ui.actions.HsqldbDatabaseManagerAction"
menubarPath="hsqldbMenu/dbServerGroup"
toolbarPath="dbServerGroup"
id="hsqldb.ui.actions.HsqldbDatabaseManagerAction">
<enablement>
<pluginState
value="activated"
id="hsqldb.ui">
</pluginState>
</enablement>
</action>
<action
label="S&top Hsqldb"
icon="icons/stop.gif"
tooltip="Stops the Hsql database server"
class="hsqldb.ui.actions.HsqldbStopAction"
menubarPath="hsqldbMenu/dbServerGroup"
toolbarPath="dbServerGroup"
id="hsqldb.ui.actions.HsqldbStopAction">
<enablement>
<pluginState
value="activated"
id="hsqldb.ui">
</pluginState>
</enablement>
</action>
<action
label="&Start Hsqldb"
icon="icons/start.gif"
tooltip="Starts the Hsql database server"
class="hsqldb.ui.actions.HsqldbStartAction"
menubarPath="hsqldbMenu/dbServerGroup"
toolbarPath="dbServerGroup"
id="hsqldb.ui.actions.HsqldbStartAction">
<enablement>
<pluginState
value="installed"
id="hsqldb.ui">
</pluginState>
</enablement>
</action>
</actionSet>
</extension>
<extension
point="org.eclipse.ui.perspectiveExtensions">
<perspectiveExtension
targetID="org.eclipse.ui.resourcePerspective">
<actionSet
id="hsqldb.ui.actionSet">
</actionSet>
</perspectiveExtension>
</extension>
<extension
point="org.eclipse.ui.popupMenus">
<objectContribution
objectClass="org.eclipse.core.resources.IFile"
nameFilter="*.sql"
id="hsqldb.ui.SQLScriptFiles">
<action
label="Run SQL Script"
class="hsqldb.ui.popup.actions.HsqldbRunScript"
menubarPath="additions"
enablesFor="1"
id="hsqldb.ui.HsqldbRunScript">
<enablement>
<pluginState
value="activated"
id="hsqldb.ui">
</pluginState>
</enablement>
</action>
</objectContribution>
</extension>
<extension
id="hsqldb.ui.preferences"
point="org.eclipse.ui.preferencePages">
<page
name="HSQLDB Server"
class="hsqldb.ui.preferences.HSQLDBPreferencePage"
id="hsqldb.ui.preferences.HSQLDBPreferencePage">
</page>
</extension>
</plugin>
|
 |
Starting HSQLDB
It's easy to start the HSQLDB engine in server mode using the class
org.hsqldb.Server. As command-line arguments, it gets the database name
(path + base name for database files), TCP port to listen for
connection requests, and a flag telling if it should call
System.exit() at shutdown.
The following command line would be typical of running HSQLDB:
java -cp /opt/hsqldb/hsqldb.jar org.hsqldb.Server -database /tmp/bd -port 9001 -system_exit=true
The above line creates database files /tmp/db.script, /tmp/db.properties, and /tmp/db.data.
We can just use the Server class main method and pass the arguments
as an array of Strings.
But we have to do this inside a new thread; otherwise, we'll lock the entire Workbench. The plugin class,
which is a singleton, keeps a reference to this thread so it can check if there's a server
that started running before trying to connect to it, and also to check if it's actually
running, because any client can
connect and submit the SHUTDOWN statement, terminating the server.
The code in Listing 3 shows the run method for
hsqldb.ui.actions.HsqldbStartAction, and Listing 4 shows
the code for the startHsqldb from
hsqldb.ui.HsqldbUtil, which actually starts the server.
Later, we'll discuss the techniques for managing user feedback.
The most interesting part of this code is how we find a location to keep the database files.
A project named .hsqldb is created, if it does not exist, and inside it the engine will look
for database.script and other database files.
Eclipse provides all plug-ins in a standard directory where any configuration file can be saved.
Such a directory can be obtained by calling
plugin.getStateLocation, but I don't feel that database files are actually
plug-in configuration files. They look more like user data files, and, as such, they
should be inside a project in the developer workspace.
Listing 3. Action to start HSQLDB in server mode, from hsqldb.ui.actions.HsqldbStartAction
public void run(IAction action) {
// check a database was really started by the plug-in
PluginUi plugin = PluginUi.getDefault();
if (plugin.getHsqldbServer() != null) {
((ApplicationWindow)window).setStatus(
"HSQLDB Server already running.");
}
else {
Cursor waitCursor = new Cursor(window.getShell().getDisplay(),
SWT.CURSOR_WAIT);
window.getShell().setCursor(waitCursor);
try {
HsqldbUtil.startHsqldb();
((ApplicationWindow)window).setStatus("HSQLDB Server started.");
}
catch (CoreException e) {
MessageDialog.openError(window.getShell(),
"Hsqldb Plugin",
"Could not create HSQLDB database project.");
e.printStackTrace(System.err);
}
finally {
window.getShell().setCursor(null);
waitCursor.dispose();
}
}
}
|
Listing 4. Code that really starts HSQLDB in server mode, from hsqldb.ui.HsqldbUtil
public static void startHsqldb() throws CoreException {
PluginUi plugin = PluginUi.getDefault();
// finds project local path for database files
IWorkspaceRoot root = PluginUi.getWorkspace().getRoot();
IProject hsqldbProject = root.getProject(".hsqldb");
if (!hsqldbProject.exists()) {
hsqldbProject.create(null);
}
hsqldbProject.open(null);
IPath dbPath = hsqldbProject.getLocation();
final String database = dbPath.toString() + "/database";
// starts a new thread to run the database server
final HsqldbParams params = getConnectionParams();
Thread server = new Thread() {
public void run() {
String[] args = { "-database", database,
"-port", String.valueOf(params.port),
"-no_system_exit", "true" };
Server.main(args);
}
};
plugin.setHsqldbServer(server);
server.start();
}
|
Stopping HSQLDB
To stop the HSQLDB server, all that's needed is a SHUTDOWN command as an SQL statement.
The method stopHsqldb from
hsqldb.ui.HsqldbUtil, shown in Listing 5, does just that. The code for the
corresponding action will be much the same as that described for starting the server, so it won't be listed here.
Exceptions ClassNotFoundException
(from Class.forName) and
SQLException are thrown so that the action code can provide adequate
feedback to the user.
Listing 5. Code to stop the HSQLDB server
public static void stopHsqldb() throws ClassNotFoundException,
SQLException {
PluginUi plugin = PluginUi.getDefault();
HsqldbParams params = getConnectionParams();
// submits the SHUTDOWN statement
Class.forName("org.hsqldb.jdbcDriver");
String url = "jdbc:hsqldb:hsql://127.0.0.1:" + params.port;
Connection con = DriverManager.getConnection(url, params.user,
params.passwd);
String sql = "SHUTDOWN";
Statement stmt = con.createStatement();
stmt.executeUpdate(sql);
stmt.close();
// no need to close a dead connection!
plugin.setHsqldbServer(null);
}
|
Running the HSQL Database Manager
Running the Database Manager utility from HSQLDB is a bit more involved than running the server itself.
While the server was created to be embeddable in other applications, the utility was intended to
be run as a stand-alone application or as a Java applet. Although both modes accept command-line
arguments (or applet parameters),
we do not want the extra overhead of starting a new Java VM just to get a Window
where the user can type SQL statements. Calling the class
static void main(String[] args) directly won't work as expected,
because it would invoke System.exit() and so terminate the
Workbench.
Browsing the org.hsqldb.util.DatabaseManager source code, we find that it could
be easily instantiated and initialized with a JDBC connection, but the required methods are
not public. So we can create a class named
org.hsqldb.util.PatchedDatabaseManager
inside the HSQLDB source tree and use the supplied Ant build script
to generate a new hsqldb.jar containing our patched Database Manager. Just two methods need to
have their visibility changed to public:
void main() and
void connect(Connection con). Using them, Listing 6
shows how the plug-in runs the patched class.
The Database Manager is an AWT application (see Figure 8), which will create its own event thread (independent
of the Eclipse SWT one) and be terminated by closing the Workbench, which calls System.exit().
There's no problem running multiple instances of the utility, except for the lack of ties to the
Workbench window. That's fine for the first revision of our HSQLDB plug-in, but before this
series is finished, we'll have to change it to be an SWT application, hosted as an
Eclipse view.
Figure 8. HSQLDB Database Manager
Listing 6. Starting the patched Database Manager utility
public static void runDatabaseManager() throws ClassNotFoundException,
SQLException {
PluginUi plugin = PluginUi.getDefault();
HsqldbParams params = getConnectionParams();
// creates a connection to the internal database
String url = "jdbc:hsqldb:hsql://127.0.0.1:" + params.port;
Class.forName("org.hsqldb.jdbcDriver");
Connection con = DriverManager.getConnection(url, params.user,
params.passwd);
if (con != null) {
// needed to patch DatabaseManager so it could
// be initialized and use the supplied connection
PatchedDatabaseManager dm = new PatchedDatabaseManager();
dm.main();
dm.connect(con);
}
}
|
Running SQL scripts
The HSQLDB Script Tool reads plain text files and executes the included SQL statements against
a provided JDBC URL. Batches of SQL statements are separated by the go command, and
scripts can also use the print command to write messages from the script.
Developers familiar with other database systems may create scripts containing just SQL statements
delimited by semicolons (;), but this makes HSQLDB run all scripts in a single batch, returning
just the last statement results. You need to include the go command between SQL statement to
receive results from each one.
The easiest way to let the user browse script results is to put then in a console view,
just like Java applications and Ant build scripts invoked from the Workbench. This requires
creating a Java Launch configuration, which in turn creates another Java VM.
As SQL scripts are
short-lived, we can consider the overhead to be acceptable, and if you think the Database Manager
should also be invoked in its own VM, you can use the runScriptTool
method presented in Listing 7 as an example.
Listing 7 is the longest listing in
this first article, and most of it is related to setting up a classpath containing the correct JRE
bootstrap classes (which must be set explicitly) and hsqldb.jar from the hsqldb.core
plug-in. Check the Resources section at the end of this article for more information about the
launch framework provided by Eclipse and extensions provided by the JDT.
To developers familiar with other GUI toolkits, it's not obvious how to get the path for the
selected SQL script file. The IObjectActionDelegate interface,
which is implemented by object contributions that extend resource pop-up menus, receives
on its run method just a reference to the action itself,
just like a top-level menu action, containing no information about the selected resource or the
originating control. The resource reference is actually provided to
selectionChanged method and has to be saved as an instance variable
so it can be used used later -- see Listing 8.
Listing 7. Running the HSQLDB Script Tool
public static void runScriptTool(IFile currentScript) throws CoreException {
PluginUi plugin = PluginUi.getDefault();
// destroys any preexisting configuration and create a new one
ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
ILaunchConfigurationType type = manager.getLaunchConfigurationType(
IJavaLaunchConfigurationConstants.ID_JAVA_APPLICATION);
ILaunchConfiguration[] configurations = manager.getLaunchConfigurations(
type);
for (int i = 0; i > configurations.length; i++) {
ILaunchConfiguration config = configurations[i];
if (config.getName().equals("SQL Script")) {
config.delete();
}
}
ILaunchConfigurationWorkingCopy wc = type.newInstance(null,
"SQL Script");
// constructs a classpath from the default JRE...
IPath systemLibs = new Path(JavaRuntime.JRE_CONTAINER);
IRuntimeClasspathEntry systemLibsEntry =
JavaRuntime.newRuntimeContainerClasspathEntry(
systemLibs, IRuntimeClasspathEntry.STANDARD_CLASSES);
systemLibsEntry.setClasspathProperty(
IRuntimeClasspathEntry.BOOTSTRAP_CLASSES);
//... plus hsqldb.core plugin
IPluginRegistry registry = Platform.getPluginRegistry();
IPluginDescriptor hsqldbCore = registry.getPluginDescriptor(
"hsqldb.core");
ILibrary[] libs = hsqldbCore.getRuntimeLibraries();
String installDir = hsqldbCore.getInstallURL().toExternalForm();
URL hsqldbJar = null;
try {
hsqldbJar = Platform.asLocalURL(new URL(installDir +
libs[0].getPath()));
}
catch(Exception e) {
// ignore URL exceptions
}
IRuntimeClasspathEntry hsqldbEntry =
JavaRuntime.newArchiveRuntimeClasspathEntry(new Path(
hsqldbJar.getPath()));
hsqldbEntry.setClasspathProperty(IRuntimeClasspathEntry.USER_CLASSES);
// sets the launch configuration classpath
List classpath = new ArrayList();
classpath.add(systemLibsEntry.getMemento());
classpath.add(hsqldbEntry.getMemento());
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH,
classpath);
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH,
false);
// current directory should be the script container
IPath dir = currentScript.getParent().getLocation();
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY,
dir.toString());
// gets the path for the selected SQL script file
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME,
"org.hsqldb.util.ScriptTool");
// builds ScriptTool command line
HsqldbParams params = getConnectionParams();
String args = "-driver org.hsqldb.jdbcDriver " +
"-url jdbc:hsqldb:hsql: " +
"-database //127.0.0.1:" + params.port + " " +
"-user " + params.user + " " +
"-script " + currentScript.getName();
if (params.passwd.length() > 0)
args += "-password " + params.passwd + " ";
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS,
args);
// saves the new config and launches it
ILaunchConfiguration config = wc.doSave();
DebugUITools.launch(config, ILaunchManager.RUN_MODE);
}
|
Listing 8. How to get the SQL Script to be executed, from hsqldb.ui.popup.actions.HsqldbRunScript
public void selectionChanged(IAction action, ISelection selection) {
currentScript = null;
if (selection != null) {
if (selection instanceof IStructuredSelection) {
IStructuredSelection ss = (IStructuredSelection)selection;
// as this action is enabled only for a single selection,
// it's enough to get the first element
Object obj = ss.getFirstElement();
if (obj instanceof IFile) {
currentScript = (IFile)obj;
}
}
}
}
|
HSQLDB properties
With most of the functionality of our plug-in set now in place, it's only missing a way to configure
server connection parameters: the TCP port it listens to, administrator user name,
and administrator user password. The TCP port may have to be changed so it won't
conflict with other applications installed on the developer's machine; the user and
password can be changed by SQL statements from any client. These parameters can be
put into a C-like structure (or pascal-like record) class named
HsqldbParams, which is shown in Listing 9.
It also declares constants used by the Plugin Preferences Store and Property Page to
fetch and save the parameters (preferences) values.
Listing 9. Preferences structure for HSQLDB server connection parameters
package hsqldb.ui;
public class HsqldbParams {
// preference names for the plugin
public static final String P_PORT = "serverPort";
public static final String P_USER = "serverUser";
public static final String P_PASSWD = "serverPasswd";
public int port = 9001;
public String user = "sa";
public String passwd = "";
}
|
Unfortunately, the preference page class generated by the PDE New Extension Wizard is a bit
misleading, including a method to set default preference values that won't be used by
the plugin preferences store. The correct place for initializing defaults is the main
Plugin class itself. Otherwise, all preferences that are at their default values will
be returned by the preferences store as 0 or null.
Thus, the preferences page class becomes much simpler, as shown in Listing 10, and the
method initializeDefaultPreferences is added to the
PluginUi class as shown in Listing 11. Note the
default values for each preference are defined by the preferences structure, not the
plugin class nor the preference page class.
Listing 10. Preferences page for HSQLDB plug-in
public class HSQLDBPreferencePage
extends FieldEditorPreferencePage
implements IWorkbenchPreferencePage {
public HSQLDBPreferencePage() {
super(GRID);
setPreferenceStore(PluginUi.getDefault().getPreferenceStore());
setDescription("Connection parameters for the embedded HSQLDB server");
}
public void createFieldEditors() {
addField(new IntegerFieldEditor(HsqldbParams.P_PORT,
"&TCP Port:",
getFieldEditorParent()));
addField(new StringFieldEditor(HsqldbParams.P_USER,
"Administrator &User:",
getFieldEditorParent()));
addField(new StringFieldEditor(HsqldbParams.P_PASSWD,
"Administrator &Password:",
getFieldEditorParent()));
}
public void init(IWorkbench Workbench) {
}
} |
Listing 11. Changed methods from the generated Plugin class
public void shutdown() throws CoreException {
// shuts down the server if running
Thread server = getHsqldbServer();
if (server != null && server.isAlive()) {
try {
HsqldbUtil.stopHsqldb();
}
catch (Exception e) {
e.printStackTrace();
}
}
super.shutdown();
}
protected void initializeDefaultPreferences(IPreferenceStore store) {
super.initializeDefaultPreferences(store);
HsqldbParams params = new HsqldbParams();
store.setDefault(HsqldbParams.P_PORT, params.port);
store.setDefault(HsqldbParams.P_USER, params.user);
store.setDefault(HsqldbParams.P_PASSWD, params.passwd);
}
|
Providing feedback to the user
As the plug-in set provides neither a view nor an editor to the Workbench, there are limited
ways to provide user feedback on the actions. We can use the
status line of the Workbench window, and also an SWT
MessageDialog so that important events (mostly
errors) aren't missed by the user.
The visibility and enablement model for Workbench actions is focused on workspace resource
selection, but most plug-in actions (starting HSQLDB server, stopping it, and starting
the Database Manager) don't depend on resource selection, but rather on whether the server is
running or not running -- conditions that have to be explicitly checked by each action
run method.
Caveat: plugin and action classes are not instantiated until absolutely required to
lower Workbench memory requirements. The manifest file contents are used to present
menu choices and to enable/disable them, but as the HSQLDB server is not a workspace resource,
it can't enable actions. You can, as described for the manifest code in Listing 2,
(see the <enablement> element)
enable/disable an action based on plug-in activation, so at startup just the "Start HSQLDB server"
action is enabled, but after that there's no easy way to, say, disable this action if the
server is already running, and then re-enable it when it's stopped.
Note the code used to put an hourglass cursor on the Workbench window, and
the code used to set the status line message. These are not easy to find in PDE documentation
or Eclipse.org articles.
Final touches
The plugin class overrides the shutdown method so it can
cleanly shut down the server when the Workbench is closed, and we're finished. Of course,
a complete plug-in set would require additional tasks not covered here, such as:
- Package the two plug-ins as a feature, so that all are installed, removed, enabled,
and disabled as a unit.
- Provide help docs for the plug-in itself, and include HSQLDB docs in a format
suitable for the Workbench help manager.
Conclusion
This article showed how to create a set of plug-ins that
bring the HSQLDB database into the Eclipse Workbench, adding the ability to
start and stop the database server, as well as running SQL statements and scripts.
We've seen how the PDE makes creating this plug-in easy, via wizards and
editors for most tasks, leaving the developer with little more than the task
of creating the code that actually implements the desired functionality.
In the next part of this series, you'll learn how to leverage the Workbench features to add value to HSQLDB development, instead of simply
running pre-existing tools.
Download | Name | Size | Download method |
|---|
| os-echsql/hsqldbplugin1.zip | | HTTP |
Resources
- by Jim Amsden's "Levels of Integration" explains how to integrate a tool into the Eclipse Workbench.
- Download the HSQLDB binary and source distribution.
- Download the source code discussed in this article.
- "Developing Eclipse plug-ins" (developerWorks, December 2002) walks though a simple "Hello, World" plug-in creation, without using the PDE wizards and editors.
- "PDE Does Plug-ins" walks though a simple "Hello, World" plug-in creation, but this time teaching how to use PDE wizards and editors.
- "Contributing Actions to the Eclipse Workbench" teaches everything about actions, actionSets, objectContributions, etc.
- "Preferences in the Eclipse Workbench UI" talks about preferences and PreferenceStores.
- "Simplifying Preferences Pages with Field Editors" shows an easier way to create preference pages (which is the one used in this article).
- "Notes on the Eclipse Plug-In Architecture" is another article you should read to gain a better understanding about the Eclipse Platform.
- "Launching Java Applications Programmatically" demonstrates the infrastructure provided by the JDT to launch Java applications similar to running the HSQLDB ScriptTool.
- "Plug a Swing-based development tool into Eclipse" (developerWorks, October 2002) talks about how to integrate Swing and AWT tools into the (SWT based) Workbench.
- "Extend Eclipse's Java Development Tools" (developerWorks, July 2003) walks though the process of creating Eclipse plug-ins using the PDE.
- Visit the HSQLDB SourceForge Project for source code and binary distributions.
- The Hypersonic SQL original project Web site is now discontinued.
-
JFaceDBC is a nice SQL console, built as an Eclipse plug-in.
- Find more articles for Eclipse users in the Open source projects zone on developerWorks. Also see the latest Eclipse technology downloads on alphaWorks.
About the author  | |  | Fernando Lozano is a long-time open source zealot and Java developer, author of the book Java and GNU/Linux (available in Portuguese only). Among other pursuits, he sells services to organizations developing J2EE applications using Eclipse and other open source tools, helps maintain the Portuguese translation of the GNU Project Web site, and is part of the Linux Professional Institute Brazil council. You can reach Fernando at fernando at lozano.eti.br. |
Rate this page
|  |