Archive

Archive for the ‘code’ Category

Tailing Multiple Logs with Cygwin

August 14, 2013 1 comment

I just joined a new project at work building a workflow system using the Documentum successor Alfresco and its Business Process Management engine Activiti. When the project dev lead gave me a tour of developing for and using the system, I noticed that he opened Cygwin bash shells to tail three different Alfresco log files to look for errors. If you’re not of the software dev persuasion but are trying to make sense of this post for some reason, let me try to explain. Tail is a standard UNIX tool to show the end of a file from the command prompt, and using the -f option dynamically updates the display when new stuff gets added to the file. Thus tailing logs allows you to see the under-the-hood output from your software so you can figure out what the heck is really happening.

I realized that I would lose my mind tailing 3 different log files in 3 different Cygwin shells constantly, and immediately set out to make this process easier.

The first problem is that Alfresco makes new log files for each day, and uses the date in the filename. Thus the tail command needs the smarts to tail today’s log using the date. Here’s my bash script to tail Alfresco’s stdout log.

#!/bin/bash
today=`date +%Y-%m-%d`
tail --lines=200 -f /cygdrive/c/Alfresco/tomcat/logs/alfrescotomcat-stdout.$today.log

For this I want to run a single Windows batch file that kicks off 3 separate Cygwin shells for tailing the logs. That batch file is simple:

start /d "C:\cygwin64" Cygwin-tail-localhost.bat
start /d "C:\cygwin64" Cygwin-tail-stdout.bat
start /d "C:\cygwin64" Cygwin-tail-stderr.bat

Now comes the slightly tricky part, the batch files that kick off the Cygwin shells. Here’s what the standard Cygwin.bat script looks like:

@echo off
C:
chdir C:\cygwin64\bin
bash --login -i

I tried appending to the above bash command to run my ~/.tail-*.sh script, but no matter what I tried I could not customize the title of the window. This gave me 3 windows that looked the same and I couldn’t tell which window displayed which log file. So I abandoned the bash command and switched to Google’s mintty terminal emulator, and everything came together nicely. Here’s what Cygwin-tail-stdout.bat looks like:

@echo off
C:
chdir C:\cygwin64\bin
start mintty.exe -s 120,60 -t stdout /bin/bash -l -e './tail-stdout.sh'

Now I click on one icon in my Start menu and instantly 3 bash consoles appear tailing Alfresco’s logs. There are probably smarter ways to do this, but I’m pretty happy with this approach.

Categories: code Tags: , ,

Programming Fonts

October 26, 2011 1 comment

On the way in this morning I was listening to the most recent episode of the This Developer’s Life podcast which is all about fonts. I decided to spend a few minutes this morning sprucing up the fonts on my Windows XP desktop. The first thing I found was the ClearType Tuner PowerToy from Microsoft . Microsoft developed ClearType ages ago, but it was disabled by default on XP (enabled on Vista and 7). I turned that on and instantly everything looked better. Highly recommended if you still have XP.

Then I set about finding a better monospaced font in Eclipse than Courier. I found a number of suggestions for Consolas, a font Microsoft developed for Visual Studio. Download it if you don’t already have it. Other popular suggestions were DejaVu Mono and Andale Mono. Give them a try.

Categories: code Tags:

Patch Accepted

April 14, 2011 1 comment

Woo hoo! My patch to the maven-scala-plugin has been accepted!

That means that my GitHub pull request was accepted into the main repository. Users can get the fix by pulling down the code and building the plugin. The fix will be included in the next version of the plugin, which I will be sure to mention here!

Categories: code Tags: , ,

maven-scala-plugin

April 1, 2011 Leave a comment

Continuing from my last post: With the Scala script written, I wanted to integrate it into our Maven build as an optional step. But this didn’t go so well.

When running mvn scala:script with a link to my script file in the configured pom.xml, I got a Fatal Error. I also couldn’t get a simple helloWorld script to run, as a separate file or scripted right into the pom.xml file. This is the error I got:


