The Curious Dev

Various coding insights from a curious dev

Easy Wins With java.awt.Desktop

java.awt.Desktop

Recently, I discovered the java.awt.Desktop class (added in Java6) and it’s really helpful to trigger various operating system tasks such as opening a website, a PDF or even opens a file for editing in the operating system’s default editor.

In this post I’ve compiled a few ways to demonstrate the usefulness of the class and hopefully something that’ll help you to achieve something easily too.

The Desktop class is static and you can simply grab an instance with Desktop.getDesktop().

In the instance where your code may or may not run as expected there is a simple utility method Desktop.isSupported() which provides some level of certainty to whether you can utilise this utility and the current point of runtime.

In addition to this, there is another method Desktop.getDesktop().isSupported(Desktop.Action.OPEN) that provides some feedback as to whether the particular supported action is available, think Printing, Emailing etc.

Opening a PDF File

At some point in the past, to programmatically open a PDF file I’ve implemented a somewhat clumsy solution that extracted the install location of Adobe Acrobat or Adobe Reader from the Windows Registry and then called the application via Java’s shell execute ability. The Desktop class provides an alternative and very simple solution:

String fileToOpen = "G:\\Dropbox\\ClimateChange\\2014-03-09_state-of-the-climate-2014_low-res.pdf";
File file = new File(fileToOpen);
Desktop.getDesktop().open(file);

Editing a Text File

Just as easy as opening a file you can also edit a file with the operating system default file association:

String fileToEdit = "G:\\Dropbox\\notes.txt"
File file = new File(fileToEdit)
Desktop.getDesktop().edit(file)

Opening a Web Page

Launching a web page is just as easy:

String urlToOpen = "https://news.ycombinator.com";
URI addr = new URI(urlToOpen);
Desktop.getDesktop().browse(addr);

Summary

All in all these features are hardly forging new boundaries, but they’re certainly convenient and hopefully can save you some time along the way.

Rolling Your Own Podcast Feed

One of the many podcasts I listen to on my daily commute is the Thrasher’s Wheat Radio Hour which offers a little insight into the huge and varied catalog of Neil Young.

I’ve been a fan for many years now and when I discovered this show I immediately wanted to subscribe to the podcast feed in my favourite “podcatcher” Miro. But this was to no avail … I could not find the feed link! The most recent show is merely added to an existing blog post as an update on the original page: http://neilyoungnews.thrasherswheat.org/2012/08/podcast-thrashers-wheat-radio-hour-show.html.

This wouldn’t do, so I decided I could roll my own feed of the show.

After initially writing a reasonably useful regex to parse the blog post for the shows and produce a feed, I then discovered an even easier way. The mp3 files for the show are simply placed in the http://thrasherswheat.org/twradio/ directory and upon browsing there one gets a simple listing of the files.

Adapting my regex to use this was straight forward.

(?!href=")TWR-Episode([0-9][0-9]?.mp3|-[0-9][0-9]?-on-[0-9][0-9]?-[0-9][0-9]?-[0-9][0-9]\.(mp3|wav))(?=">\s)

Next, I needed a way of serving up the feed and the logical solution for me was Grails on Heroku.

To be fair, using Grails to produce a single XML feed is serious overkill, and the produced WAR is nearly 90MB, another lighter solution might be Ratpack, but I’ll delve into that another day. Grails is my favourite web framework of the last few years and rather than optimising for a small WAR file, I am attracted to the quick development cycle on offer.

The basic structure of the app is thus:

  • A FeedFetcherService service that screen scrapes the directory and returns an ordered list of URLs for each show
  • A view at feedFetcher/index.gsp that serves up the XML to the requesting application
  • A FeedFetcherController controller that calls the above service and forwards the data to the view

Note that there are no domain classes, at this point there’s no need to store anything, just build the feed anew upon every request.

Service

class FeedFetcherService {
    //private String endpointUrl = "http://neilyoungnews.thrasherswheat.org/2012/08/podcast-thrashers-wheat-radio-hour-show.html"
    String endpointUrl = "http://thrasherswheat.org/twradio/"

    def checkSource() {
        println "using URL: ${endpointUrl}"

        //retrieve web page text
        String sourceText = new URL(endpointUrl).text

        //extract all links to episodes with regex
        def podcasts = sourceText.findAll(/(?!href=")TWR-Episode([0-9][0-9]?.mp3|-[0-9][0-9]?-on-[0-9][0-9]?-[0-9][0-9]?-[0-9][0-9]\.(mp3|wav))(?=">\s)/)

        def downloadList = processList(podcasts)

        return downloadList
    }

    def processList(def podcasts) {
        //iterate through the list and add to download list if within requested boundaries
        def downloadList = [:]
        podcasts.each { p ->
            String pNbr = p.findAll(/(?!Episode[-]?)([0-9][0-9]?)(?=(-on-|.mp3))/)[0]
            println "filename: ${endpointUrl}${p}"
            assert new Integer(pNbr)
            downloadList.putAt(pNbr, p)
        }

        //sort in reverse episode nbr order
        downloadList = downloadList.sort { p1, p2 -> new Integer(p2.key) <=> new Integer(p1.key) }

        return downloadList
    }
}

