The Source for Java Technology Collaboration
User: Password:



David Walend's Blog

August 2005 Archives


Connecting to a Command Line Process

Posted by dwalend on August 24, 2005 at 05:53 AM | Permalink | Comments (6)

Connecting to a Command Line Process AdamTaglet alpha-0-1 is out! It's really rough; .gifs and .jpegs use the default font, instructions are sparse, and only links to classes in the same package work. But it meets the first release criterion. It does something useful.

Part of this project was adding a tographviz package to JDigraph to encapsulate interaction with the dot language and the Graphviz command line. I built some fairly elaborate code to wrap around the Graphviz command line. I created the GraphvizControl.java class to bundle it all up. It makes for a nice blog article.

I discovered early on that the operating system offered me a fairly short time to read in stderr and stdout. Sometimes I would get all of the output from dot, sometimes just pieces of an error message, or part of the drawing. I solved this problem by creating the StreamPull inner class:


/**
Reads an InputStream until the stream reaches its end. 
The contents of the stream are available in getResult(). 
Any exceptions encountered are in getTrouble().
*/
    private static class StreamPull
        implements Runnable
        {
            private InputStream is;
            private final Object guard = new Object();
            private String result = null;
            private IOException trouble = null;
            
            StreamPull(InputStream is)
            {
                this.is = is;
            }
            
            public void run()
            {
                try
                {
                    StringBuilder stringBuilder = new StringBuilder();
                
                    InputStreamReader streamReader = new InputStreamReader(is);
                    
                    int i;
                    while((i = is.read()) != -1)
                    {
                        char c = (char)i;
                        stringBuilder.append(c);
                    }
                    synchronized(guard)
                    {
                        result = stringBuilder.toString();
                    }
                }
                catch(IOException ioe)
                {
                    synchronized(guard)
                    {
                        trouble = ioe;
                    }
                }
                finally
                {
                    try
                    {
                        is.close();
                    }
                    catch(IOException ioe)
                    {
                        synchronized(guard)
                        {
                            if(trouble==null)
                            {
                                trouble = ioe;
                            }
                        }
                    }
                }
            }
            
            String getResult()
            {
                synchronized(guard)
                {
                    return result;
                }
            }
            
            IOException getTrouble()
            {
                synchronized(guard)
                {
                    return trouble;
                }
            }
        }

Is there a better way to read an InputStream to its end? while((i = is.read()) != -1) looks like 1985. Shouldn't we have left that behind with the piano keyboard tie?

The other ugly part is that massive finally{} block. Four }s reminds me of lisp, but at least the error code is not mixed in to the rest.

The makePicture() method ties together the Process, the InputStreams, StreamPulls, Threads, and an OutputStream. It gets the process out of a ProcessBuilder, grabs the stdout and stderr streams from the process, feeds them to StreamPulls, starts Threads for the StreamPulls, and then writes the instructions to the process. After that, it waits for the process to complete, then joins the two StreamPulls. I didn't use buffers on the streams; the code seems to get by without them just fine. (Your profiler may say something different. Let me know if it does.) I wish Process had a waitFor() method with a time out so that I could add some warning against infinite loops short of calling process.destroy(). However, dot is a pretty solid program with some years of use behind it.


    public String makePicture(String dotString,String process,String format)
        throws GraphvizControlException
    {
        try
        {
            Process dotProcess = new ProcessBuilder(process, "-T"+format).start();
    
            InputStream fromDotStream = dotProcess.getInputStream();
            InputStream errorDotStream = dotProcess.getErrorStream();
    
            StreamPull errorDotPull = new StreamPull(errorDotStream);
            StreamPull fromDotPull = new StreamPull(fromDotStream);
            
            Thread errorThread = new Thread(errorDotPull);
            Thread fromThread = new Thread(fromDotPull);
            
            errorThread.start();
            fromThread.start();
            
            //write to standard out for the process
            OutputStream toDotStream = dotProcess.getOutputStream();
            PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(toDotStream));
            
            printWriter.write(dotString);
            printWriter.close();
    
            dotProcess.waitFor();
            
            errorThread.join(1000);
            fromThread.join(1000);
            

And then there's this massive block of error handling code that opens with a colossal if statement. It translates to "if anything went wrong, build up an error message and throw an Exception." After that code there's one line that returns dot's output as a String, and a little more exception handling.


            if(errorDotPull.getTrouble()!=null 
                || ((errorDotPull.getResult()!=null)&&(!errorDotPull.getResult().equals(""))) 
                || fromDotPull.getTrouble()!=null 
                || fromDotPull.getResult()==null)
            {
                StringBuffer buffer = new StringBuffer();
                IOException cause = null;
                if((errorDotPull.getResult()!=null)&&(!errorDotPull.getResult().equals(""))) 
                {
                    buffer.append(errorDotPull.getResult());
                }
                if(fromDotPull.getResult()==null) 
                {
                    buffer.append("result from process is null");
                }
                if(fromDotPull.getTrouble()!=null)
                {
                    buffer.append("IOException on external process' output stream.");
                    cause = fromDotPull.getTrouble();
                }
                if(errorDotPull.getTrouble()!=null)
                {
                    buffer.append("IOException on external process' error stream.");
                    cause = errorDotPull.getTrouble();
                }
                if(cause == null)
                {
                    throw new GraphvizControlException(buffer.toString());
                }
                else
                {
                    throw new GraphvizControlException(buffer.toString(),cause);
                }
            }
            
            return fromDotPull.getResult();
        }
        catch(IOException ioe)
        {
            throw new GraphvizControlException(ioe);
        }
        catch(InterruptedException ie)
        {
            throw new GraphvizControlException(ie);
        }
    }

I'm mostly happy with the way this class came out, but the whole works has the feel of something that should have just been there. I normally like the library Sun provides in Java, but I felt like I was working far from the context of my problem while bringing this class together. Building the code felt a little strange, like I was assembling a box of many small parts that should have come in larger, pre-assembled chunks. Creating Swing code often feels the same way to me, but the io package always feels fine. I think it's familiarity vs. complexity.

GraphViz Class Diagrams

Posted by dwalend on August 11, 2005 at 07:11 PM | Permalink | Comments (0)

Last week, Kohsuke Kawaguchi suggested that we could use GraphViz to generate class diagrams automatically. This idea caught my imagination. I've been looking for a readily-available set of test data a for JDigraph, plus an excuse to invest time in digraph visualization for another project that uses SVG. Class diagrams are a great fit.

I used methods on Class to populate a Digraph with Classes as nodes and their relationships ("extended by" and "implemented by") as edges. I used digraph iterators to cover the graph and create a dot-formatted String. I pass the String into GraphViz' dot utility to produce an SVG picture. In the test code, I use SVGSalamander to display the picture.

The project came together in three evenings. It deserves a weekend day to refactor some test code into more permanent parts, and maybe another evening or two to put it into a doclet. I hit a few snags that are worth their own blog entries, but I wanted to show the picture now. The effort is on its way to becoming a new project (under Java Tools as soon as I think of a good name). The test code is alive in JDigraph already. It's a work in progress, but here's what one of the first class diagrams looks like:

SVG version of the digraph

Here's what it looks like as a gif (a little lumpy) if your browser doesn't support SVG:

digraph.gif



Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds