Looking for contributors for my incomplete Java-Gnome Bazaar branches

I’ll be unavailable for a period of time, so my Java-Gnome development will halt during that period. As a result I’m looking for contributors to continue my incomplete branches.

Vte: http://research.operationaldynamics.com/bzr/java-gnome/hackers/serkan/vte/
VTE library provides a terminal widget which powers Gnome Terminal and other GTK+ terminal emulators. The purpose of this branch to add coverage of VTE API.

Sexy: http://research.operationaldynamics.com/bzr/java-gnome/hackers/serkan/sexy/
Libsexy provides additional widgets for GTK+. The purpose of this branch to add coverage of widgets provided by Libsexy.

Gerrorcode: http://research.operationaldynamics.com/bzr/java-gnome/hackers/serkan/gerrorcode/
Current implementation of GlibException doesn’t capture error code and error domain associated with a GError. The purpose of this branch is to add necessary native and Java bits to provide those these two properties.

Linkbutton: http://research.operationaldynamics.com/bzr/java-gnome/hackers/serkan/linkbutton/
LinkButton is a widget providing an HTML anchor like link. The purpose of this branch is to cover LinkButton.

And thanks in advance to people who take over the development of the branches. And special thanks to Andrew Cowie who helped throughout my Java-Gnome development.

Writing a Last.FM REST client with Jersey and Java-Gnome

This tutorial will start with a sample top artists (i.e favorite artists) file from http://ws.audioscrobbler.com/2.0/?method=user.gettopartists&user=Firari&api_key=b25b959554ed76058ac220b7b2e0a026. It will follow several steps to write a dynamic client GTK UI for the topartists service. Project depends on java-gnome (4.0.12+), jsr311-api (aka jax-rs), jersey-core and jersey-client libraries. Note that there’s a Java library for accessing Last.FM web services available at http://www.u-mass.de/lastfm Project files can be downloaded as a tarball.

Step 1: Generating XML schema from the sample XML

I downloaded a sample file from the the given URL. I used Trang to reverse engineer the XSD from XML. The command line tool just takes 2 arguments, the input xml and the output xsd files. Here’s the generated XSD file.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
  <xs:element name="lfm">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="topartists"/>
      </xs:sequence>
      <xs:attribute name="status" use="required" type="xs:NCName"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="topartists">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" ref="artist"/>
      </xs:sequence>
      <xs:attribute name="type" use="required" type="xs:NCName"/>
      <xs:attribute name="user" use="required" type="xs:NCName"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="artist">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="name"/>
        <xs:element ref="playcount"/>
        <xs:element ref="mbid"/>
        <xs:element ref="url"/>
        <xs:element ref="streamable"/>
        <xs:element maxOccurs="unbounded" ref="image"/>
      </xs:sequence>
      <xs:attribute name="rank" use="required" type="xs:integer"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="name" type="xs:string"/>
  <xs:element name="playcount" type="xs:integer"/>
  <xs:element name="mbid" type="xs:string"/>
  <xs:element name="url" type="xs:anyURI"/>
  <xs:element name="streamable" type="xs:integer"/>
  <xs:element name="image">
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base="xs:anyURI">
          <xs:attribute name="size" use="required" type="xs:NCName"/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
</xs:schema>

Step 2: Generating data classes for mapping the xml

xjc (JAXB Binding Compiler) is used to generate the classes in generated package. Jersey can utilize JAXB to map the result XML to data classes.

Step 3: Writing code to query Last.FM web service

public static Lfm queryTopArtists(String userName) {
	final Client client;
	final WebResource webResource;
	final MultivaluedMap queryParams;
	final Lfm result;

	client = Client.create();
	webResource = client.resource("http://ws.audioscrobbler.com/2.0");
	queryParams = new MultivaluedMapImpl();
	queryParams.add("method", "user.gettopartists");
	queryParams.add("user", userName);
	queryParams.add("api_key", "b25b959554ed76058ac220b7b2e0a026");
	result = webResource.queryParams(queryParams).get(Lfm.class);
	return result;
}

Here we’re building the HTTP request along with the parameters in the query string. And finally we’re calling the service and mapping the result to Lfm class which corresponds to XML’s root element lfm.

Step 4: The GTK+ GUI

Here were initializing the table and its data model.


/*
 * Initialize the table with its DataColumn's.
 */
model = new ListStore(new DataColumn[] { rank = new DataColumnString(),
		artistImage = new DataColumnPixbuf(),
		artist = new DataColumnString(),
		playCount = new DataColumnString(),
		percent = new DataColumnInteger() });
view = new TreeView(model);

Here we’re creating the view columns and binding their properties to data model. Note that were’re binding 2 properties of the CellRendererProgress to different columns in the data model.

/*
 * Create TreeViewColumns and bind the DataColumn's to their properties.
 */
vertical = view.appendColumn();
vertical.setTitle("Rank");
rendererText = new CellRendererText(vertical);
rendererText.setText(rank);

vertical = view.appendColumn();
rendererPixbuf = new CellRendererPixbuf(vertical);
rendererPixbuf.setPixbuf(artistImage);

vertical = view.appendColumn();
vertical.setTitle("Artist");
rendererText = new CellRendererText(vertical);
rendererText.setText(artist);

vertical = view.appendColumn();
vertical.setTitle("# of times played");
rendererProgress = new CellRendererProgress(vertical);
/*
 * It's nice that in GTK+ we can bind multiple DataColumn's to
 * properties of a single TreeViewColumn.
 */
rendererProgress.setText(playCount);
rendererProgress.setValue(percent);