So in the above code, the checkSource method grabs the text content returned from http://thrasherswheat.org/twradio/ and runs the regex on it to produce a list of strings that corresponds to the episodes.

The processList method is then called to clean up the URLs into a map and then sort the map in reverse-chronological order.

Those two methods probably represent 75% of the effort.

Controller

class FeedFetcherController {
    def feedFetcherService

    def index() {
        def eps = feedFetcherService.checkSource()

        List<?> epList = []
        eps.each {
            def ep = new Expando()
            ep.key = it.key
            ep.value = it.value
            ep.url = "http://thrasherswheat.org/twradio/" + it.value
            ep.description = it.value.toString().substring(0, it.value.toString().size()-4)
            epList << ep
        }

        [episodes: epList, timeNow: getTimeNow()]
    }

    private String getTimeNow() {
        SimpleDateFormat sdf = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z")
        def cal = GregorianCalendar.getInstance()

        return sdf.format(cal.time)
    }
}

The controller calls the service and then builds a list of Expando objects (use em!) with the current time. This list is then handed to the view.

View

<%@ page import="javax.swing.text.DefaultEditorKit" contentType="text/xml;charset=UTF-8" %><?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>Thrasher's Wheat Radio</title>
        <description>The TWR podcast</description>
        <link>http://neilyoungnews.thrasherswheat.org/2012/08/podcast-thrashers-wheat-radio-hour-show.html</link>
        <language>en-us</language>
        <copyright>Copyright 2013</copyright>
        <lastBuildDate>${timeNow}</lastBuildDate>
        <pubDate>${timeNow}</pubDate>
            <docs>http://blogs.law.harvard.edu/tech/rss</docs>
        <webMaster>scott@dands.ws</webMaster>

        <g:each in="${episodes}" var="ep">
        <item>
            <title>${ep.value}</title>
            <link>http://neilyoungnews.thrasherswheat.org/2012/08/podcast-thrashers-wheat-radio-hour-show.html</link>
            <guid>${ep.key}</guid>
            <description>${ep.description}</description>
            <enclosure url="${ep.url}" length="1" type="audio/mpeg"/>
            <category>Podcasts</category>
            <pubDate>${timeNow}</pubDate>
        </item>
        </g:each>
    </channel>
</rss>

In the interest of speed, I decided to just hack this up and grabbed the XML from another podcast and stripped out all unneccessay data and hardcoded as much as possible. The view simply iterates over the provided list of Expandos to produce the item elements.

In the case of Miro at least, I believe all that is needed is for the GUID value to be unique for each episode and it can track what it already has, what is new etc.

Heroku Deployment

I wont go into detail about the Heroku deployment as it is well documented here and it’s really much like any other Heroku app in that when you’re ready to deploy you:

  • create an Heroku app heroku create
  • create a Procfile
  • commit it to the git repo and then push git push heroku master

And that’s it. Probably less than two hours development time with a little bit of tweakage here and there.

The feed is available here:

murmuring-depths-8428.herokuapp.com/feedFetcher/index

Code

As the code is in a Heroku Git repo, I don’t have a link to share, but maybe I’ll throw it up on Github if someone is interested.

Testing XSL - Part 2

In Part 1 of this Testing XSL series I introduced how XSL can be used and some basic ways to test their correctness.

In this post I’m going to go into further detail with some utility methods that greatly reduce the amount of code you need to test the XSL on a more fine-grained level. They also abstract away any need to worry about DocumentBuilderFactories or Transformers and let you deal with just strings.

One thing about the XML diffs is that they’re quite brittle, if you break one thing it tends to push all the elements out of place and you could end up with hundreds of errors that make it difficult to work out what you broke. You also have to provide a full expected sample of what you expect from the XSL, which might not always be something you want to do just yet.

By writing a simple unit test to execute the XSL with a given input XML you can confirm various pieces of the puzzle are correct, without having to do it all at once.

Utility Methods

I’ve written three new utility methods to make this easy and some accompanying tests to demonstrate and test them, continuing with my “countries” example from Part 1.

First up, countOccurrencesWithXpath which allows one to simply provide an XPATH to count the elements and you can then use this in your test to confirm correctness.

public static Integer countOccurrencesWithXpath(String xmlDocument, String xpath) {
    try {
        Document resultDoc = loadDocumentFromString(xmlDocument)

        // run XPATH on provided XML
        XPath theXpath = XPathFactory.newInstance().newXPath()
        String xpathExp = "count(" + xpath + ")"
        XPathExpression expr = theXpath.compile(xpathExp)
        Number count = (Number) expr.evaluate(resultDoc, XPathConstants.NUMBER)
        println(xpathExp + " = " + count.intValue())

        return count.intValue()
    } catch (Exception e) {
        println(e.getMessage())
        return -1
    }
}

Here we’re simply taking in the XML payload and a provided XPATH and executing it with the count function.

The test to demonstrate this is very simple and demonstrates how easily one could introduce this functionality to an existing project. The XPATH used here //Country/Capital is simply to find just the Capital elements that are under Country elements.