[INFO] ------------------------------------------------------------------------
[ERROR] FATAL ERROR
[INFO] ------------------------------------------------------------------------
[INFO] scala/ScalaObject
scala.ScalaObject
[INFO] ------------------------------------------------------------------------
[INFO] Trace
java.lang.NoClassDefFoundError: scala/ScalaObject
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)
at java.lang.ClassLoader.defineClass(ClassLoader.java:616)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
at org_scala_tools_maven.ScalaScriptMojo.runScript(ScalaScriptMojo.java:185)
at org_scala_tools_maven.ScalaScriptMojo.doExecute(ScalaScriptMojo.java:154)
at org_scala_tools_maven.ScalaMojoSupport.execute(ScalaMojoSupport.java:340)
at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:490)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:694)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalWithLifecycle(DefaultLifecycleExecutor.java:556)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:535)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:387)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:348)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:180)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:328)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:138)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:362)
at org.apache.maven.cli.compat.CompatibleMain.main(CompatibleMain.java:60)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315)
at org.codehaus.classworlds.Launcher.launch(Launcher.java:255)
at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430)
at org.codehaus.classworlds.Launcher.main(Launcher.java:375)
Caused by: java.lang.ClassNotFoundException: scala.ScalaObject
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
... 33 more
[INFO] ------------------------------------------------------------------------

The maven-scala-plugin couldn’t even find the ScalaObject class, which is analagous to Java Object. Of course I googled the heck out of this error and found a few people suffering the same problem, but no one posted a solution. Then I found maven-scala-plugin’s repo on GitHub and discovered that this issue has been reported but not yet fixed. So I decided to download the source code and take a look.

Here’s what I found, in ScalaScriptMojo.java:

    private URLClassLoader createScriptClassloader(File scriptDir,
            Set<String> classpath) throws MalformedURLException {
        List<URL> urls = new ArrayList<URL>();

        // add the script directory to the classpath
        urls.add(scriptDir.toURI().toURL());

        for (String string : classpath) {
            urls.add(new URL("file://" + string));
        }

        URLClassLoader loader = new URLClassLoader(urls.toArray(new URL[urls
                .size()]), getClass().getClassLoader());
        return loader;
    }

That seemed like a pretty hinky way to me to send files to the URLClassLoader. The code that generates the Set of classpath Strings uses File.getCanonicalPath() to create the Strings. Thus they are platform-dependent Strings, and on a Windows box you can’t use them to create a valid File URL by prepending “file://” to them because the path separators are “\”. I assume that the plugin contributors are on Linux machines and couldn’t replicate the problem to fix it.

Like all the best bug fixes, this one was a one-liner:

for (String string : classpath) {
    urls.add(new File(string).toURI().toURL());
}

Now the URLs will be correct no matter what platform you’re on.

I forked the maven-scala-plugin repository on GitHub and checked in my fix. I was waiting to post this entry until my Pull Request was accepted by the repo owner, but I guess he is too busy working on other efforts. Until the request is accepted, please check out my forked repo.

Categories: code Tags: , ,

Scala script

March 31, 2011 Leave a comment

In my day job recently I found a way to sneak some Scala code into the project. I developed a datamodel module containing POJOs and JiBX bindings to marshal Objects into XML documents conforming to our XML schema. Some elements were constrained by enumerations, located either in the schema documents themselves or elsewhere in SQL dataload scripts. I stored these enumerations as properties files, so they could be easily edited when enumeration values changed.

Obviously this solution is not perfect. It violates the DRY (Don’t Repeat Yourself) principle, and enumeration value updates are likely to be forgotten. I decided to write a script that pulls the enumeration values out of their original locations and generates the properties files fresh, to ease maintainability. Scala includes the ability to run as a script, and its XML support is a lot better than Java’s, so I picked it.