Here we’re querying the service (i.e calling the utility method we wrote) and populating the table. The image data will be fetched asynchroniously by AsyncImageLoader which is a subclass of Thread (Its code wil follow)

/*
 * Query Last.FM user.gettopartists method JAX-RS handles the Webservice
 * call and JAXB handles the unmarshalling of the XML response.
 */
result = LastFMUtil.queryTopArtists("Firari");

/*
 * Add the properties of Artist items as TreeView rows.
 */
if (result.getStatus().equals("ok")) {
	final List<Artist> topArtists = result.getTopartists().getArtist();
	/*
	 * Determining maximum playcount from top of the list.
	 * It will be used to calculate the percentage of the ProgressBar's.
	 */
	maxPlayCount = topArtists.get(0).getPlaycount().floatValue();
	for (final Artist artistItem : topArtists) {
		final TreeIter row = model.appendRow();
		model.setValue(row, rank, artistItem.getRank().toString());
		/*
		 * Asynchronously load the image data and set it as the image
		 * column. The first image URL is the "small" one.
		 */
		new AsyncImageLoader(model, row, artistImage, artistItem.getImage().get(0).getValue()).start();
		model.setValue(row, artist, artistItem.getName());
		model.setValue(row, playCount, artistItem.getPlaycount().toString());
		model.setValue(row, percent, Math.round(artistItem.getPlaycount().intValue() / maxPlayCount * 100));
	}
}

Here’s the code for AsyncImageLoader.

class AsyncImageLoader extends Thread {
	private ListStore model;
	private TreeIter row;
	private DataColumnPixbuf artistImage;
	private String url;

	public AsyncImageLoader(ListStore model, TreeIter row,
			DataColumnPixbuf artistImage, String url) {
		super();
		this.model = model;
		this.row = row;
		this.artistImage = artistImage;
		this.url = url;
	}

	@Override
	public void run() {
		try {
			/*
			 * Fetch the image data and set it as the image column of the
			 * specified row.
			 */
			URL artistImageURL = new URL(url);
			URLConnection artistImageConnection = artistImageURL
					.openConnection();
			DataInputStream in = new DataInputStream(artistImageConnection
					.getInputStream());
			byte[] artistImageData = new byte[artistImageConnection
					.getContentLength()];
			in.readFully(artistImageData);
			/*
			 * The image will have 32 pixels height.
			 */
			model.setValue(row, artistImage, new Pixbuf(artistImageData, -1,
					32, true));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

Here’s a screenshot of the running application.

TopArtists

The image will have 32 pixels height.

Introducing spellchecking support for TextView widget in Java-gnome

I was working on spellcheckers in Linux for quite a long time. My interest comes from another project that I’m involved in,  Zemberek, a Turkish NLP library which also includes a spellchecker. For Linux part, I was involved in developing its DBus interface as well as porting Enchant plugin to this new interface. I’m also maintaining Gentoo ebuilds for Zemberek packages and helping with Enchant package.

As a result, I gained experience with spellcheckers. At first, I started investigating GtkSpell to add bindings for Java, but I was unable to created usable .defs file from the headers. And after investigating Python bindings and seeing that it was hand-written, I examined another possibility, LibSexy. I started implementing LibSexy Java bindings which is still not complete. And main motivation was SexySpellEntry included in the library. I unfortunately hit a bug with text segmentation with it along with another bug causing a critical error (which ends up in an unchecked Exception in Java) which occurs in GtkSpell as well. (See Gentoo bugs #270179 and #270177) Anyway, later on Andrew helped to start-up the GtkSpell coverage with hand-crafted .defs file.

After a few weeks of development spelling branch is now merged to mainline which will make its way into 4.0.12 release. During the development I discovered that GtkSpell has a fallback mechanism for language of the spellchecker. The order is

  • lang peremeter passed to functions
  • LANG environment variable
  • English if none of the above is set.

Announcing Libnotify Java bindings

After 2 ½ months of work libnotify branch finally made its way to mainline. It’s my biggest contribution to Java-Gnome project. Again thanks to Andrew, Guillaume and Vrexio for their help and aid in the development and the testing. It will close a more than 3 year old ticket reported to Galago Project.

Bleeding-edge testers may grab the mainline branch and others may wait until next version (4.0.12) is released.

Libnotify Java bindings – Adding actions

Java-Gnome bindings doesn’t support callbacks at the moment. It can generate code for signals only. To add action callbacks, I reimplemented it as action signal. Here’s an example code to show how to add signals:

notification.addAction("a", "Action 1.a", new Notification.Action() {

    public void onAction(Notification source, String action) {
        System.out.println("1.a");
    }

});

The implementation currently has one drawback. Signals are not disconnected properly. This causes two issues. One, signals aren’t disconnected if actions are removed with clearActions(). Two, if actions with same actionIDs are added signals for all are triggered. Andrew will be adding support for signal disconnection to java-gnome and I’ll be using it in libnotify branch.

Bringing Libnotify to Java

I’ve been contributing to java-gnome for sometime. It was generally build system and test improvements with little bits of coverage addition that was merged to mainline for now. Although I began libnotify branch later than other two (libsexy and vte) the branch improved way faster than the others. The Bazaar branch has nearly full coverage of the underlying API.

The example included shows a critical low-battery warning with gnome-power-manager icon sitting in the notification area (Assuming you have it installed).

low-battery

I’ll be implementing the missing action covarage now and try to make it to our next release 4.0.12. And of course thanks goes to our lead Andrew Cowie for his help in development and Guillaume Mazoyer for testing it and providing feedback.

Hello World

After more than 2 years of blogging in Turkish, here’s my “Hello World” post in English.