More Groovy Goodness

Yesterday afternoon, I was talking to a friend about one of my apps, and he asked about how big the app was.  I had no idea, and thought I'd write a little app to figure it out for me.  Oh how easy Groovy makes things.  The basic idea was to build a simple parser to return counts of files, directories, lines of code, and bytes, as well as break them down by file types.  Hand that data structure off to a renderer for output, and you're done.

The code is available at https://ssl.barneyb.com/svn/barneyb/code_counter/trunk/, just check it out and execute:

groovy Runner </path/to/project>

Executing the project on itself, you get this output:

                         Count      All Lines     Real Lines          Bytes
---------------------------------------------------------------------------
Paths                        1
Directories                  1
Files                        9            422            291          8,047
   text                      9            422            291          8,047
      groovy                 7            402            271          7,945
      txt                    2             20             20            102
   binary                    0                                            0

<project pathCount='1' fileCount='9' directoryCount='1' byteCount='8047' lineCount='422'>
  <type type='text' fileCount='9' byteCount='8047' lineCount='422' realLineCount='291'>
    <extension extension='groovy' fileCount='7' byteCount='7945' lineCount='402' realLineCount='271' />
    <extension extension='txt' fileCount='2' byteCount='102' lineCount='20' realLineCount='20' />
  </type>
  <type type='binary' fileCount='0' byteCount='0' />
</project>

You can also pass in multiple paths (if your project has multiple components), and you'll get per-path breakdowns as well as the totals.  The two output formats are created by separate renderers (text and xml) operating on the same data structures, and each can be used independently.

Of particular interest was the ease of counting lines (line 2 gets the lines in the file as List of Strings, line 3 counts them, and line 4 counts the ones that have non-whitespace characters):

// 'file' is a java.io.File
def lines = file.readLines()
this.lineCount = lines.size()
this.realLineCount = lines.sum(0, { it.trim().length() == 0 ? 0 : 1 })

Similarly, reading the configuration for which files to ignore, and which extensions are considered text files:

TEXT_EXTENSIONS.addAll(getClass().getResourceAsStream("/text_extensions.txt").readLines())
FILES_TO_IGNORE.addAll(getClass().getResourceAsStream("/ignore.txt").readLines())

This piece, in particular, I'd intended to leave as configured-in-code, because I didn't want to deal with the external resource headache and then the parsing of the files once I got them.  Ha.  As if Groovy would make me do that.

There's a whole bunch of other stuff in there, particularly the mixing of implicit and explicit getters to unify object APIs.  If you want a directed tour, the core of the app resides these methods: CodeCounter's constructor, PathInfo.processDirectory, and FileInfo's constructor.  The output is handled by TextRenderer (using a PrintWriter) and XmlRenderer (using MarkupBuilder).

Total time to build the app was about an hour, required no special editor (I used TextPad), IDE, or compiler.  The classes can be executed in any Groovy environment (including CF Groovy), or run them through groovyc and use them in any Java environment.  Oh how nice it is.

Meme(me)

Per Mark:

The Rules:

  • Take a picture of yourself right now
  • Don't fix your hair, don't change your clothes, just take the picture
  • Post that picture with no editing
  • Post these instructions with the picture

I do this every day, meme or not.  See the stream, or get it as RSS.

CF Groovy Preso

Last night I talked at the PDX RIA group about using Groovy to extend CFML applications and better leverage some of the benefits that Java has to offer (in addition to the benefits Groovy itself brings).  Unfortunately we had some technical difficulties, but the presentation was recorded on Connect, and can be accessed at http://experts.na3.acrobat.com/p71343823/.  I apologize for the delay in posting, I usually try to get things up immediately afterward, but just didn't happen this time.

In addition to the Connect recording, I've posted my slides (in PDF format).  The sample app I demoed is just the CF Groovy demo app available at https://ssl.barneyb.com/svn/barneyb/cfgroovy/trunk/demo.  Within the demo app is the framework itself, including Hibernate (whose JARs are the source of the large download).  The demo app is also available online should you wish to run it without having to download anything.  Of course, there's not much to see without the code.

Why, Microsoft, Why?

I've had the unfortunate luck to partially inherit sysadmin duties for the web team at Mentor (along with Joshua) after one our coworkers left.  While I don't necessarily mind a sysadmin role, almost all of the infrastructure is Windows.  The sole exception is our Subversion server, which is Linux, and only because I migrated everything of the existing Windows box last year sometime.  That migration was done specifically for the mod_python support that Linux provided to gain a significant performance benefit for Trac.