@Test
def void testCountryTransformWithCountOfOccurrences() {
    String result = XslUtils.transformXmlWithXsl(
            XslUtils.loadXmlFromFile("xml/countrylist.xml"),
            XslUtils.loadXslFromFile("xsl/TransformCountries.xsl"))
    assertNotNull(result)
    println result

    //check that the transform has produced the 2 expected Country elements
    assertEquals(2, XslResultUtils.countOccurrencesWithXpath(result, "//Country"))
    
    //check that the transform has produced the 2 expected Capital elements
    assertEquals(2, XslResultUtils.countOccurrencesWithXpath(result, "//Country/Capital"))
}

The next method I’ve written is checkElementValueExists which allows one to easily check for the existence of a particular element and it’s value via a simple XPATH. This could be done via a simple string check on the XML payload string, but tends to be more brittle if namespaces are being used or especially if you’ve got the same common element name at different levels in the document structure (such as ‘Name’ or ‘Value’).

public static boolean checkElementValueExists(String xmlDocument, String xpath, String expectedVal) {
    try {
        Document resultDoc = loadDocumentFromString(xmlDocument)
        boolean found = false

        // run XPATH on provided XML
        XPath theXpath = XPathFactory.newInstance().newXPath()
        String xpathExp = xpath.startsWith("//") ? "${xpath}" : "//${xpath}"
        XPathExpression expr = theXpath.compile(xpathExp)
        NodeList nodeSet = (NodeList)expr.evaluate(resultDoc, XPathConstants.NODESET)

        // loop through nodeSet and check that an expected value exists
        nodeSet.each { Node node ->
            if (expectedVal.equals(node.getTextContent())) {
                found = true
            }
        }

        return found
    } catch (Exception e) {
        println(e.getMessage())
        return false
    }
}

The method essentially executes the provided XPATH and then loops through the results trying to match the expectedVal parameter.

The test to go along with this is trivial also, with a suitable XPATH to find the element you’re checking and the expected value.

@Test
def void testCountryTransformWithElementValue() {
    String result = XslUtils.transformXmlWithXsl(
            XslUtils.loadXmlFromFile("xml/countrylist.xml"),
            XslUtils.loadXslFromFile("xsl/TransformCountries.xsl"))
    assertNotNull(result)
    println result

    //check that one of the expected Capital elements contains 'Canberra'
    assertTrue(result.contains("<Capital>Canberra</Capital>"))  // <-- old way
    assertTrue(XslResultUtils.checkElementValueExists(result, "//Country/Capital", "Canberra")) // <-- better way
    
    //check that we're getting a negative when we should be
    assertFalse(XslResultUtils.checkElementValueExists(result, "//Capital", "Sydney"))
}

Here, both the string check way and the XPATH way are demonstrated.

The third and final test utility method is validateXmlWithSchema which allows one to provide an XML Schema (or more accurately the path to one) against which the provided XML document will be validated.

public static List<String> validateXmlWithSchema(String xmlDocument, String schemaPath) {
    StreamSource sourceXml = new StreamSource(new StringReader(xmlDocument))
    SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
    Schema schema = sf.newSchema(XslUtils.loadXslFromFile(schemaPath))
    Validator validator = schema.newValidator()
    MyErrorHandler errorHandler = new MyErrorHandler()

    try {
        validator.setErrorHandler(errorHandler)
        validator.validate(sourceXml);
    } catch (SAXException e) {
        e.printStackTrace()
    }

    List<String> errorsList = errorHandler.getErrors()
    errorsList.each { println it }

    return errorsList
}

Here’s the “countries” schema I’ve created to use in this example:

<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="Countries">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="Country" type="countryType" minOccurs="1" maxOccurs="unbounded"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:complexType name="countryType">
        <xs:sequence>
            <xs:element name="Name" type="xs:string"/>
            <xs:element name="Capital" type="xs:string"/>
            <xs:element name="TLD" type="xs:string" minOccurs="0"/>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

It is quite basic, but our XML payload can still be tested with the above method, as demonstrated here:

@Test
def void testCountryTransformValidatesToSchema() {
    String schemaLocation = "xsd/Countries.xsd"
    String result = XslUtils.transformXmlWithXsl(
            XslUtils.loadXmlFromFile("xml/countrylist.xml"),
            XslUtils.loadXslFromFile("xsl/TransformCountries.xsl")
    )

    assertNotNull(result)
    assertEquals(0, XslResultUtils.validateXmlWithSchema(result, schemaLocation).size())
}

The validateXmlWithSchema method simply returns a List of Strings for where there are validation errors, if the list is empty then there are no errors.

But if I were to use a different XSL which produces a different result, then the test will fail: Failed Validation Test

Running Tests With Gradle

As I’ve Gradlified this project I can now simply call the gradle wrapper to test with gradlew clean test and a clean looking test report is produced:

Test Report

Code

The update repository is here on Bitbucket. Included are the above demonstrated tests and over the next while I might add some more to test the methods more negatively.

Summary

Hopefully this post has helped to demonstrate how your XSL development task can be made easier with the addition of a very simple test library.

In the next post I intend to go into the process of adding test coverage to your XSLs to see how well your unit tests are exercising them.