Hydrogen
A plugin for the Eclipse IDE to configure and launch one or more H2 database servers
I am one of those people who cannot come up with ideas for side projects to save their life. Honestly, I’m not really sure how I came up with this idea. The thought of creating an Eclipse plugin most likely came from a tech talk at work, and my team had used H2, but putting them together…?
For the first two-ish years in my professional career I worked exclusively an Eclipse RCP application. It was a fantastic experience, and it allowed me to get very familiar with SWT. A few years ago I stopped working with SWT as frequently, and realized that it would be helpful to have a project to look back on as an SWT-refresher of sorts - something to refresh my memory.
Project Structure
If you take a look at the code, you’ll see the following project structure:
1
2
3
4
5
6
7
8
hydrogen
+ com.avojak.plugin.hydrogen.core
+ com.avojak.plugin.hydrogen.feature
+ com.avojak.plugin.hydrogen.lib
+ com.avojak.plugin.hydrogen.site
+ com.avojak.plugin.hydrogen.target
+ com.avojak.plugin.hydrogen.test
+ com.avojak.plugin.hydrogen.test.report
The core functionality, as the name implies, lives in the .core
module. The .feature
, .site
, and .target
modules used to define the target environment, and how the plugin will be packaged and distributed.
Obviously the .test
module contains the unit tests. The .test.report
module really only contains a pom.xml
file which is used to aggregate code coverage data from the .core
and .test
modules (I’ll touch on this later).
Finally, the .lib
module is a fragment which distributes the H2 JAR. Even though Hydrogen installs with the H2 JAR already packaged, it can be updated later by the user.
Elements
The UI
As a plugin, there are only a few UI elements to consider:
- The toolbar menu
- The Launch Configuration dialog
- The preference dialog
Toolbar
The toolbar contains a single icon with a dropdown menu. The menu emulates the Run Configuration menu that Eclipse users will be familiar with. From the menu, you can launch pre-defined server configurations, or open the menu to create new configurations.
Launch Configuration
This is the most complicated dialog. It contains tabs for each type of server that can be run for H2: Web, TCP, and PostgreSQL. This is where the majority of the SWT work came in. Fortunately, Eclipse provides an AbstractLaunchConfigurationTab
which makes input validation and basic layout and widget creation relatively simple.
Preferences
There is only a single, which lets users use a specific version of H2. As I mentioned earlier Hydrogen comes with the H2 JAR already, however I do not anticipate releasing an update to Hydrogen for every release of H2. Instead, users can simply point to a different JAR on their local file system if they would like na update.
Retrieving the version of the embedded JAR was a fun exercise. To accomplish this, I used a combination of the JarInputStream
and Manifest
classes to inspect the manifest file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static final String IMPLEMENTATION_VERSION = "Implementation-Version";
public Optional<String> getVersion(final String jarPath) {
if (jarPath == null || jarPath.trim().isEmpty()) {
throw new IllegalArgumentException("jarPath cannot be null or empty");
}
try (final JarInputStream jarStream = jarInputStreamFactory.create(jarPath)) {
final Manifest manifest = jarStream.getManifest();
return Optional.ofNullable(manifest.getMainAttributes().getValue(IMPLEMENTATION_VERSION));
} catch (final FileNotFoundException e) {
LOGGER.error("JAR file [" + jarPath + "] not found", e);
return Optional.empty();
} catch (final IOException e) {
LOGGER.error("I/O error while creating stream for JAR file [" + jarPath + "]", e);
return Optional.empty();
}
}
To allow easy mocking during testing, I created a factory class JarInputStreamFactory
which simply wraps the construction:
1
2
3
public JarInputStream create(final String jarPath) throws FileNotFoundException, IOException {
return new JarInputStream(new FileInputStream(jarPath));
}
Behind the Scenes
The real meat and potatoes of Hydrogen lies within HydrogenLaunchConfigurationDelegate
. This is where everything comes together and the H2 servers are launched via the H2 JAR. The class extends AbstractJavaLaunchConfigurationDelegate
which provides a handful of convenient methods.
The primary method here is launch(...)
, which performs the following operations:
- Verify the JVM installation
- Read the Hydrogen user preferences to determine which H2 JAR to use
- Ensure that the H2 JAR specified is present and can be executed
- Create the run configuration (classpath, working directory, etc.)
- Build the program arguments based on what was set in the launch configuration dialog
- Set the JVM arguments (launch headless - we don’t want the user to see an application icon or window appear when H2 is running)
- Run!
Build & Test
One hurdle that I faced early in development was getting accurate code coverage metrics for my unit tests. I landed on JaCoCo because it seemed easy to integrate into a Maven project. I ran into some issues due to the multi-module nature of Hydrogen, but thanks to this fantastic article, I was able to get things running quite nicely. I ended up creating a separate module specifically for an aggregated report (.test.report
).
The pom.xml
for the report module:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<profile>
<id>jacoco</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<configuration>
<excludes>
<!-- Exclusions -->
</excludes>
</configuration>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>report-aggregate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
And in the parent pom.xml
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<profile>
<id>jacoco</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<configuration>
<propertyName>jacoco.agent.argLine</propertyName>
</configuration>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.eluder.coveralls</groupId>
<artifactId>coveralls-maven-plugin</artifactId>
<version>4.3.0</version>
<configuration>
<jacocoReports>
<jacocoReport>${project.basedir}/${project.groupId}.hydrogen.test.report
/target/site/jacoco-aggregate/jacoco.xml</jacocoReport>
</jacocoReports>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
As an added bonus, the JaCoCo report integrates nicely with Coveralls.
Finished Product
- GitHub: https://github.com/avojak/hydrogen
- Eclipse Marketplace: https://marketplace.eclipse.org/content/hydrogen