So today, I'm trying to help the boss install this Scrum management application on one of our internal servers.  It's an ASP.NET application (horror of horrors, I know), and the server doesn't have the WWW Publishing Service installed.  It's got IIS for the mail server, but all the apps on there are standalone JEE apps, and we just let the container serve directly to the network.  Simple process, go into Add/Remove Programs, hit the Windows Components button, find the right item, check it's box and hit next.  Ha!

Apparently the Cluster Service (which we didn't ask for) requires MSDE to be installed, and while it's listed in the installed programs, it was apparently not installed enough.  So we spend like an hour trying to find this stupid MSI that it needed.  My question is, why the heck can't Windows go get this package automatically.  When I install Apache via 'yum' (on Linux), it magically goes and gets everything it needs, all by itself.  And keep in mind that we have to pay money in order to use Windows and IIS!

Eventually we gave up, hit Cancel about ten times to skip the Cluster Service, and the rest installed.  We installed the app (which needed the ability to create it's own database, I might add, refusing to accept an already-created one – WTF?) and it didn't work.  Little did we know that while we could go edit the ASP.NET settings in the web site properties, that doesn't mean anything.  The properties just sit there until you go install the ASP.NET Windows component.  Next question: why the hell can you use a Windows-provided dialog to configure a service that isn't installed?  Surely dependency management is something they understand up in Redmond, and things like making configuration dependent on the software to be configured seems reasonable?

I haven't been around Windows servers in a good number of years, but this kind of lunacy is just what I remember from when I was.  No visibility into what's going on and/or why, plenty manual work to get things juuuuuuuuuuust right and then no way to save that stuff in SVN in case it breaks, and horribly fragmented everything.

Fortunately, we're slowly replacing IIS on all our other boxes with Apache so we can at least version the config and use such radical tools as copy and paste.  We've just about eliminated JRun in favor of Tomcat.  Once CommonSpot is gone from our last property still using it we'll be free of that burden.  We still have huge investment in MS SQL Server, but beyond that, we'll actually be in a position to start switching servers to Linux, which will be awesome.

The success of the SVN server (perfect uptime, great performance, easy configurability, etc.) has definitely shown the utility of a programmer-friendly OS and toolset over what Windows provides.  Hopefully that carries sufficient weight to justify migrating other infrastructure where there's not a specific platform advantage to exploit.

Stream Layouts

No, I'm not talking about those things full of water running down the mountainside, I'm talking about how you assemble a full HTML (or whatever) page from a webapp.  Being a Trac user, I've done a bit with Genshi, and then with Grails used SiteMesh, both of which are stream-based engines.  I'd always though the paradigm was a bit odd, and kind of inside-out, but today I realized they're exactly the right way to do things.

The basic idea is that your app generates the page body, as a full HTML document, and then it gets passed down the stream and manipulated.  Maybe it gets some LINK and SCRIPT tags added to the HEAD, or a footer to the end of the BODY.  Contrast this with the "content variable" assembly process, where your app generates a bodyContent variable which is handed to a layout to render into a full page.

What invariable ends up happening with the latter approach is a whole pile of variable along with bodyContent (headContent, pageTitle, footerContent, aboveFooterContent, belowFooterContent, etc.).  A huge mess, especially if you're using Fusebox or something like it, where you typically put your HTML in a dsp_ file and include it into a content variable, because you end up with a massive number of one-line includes, most of which are all bound to rendering a single page (i.e. aren't reused).

Contrast that with SiteMesh or Genshi, where you can package all those separate pieces up in a single packet, and let them stream out of your app to the layout engine that takes care of everything.  It might be combining your content with another app (think a portal), might be wraping it with header/nav/footer, might be restructuring it for an iPhone vs. a PC.

This doesn't come without a downside, of course.  For example your navigation isn't controlled by your application anymore, so if you're addicted to XFAs (as I am), you're kind of stuck.  For websites it's not as big a deal because you don't want your URL-space changing, but for applications it's a bit more troublesome.  This is straightforward to deal with in the opposite direction, however.  There are also some performance implications, though they're pretty trivial.

I'm trying to figure out a good way to integrate SiteMesh with CFML.  CF is just a huge burden to try and drag into the JEE way of doing things, but Railo should be far simpler.  I really don't want to use JSP for the layout engine, and while I like using Velocity, keeping to a single templating language is certainly beneficial.  Whether I'll actually have time to implement something remains to be seen, but it sure would be nice.

Groovy Preso on Connect

My Groovy presentation next week for the PDX RIA will also be available online (via Connect) at http://experts.na3.acrobat.com/r59025898/.  A recording will be available as well, but that URL isn't available until afterwards.  I'll post it after the meeting, along with my materials.

Groovy at PDXRIA

Next Thursday (Sept 11) I'll be presenting at the PDX RIA group (formerly CFUG) about Groovy.  Someone (cough…Simeon…cough) hasn't updated the site with the new info, but rest assured that it's happening.  As you might imagine, I'll be talking about my CF Groovy framework, but I'll also be talking about JVM language integration in general, Groovy in general, and how hybrid apps are a good thing.  Included in that will be some Hibernate goodness, which I'm still amazed by every time I pick it up because of the drastic reduction in code to write.

Unfortunately, I'm currently at a job where leveraging these technologies is verboten.  We use CF8 and Magnolia (a Java CMS) for most of our sites, so it could be really handy to shell out for some "Java" without invoking a compiler.  However, it doesn't happen like that.  We write some Java, compile, deploy, cycle the container, test, repeat.  Or we hack together something with CFML to synthesize the functionality we need.  I get some relief on personal projects, where it's CF Groovy w/ Hibernate all the way, but that understandably pales in comparison to my at-work efforts.  C'est la vie.  I do get a paycheck for the work stuff, which does a remarkable job of offsetting the downsides.  ;)