Here’s an edited version of the script:

 import io.Source
 import java.io.{FileWriter, PrintWriter}
 import xml.{Node, Elem, XML}
 
 /**
  * This script will extract enumerations from schema and dataload files and write corresponding properties files.
  */
 
 val schemaDir = "C:\\Schemas\\ixm-ws-5.5\\xsd\\"
 val outputDir = "C:\\testharness\\datamodel\\src\\conf\\"
 
 // Get the enumerations out of jxdm.xsd
 var xsd = XML.loadFile(schemaDir + "subset\\jxdm\\3.0.3\\jxdm.xsd")
 
 extractEnumeration(xsd, "ActivityType.properties", "element", "ActivityTypeText", {node => (node \ "@value").text})
 extractEnumeration(xsd, "DocumentStatus.properties", "element", "StatusText", {node => (node \ "@value").text})
 
 // Get the enumerations out of ansi-nist.xsd
 xsd = XML.loadFile(schemaDir + "subset\\niem\\ansi-nist\\2.0\\ansi-nist.xsd")
 
 extractEnumeration(xsd, "ScaleUnits.properties", "simpleType", "SLCCodeSimpleType", {node => (node \\ "documentation").text})
 
 def extractEnumeration(xsd: Elem, propFileName: String, elementType: String, elementName: String, p: (Node) => String) {
   val out = new PrintWriter(outputDir + propFileName)
   xsd \\ elementType filter(node => (node \ "@name").text == elementName) foreach{(element) =>
     element \\ "enumeration" foreach{(enumeration) =>
       val value = (enumeration \ "@value").text
       val key = p(enumeration).trim.replace(" ", "\\ ")
       out.println(key + "=" + value)
     }
   }
   out.flush
   out.close
 }

The neatest part about this code is the last parameter to the extractEnumeration function: a function converting a Node to a String. Some of the enumeration values appear in nodes with the attribute “value” and some appear in nodes called “documentation”. I created closures that extracted the text out of each type of node and sent those functions as parameters to the extractEnumeration method. In Java I’d have to create a switch statement for each type of enumeration value extract and sent in a parameter to indicate which option to use. This way, when a new value extraction function is needed, we don’t have to touch the extractEnumeration function at all. I could have assigned the two closures to objects and reused them for all the extractEnumeration calls, but I thought this way made the code a little more readable.

My next idea for this was to integrate this script as an optional step in our maven build. But this didn’t go so well. Full details in the next post!

Categories: code Tags:

Book Review: The Passionate Programmer by Chad Fowler

January 11, 2011 Leave a comment

The Passionate Programmer cover image

The Passionate Programmer is actually a second edition book; the first edition was entitled My Job Went To India (And All I Got Was This Lousy Book): 52 Ways To Save Your Job. Big improvement already!

The Passionate Programmer’s subtitle is Creating A Remarkable Career in Software Development. Through 53 short chapters, Chad Fowler inspires readers to do better and go further in our careers. Chad was a jazz saxophonist before making a change to software and his book illustrates many connections between the two fields. One point that stuck with me is practicing. Musicians obviously have to practice a lot before they get on stage and perform. Chad says developers should practice as well, before getting on the “stage” of delivering for a customer. He recommends doing your own studying, such as learning regular expressions or APIs that you are unfamiliar with, to increase your chops. Open source project work is an excellent way to gain experience implementing features, studying the code of other contributors of the project, and getting feedback by having your contribution reviewed.

The book is divided into five parts: Choosing Your Market; Investing In Your Product; Executing; Marketing… Not Just For Suits; and Maintaining Your Edge. Choosing Your Market focuses on planning your career by taking a high level view of the landscape and positioning yourself for maximum benefit. Investing In Your Product gives advice on how to improve your skills. Executing talks about the daily grind and how to make work more productive and enjoyable. Marketing is pretty self explanatory, giving some great advice on communicating how great you are to others. Maintaining Your Edge tells you how to avoid becoming a one hit wonder.

One topic that resonated with me was Chapter 36: Being Present. Chad talked about his time as CTO of a software development center in Bangalore, India and his troubles being so far removed geographically from the rest of the management team. His performance review mentioned a lack of “presence”, which is pretty obvious problem when you have such a hard time communicating with your managers. He relates this to coders’ sometimes antisocial tendencies to hole up in the cubicle and get into “the zone” of programming. This is an issue for us at Blackstone sometimes, because of the distributed nature of the projects and client sites. Maintaining that presence in the community of Blackstone is difficult but very important for both management perceptions and being able to leverage teamwork to achieve great things.

A few points in this book tied in tightly with the direction I feel Blackstone’s Company Contribution evaluation has been heading. Chapter 38: Change The World advises “Have a mission. Make sure people know it.” Chapter 42: Remarkability talks about doing things significantly different than those around you to be remarkable, such as releasing open source software, writing books and articles, and speaking at conferences. It’s not enough in this field to show up to work on time, write code on schedule and fix bugs quickly. Keeping up with the technology wave is important, but the ultimate goal should be to get out in front of it and show everyone a new way to go.

In the acknowledgments, Chad Fowler admits a huge debt to Dave Thomas and Andy Hunt’s great 1999 book The Pragmatic Programmer, which kicked off their entire book line. Both books contain a lot of great tips wrapped in short chapters that I will come back to frequently. I consider both to be essential and valuable, providing quick inspiration to improve both software and my career.

Categories: code Tags:

JAutodoc

January 5, 2011 1 comment

Things on my project have quieted down a bit, so I’m trying to get done some of the nice touches that I don’t usually have time to do. Because my module is brand new, it’s completely doable to make the inline documentation really spiffy. I want to do all the stuff to make the Javadoc look just right.

One of those things is to do package javadoc. You used to do that with a package.html file, but as of Java 5 they want you to create a package-info.java file instead. You can read all about it at http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html

Because I use Eclipse, I figured there’d be some buried menu command to generate this stuff, but surprisingly there wasn’t. I did, however, find an Eclipse plugin called JAutodoc to do this for me.

Great! Except the plugin installation failed. The installation exception message mentioned a missing module, org.eclipse.team.cvs.ssh. Eclipse 3.6 Helios comes with the org.eclipse.team.cvs.ssh2 plugin, but for some reason they did not include org.eclipse.team.cvs.ssh.

I couldn’t find a site to download just that (54k) plugin jar file, but I did discover that Eclipse 3.5 Galileo did include it. So I moved that jar from my Galileo Eclipse installation to the Helios installation, retried the JAutodoc install, and everything works.

So if you want to be fussy about your Javadocs in Eclipse, check out JAutodoc, and if you have trouble installing it, this could be your solution!

Categories: code Tags: , ,

XOP Converter

December 18, 2010 2 comments

My most recent task at work involves XOP, or XML-binary Optimized Packaging. Basically, if you have an XML document that needs to have binary data in it, like images, you have two ways of doing it. You can base64 encode the data and put it directly in an XML element, or you can use XOP to keep the data in binary form and attach the file to the message using MIME. XOP is usually used to represent SOAP messages with MTOM (Message Transmission Optimization Mechanism).

My app uses JiBX to convert my Java objects into XML documents, and base64 encodes them. But I got a new requirement to also support XOP encoding. So to minimize impact on the app, I decided to write a converter that would take in a XML document with base64 encoded data and spit out a properly encoded XOP document. Then it would also be useful to convert existing documents and creating new ones. The module that helped me do this was Apache Axiom, a component of the Apache Axis2 web services engine.

The main problem with Axiom is its poor documentation. Almost all of the docs I could find about Axiom and Axis2 were from a server-side perspective, and I am writing a client-side app. So I thought I’d share the bit of code I wrote to do this conversion.

public class XOPConverter {

    @SuppressWarnings("rawtypes")
    public static void convertToXOP(String xml, OutputStream os) 
        throws XMLStreamException, JaxenException, JiBXException, IOException {

        // set up all the AXIOM objects
        XMLInputFactory factory = XMLInputFactory.newInstance();
        XMLStreamReader parser = factory.createXMLStreamReader(new StringReader(xml));
        StAXOMBuilder builder = new StAXOMBuilder(parser);
        OMDocument document = builder.getDocument();
        OMElement documentElement =  builder.getDocumentElement();
        OMFactory fac = OMAbstractFactory.getOMFactory();
        
        // find all the BinaryBase64Object nodes
        AXIOMXPath xpathExpression = new AXIOMXPath("//niem-nc:BinaryBase64Object");
        xpathExpression.addNamespace("niem-nc", "http://niem.gov/niem/niem-core/2.0");
        List nodeList = xpathExpression.selectNodes(documentElement);
        
        for (Iterator i = nodeList.iterator(); i.hasNext(); ) {
            Object obj = i.next();
            if (!(obj instanceof OMElement)) {
                continue;
            }
            OMElement element = (OMElement)obj;
            // get bytes data out of that node
            String base64data = element.getText();
            // decode base 64 encoding
            byte[] data = Base64Serializer.deserializeBase64(base64data);
            // create ByteArrayDataSource
            ByteArrayDataSource ds = new ByteArrayDataSource(data);
            // set the MIME type
            ds.setType("image/" + XOPConverter.getMIMEType(data));
            // create OMText object
            OMText textElement = fac.createOMText(new DataHandler(ds), true);
            // replace BinaryBase64Object node text with OMText object
            element.setText("");
            element.addChild(textElement);
        }
        
        // do the XOP conversion
        OMOutputFormat format = new OMOutputFormat();
        format.setDoOptimize(true);
        format.setMimeBoundary("MIME");
        format.setContentType("Multipart/Related");
        MTOMXMLStreamWriter mtomWriter = new MTOMXMLStreamWriter(os, format);
        mtomWriter.setDoOptimize(true);
        document.serializeAndConsume(mtomWriter);
        document.close(false);
    }
    
    /**
     * @param args
     * @throws IOException 
     * @throws XMLStreamException 
     * @throws JaxenException 
     * @throws JiBXException 
     */
    public static void main(String[] args) 
        throws IOException, XMLStreamException, JaxenException, JiBXException {
        
        if (args.length == 0) {
            System.out.println("Usage: <filename>");
            return;
        }
        String inputFileName = args[0];
        String outputFileName = inputFileName.substring(0, 
                inputFileName.lastIndexOf(".")) + ".xop";
        boolean overwrite = false;
        if (args.length > 1) {
            String overwriteStr = args[1];
            overwrite = Boolean.parseBoolean(overwriteStr);
        }
        // check if output file exists
        if (new java.io.File(outputFileName).exists() && !overwrite) {
            System.out.println("ERROR: Output file " + outputFileName + 
                    " exists, please move it.");
            return;
        }
        
        java.io.BufferedReader reader = 
            new java.io.BufferedReader(new java.io.FileReader(args[0]));
        String line = null;
        StringBuilder stringBuilder = new StringBuilder();
        String ls = System.getProperty("line.separator");
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line);
            stringBuilder.append(ls);
        }
        String xml = stringBuilder.toString();
        
        FileOutputStream os = new FileOutputStream(outputFileName);
        convertToXOP(xml, os);
        System.out.println("done");
    }
}

Now I’ll step through the code and explain it a bit.

The convertToXOP method takes the XML text as a String, and an OutputStream where the XOP is written. In my original document, all the base64 encoded objects I need to extract and attach are in elements labeled . So I cook up an XPath expression to find all those elements for processing. The code gets the data out of the element and un-base64 encodes it. I didn’t write that code, so you’ll have to do that yourself. Then we put the byte array in a ByteArrayDataSource and tag it with the MIME type we want to use. In a later post I’ll show how to figure out what encoding an image uses. Then use the OMFactory to create a new OMText element containing the byte array and set it to be optimized. Add it to the original element and reset its text, clearing out the base64 encoded data.

The key to doing the conversion is Axiom’s MTOMXMLStreamWriter class. You send the MTOMXMLStreamWriter the OutputStream where you want the XOP document to go, and an OMOutputFormat object. Setting the doOptimize flag on the OMOutputFormat object enables the optimization step that moves the binary objects to MIME attachments. Sending the MTOMXMLStreamWriter to the OMDocument’s serializeAndConsume method writes the XOP output.

I hope this code and explanation has been helpful to you.

Categories: code Tags: , ,

Scala test drive

November 22, 2010 Leave a comment

Last week I got together with some coworkers to learn Scala and write some code. Since it was our first gathering, we kept it very simple and tried to really understand how the Scala code works and how it is different from Java.

Before the session, my day job gave me a very simple programming task, to read first and last names out of a directory of xml files and write them to a CSV file along with the name of the xml file it came from. There are many better and easier ways to do this, but I wrote it in Java, intending to rewrite it in Scala with my group.