Twitterfon, Typing Upside-Down, and IMPlus

After more use, I've uninstalled both Twitterific and Twinkle, and switched to Twitterfon. It's simple, fast, works well, and hasn't crashed yet. It touts a focus on the core, not extra features, but I'm not sure what is missing.

I also just found IMPlus, which is a multiprotocol IM client for iPhone. It can't work in the background, of course, but it seems pretty nice. Only used it a couple times do far, but no compaints so far.

Lastly, I'm getting the hang of typing a little better. One very interesting facet of this is that I simply cannot type upside-down. I'm pretty much always in bed before Heather, so I usually end checking my email and/or Twitter (or blogging ;)) while I wait. But try as I might, I cannot lie on my back to do it. I have to flip over, prop myself up on my elbows, and type that way. The weight of my hand is inverted on my back, so I miss everything. Not very fun.

Ok, so it wasn't "lastly". This time it is, though. Lastly, I had a whirlwind weekend of reading. Finished "Skinny Legs and All" Friday evening, read "The Hitchhiker's Guide to the Galaxy" on Saturday, and the "Ender's Game" on Sunday and Monday. More surprising is that it was the first time for all three, not just the first one. SLaA and EG definitely blew the pants off THGttG, though I've not decided which was better between them. Very different, and both very good in their own ways, though I thought both kind of copped out on the ending a bit.

Refresh on GMail for iPhone

After a couple weeks of reloading the whole GMail app to check for new messages, I found a better way. It always seemed silly to have to do it, but there isn't a refresh button like on the desktop interface. Like so many things with Google products, all it takes is stepping back from what you expect based on other apps and just doing what seems rational. It often just works.

So how do you do it?

Just tap the page label. The one with the message count. It updates, and reloads the massage list if needed.

My New Blog

I just created another blog for myself.  Why have only one, when I could just as easily have two?  I've always tried to keep things generally technical on here, knowing that most of my readers are here for the technical content.  The new blog is a complete departure.

A few years ago, I started building an app that would allow me to take pictures with my cell phone and email them to my blog for automatic posting.  But the problem ended up being fraught with complexity, and just wasn't much fun at all.  So I bailed.  But now with my iPhone and the WordPress app, putting phone pictures on a blog is a snap.  I'll probably end up posting actual content at some point, but for now, it's just a photostream.  We'll see how it goes.

This was also a good test for the WordPress management framework I've built.  I host a number of blogs on my server, and because of the way WordPress works, you have to have a separate copy of the code for each blog.  While that itself is not really an issue, managing the separate copies is.  Enter Ant and Rsync.  My Subversion repository for the blogs houses a set of config files, a single copy of WordPress, and a collection of custom themes.  I use Ant to convert that into a set of complete installations, with the right themes going into the right installations, and configuring each one as needed (databases, urls, etc.).  Then Rsync is used to copy all that crap out.  Since most of the time little has changed, rsync really cuts down on the transfter time and bandwidth.  Adding a new blog was as simple as dropping a new config file, registering the blog, creating the database, and running the Ant/deploy pair of commands.

The process could definitely be smoother, but for a use case that wasn't even on the table when it was built, it handled it admirably.  Since I don't forsee creating a lot of new blogs, I don't think I'll be enhancing the functionality to accomodate that use case any better, but it made me happy that it was easily scalable in an unforseen way.