Here’s the Java code:

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileFilter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class OTHExtractor {
    private static OutputStreamWriter writer = null;
    
    public static void main(String[] args) throws ParserConfigurationException,
            SAXException, IOException, XPathExpressionException {
        writer = new OutputStreamWriter(new FileOutputStream(new File("names.java.txt")));
        try {
            File[] files = new File(".").listFiles(new FileFilter() {
                public boolean accept(File file) { 
                    return file.getName().endsWith(".xml");
                } 
            });
            for (File file : files) {
                process(file);
            }
        } finally {
            writer.flush();
            writer.close();
        }
        System.out.println("done");
    }

    private static void process(File xmlFile)
            throws ParserConfigurationException, SAXException, IOException,
            XPathExpressionException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(xmlFile);

        XPathFactory xpFactory = XPathFactory.newInstance();
        XPath xpath = xpFactory.newXPath();

        NodeList nodeList = (NodeList)xpath.evaluate("//PersonGivenName/text()", 
                doc, XPathConstants.NODESET);
        String givenName = "";
        if (nodeList != null && nodeList.getLength() > 0) {
            givenName = nodeList.item(0).getNodeValue();
        }

        nodeList = (NodeList)xpath.evaluate("//PersonSurName/text()", 
                doc, XPathConstants.NODESET);
        String surName = "";
        if (nodeList != null && nodeList.getLength() > 0) {
            surName = nodeList.item(0).getNodeValue();
        }
        writer.write(givenName + ", " + surName + ", " + xmlFile.getName() + "\n");
    }
}

And here’s the Scala code we wrote to replace it:

import java.io.PrintWriter
import scala.xml.XML
import java.io.File

object OTHExtractorScala {
  def main(args:Array[String]) = {
    val root = new File(".")
    
    val files = root.listFiles().filter(file => file.getName.endsWith(".xml"))
    val writer = new PrintWriter(new File("names.txt"))
    try {
      files foreach (file => writer.write(process(file)))
    } finally {
      writer.flush
      writer.close
    }
    println("done")
  }
  
  def process(file:File) = {
    val names = parseXML(file)
    names._1 + ", " + names._2 + ", " + names._3 + "\n"
  }
  
  def parseXML(file:File) = {
    val xml = XML.loadFile(file)
    ((xml\\"PersonGivenName").text, (xml\\"PersonSurName").text, file.getName)
  }
}

That’s 64 lines of verbose code whittled down to 29 very readable lines. Not bad!

Let’s take a closer look at some of this fancy Scala code.

    val files = root listFiles() filter(file => file.getName.endsWith(".xml"))

The first interesting thing about this line of code is that the call to filter the result of root’s listFiles call. We skipped the dots! You’re allowed to skip the dot and the parentheses when you call a function with zero or one arguments. Here we had to include the parentheses after listFiles so Scala would know that we’re calling a function called filter and not sending a value called filter to the listFiles function.

Next up is file => file.getName.endsWith(".xml")). What is this? This is an anonymous function. The bit before the => is the argument this function takes, in this case file. The part after the => is the body of the function. The Scala Array object’s filter method takes a function that returns Boolean. So this is a much slicker way of getting just the xml files than Java’s FileFilter interface.

The other thing I want to point out is the tuple. The parseXML method returns a tuple where the first element is the given name, the second element is the surname, and the third element is the file name. In Java we’d have to package them into a collection of some sort, or create an inner class to hold the values. Tuples make this much easier. The process method grabs the elements from the tuple names and turns them into a concatenated String separated by commas.

Oh, one other thing. Scala’s XML module ROCKS. Look how much easier it is to run XPath queries on documents. One line of code creates the XML document, one line of code finds the elements in the document. We needed a lot more verbose Java to do that.

Categories: code Tags:

eclipse.ini

November 3, 2010 Leave a comment

Eclipse is great, but it takes a while to get started on my work laptop. I decided to search around to see what I could do to speed it up.

The first tip I found was to make sure you were running Eclipse under the most recent JDK. By default Eclipse uses the first JVM it finds in your path. The server components on this project require Java 5, not 6, so many developers have to configure their systems to allow Maven to use Java 5. They can still install Java 6 and use it with Eclipse with the following argument:

-vm "C:\Program Files\Java\jdk1.6.0_22\bin\javaw.exe"

You can either put that in the shortcut you use to start Eclipse, or you can put it in the Eclipse.ini file.

The second tip is to disable unnecessary plug-ins on startup. In Eclipse 3.6 (Helios) this is configurable in Preferences -> General -> Startup and Shutdown. I don’t think a lot of people use Mylyn, so that’s a feature to disable if you don’t.

The third tip is to customize the eclipse.ini file. I found this great post on StackOverflow that explains the best options for entries in eclipse.ini for any recent version of Eclipse you are using.

For an in-depth look at all the Eclipse runtime options, check out the Eclipse documentation.

Categories: code Tags: