Sunday, March 13, 2011

A snag in my Grails Script

Yesterday, I blogged about doing a build/deployment from a standalone Groovy script. However, I discovered what I think is a bug in GrailsScriptRunner that prevents System properties from being passed in from external script runners (such as the GrailsTask Ant task or even from Gradle). This kinda sucks, so I'm investigating a couple of possible workarounds, or patching GrailsScriptRunner to fix the problem.

Saturday, March 12, 2011

Adventures in GrailsBuilding (and Deployment)

For one of my recent Grails projects, I wanted to do some scripted build and deployment. There seems to be a ton of options available. I've recently been using Capistrano for a *Rails* project, which is pretty much the de facto standard on that side of the world. Capistrano is pretty powerful, but I'd rather not mix apples and oranges, and I much prefer Groovy over Ruby.

In Grails/Groovy land, we have Gant (Groovy wrapper for Ant), which is a Make-like facade around Groovy AntBuilder. We have Gradle, which is what the Grails scripts use.

In the end, I decided to go with simple Groovy + AntBuilder. My initial version of the script just does two things -- it runs the Grails War task, then uses JSch to scp the warfile to a bunch of servers. Even for just these two simple tasks, a bit of fancy footwork was required.

First of all, I wanted to define the deploy configuration in a Groovy Config file read in using ConfigSlurper. This format easily allows switching between different environments in a highly readable format.


def env = args[0]
def config = new ConfigSlurper(env).parse(new File('deployconfig.groovy').toURL())


Next, running the Ant GrailsTask was non-trivial. There are lots of references on the web to people having problems similar to mine -- for whatever reason, GrailsTask is not compatible with Ant 1.8, which is what is included in Groovy 1.7. One interesting thing I figured out was that AntBuilder hard-codes the Ant logging level to INFO. To set it to DEBUG, you can do this:


def ant = new AntBuilder()
ant.project.getBuildListeners().each {
it.setMessageOutputLevel(Project.MSG_DEBUG)
}


IN the end, I ended up having to downgrade to Groovy 1.6. MacPort makes this a pretty simple process (including the ability to easily switch back and forth between versions) that is documented very well here.

Next, I use Grape to download the dependent jars (grails and jsch). I do this within the script to make it as self-contained as possible. Unfortunately, the Grape API changed from Groovy 1.6 to 1.7, so I had to do the following to add the repositories to Grape:


def groovyVersion = org.codehaus.groovy.runtime.InvokerHelper.getVersion()
println "Groovy version $groovyVersion"

if (groovyVersion.startsWith("1.6")) {
Grape.resolve(name:'jsch', root:'http://jsch.sf.net/maven2/')
Grape.resolve(name:'grails', root:'http://repository.codehaus.org/')
} else {
Grape.addResolver(name:'jsch', root:'http://jsch.sf.net/maven2/')
Grape.addResolver(name:'grails', root:'http://repository.codehaus.org/')
}


Finally, to get the GrailsTask to run, I had to set up a classpath for it:


def grailsHome = System.getenv('GRAILS_HOME')
ant.path(id:'grails.classpath') {
fileset(dir:"$grailsHome/dist") {
include(name:"grails-bootstrap-*.jar")
}
fileset(dir:"$grailsHome/lib") {
include(name:"groovy-all*.jar")
include(name:"ivy*.jar")
include(name:"gpars*.jar")
include(name:"gant_groovy*.jar")
}
}
ant.taskdef(name: "grails", classname: "grails.ant.GrailsTask",
classpathref: 'grails.classpath'
)
ant.grails(home:grailsHome, script:'War')


The final step to copy the WAR file is pretty simple:

config.hosts.each { host ->
def commonParams = [
keyfile: config.keyfile,
username: config.username,
passphrase: '',
trust: true
] // these are parameters that are used for any SSH-related tasks
ant.scp(commonParams +
[
file: 'target/app.war',
todir: "${config.username}@$host:/${config.deployDir}"
]
}


Just a random side note... I sure wish the Groovy Object class included each(). It's REALLY convenient to be able to pass objects that may either be individual instances or Collections back and forth. Having to check for a Collection is unnecessary. If this were implemented, my config file could contain either:
hosts: 'hostname'
or
hosts: ['hostname1','hostname2']

instead of the more bulky
hosts: ['hostname']

When I get some more time, I want to look more closely at Gradle. But for now, my standalone Groovy script will suit my needs just fine.

Tuesday, January 18, 2011

Rebirth

Well, I certainly (as most bloggers do) had grand hopes for providing more content than once every 4 months. I've had a plethora of "a-ha" moments since my last psot, followed by "wow, I've got to blog about that", followed by "wow, am I really gonna blog about that before doing XXX."

Anyways, (as most bloggers are wont to do), I'm going to promise to try to write a bit more often. It'll help if you guys give me an occasional prod. :-)

This last week, I became a proud card-carrying member of the Apple developer community, having purchased a 13" 2.4GHz MacBook Pro so that I could do native development on a couple of projects I'm working on.

My initial thoughts are that I'm very much smitten by the whole Mac ownership experience. It's so....NICE having a computer and OS that doesn't freeze every 10 minutes for some random activity, whose bluetooth works seamlessly, where having a UNIX shell isn't the bastardized hack that Cygwin has become, and I can have the oodles of apps that I like to have open at once with zero impact on performance.

My big growing pain has been adjusting to the differences in keyboard layout and shortcuts. Those of you who know me well know that I live and die by the keyboard, only resorting to mouse/trackpad as a last resort. And while my fingers have almost gotten used to where to go to hit control-A to go to the start of a line, switching back and forth between PC and Mac keyboards is still a problem for me. I'm sure I will adapt, eventually.

As far as development goes, the XCode SDK is still a bit of a mystery to me, but I'm slowly figuring it out. Luckily, I'm not having to learn Objective C yet, although that is certainly around the corner.

I did figure out one really weird problem that was plaguing me, and while I'm usually quite adept at Googling for solutions to problems like this, I couldn't find any references to it. So, here's a quick summary in the hopes that it will help someone out:

I was able to build and run my app with no problems, both in the simulator and on my device. But then I wanted to archive a build to send as an ad hoc distributable, I ran into problems. I selected "Build and Archive", a dialog box popped up that said:

"Unable to create application archive directory. To view or change permissions, select the item in the Finder and choose File > Get Info."

Sounds reasonable. Only, I had no clue where this "archive dirctory" was. Must be in the project settings somewhere, right? Nope. XCode preferences? Uh uh. Probably a trivial task for someone more familiar with this environment to dig into whatever XCode's equivalent to makefiles is to figure this out, but for noob me, I was stumped.

I did find a script that performs the build and archive steps from the commandline. Running this script produced a similar error to the dialog I was getting in XCode - with the additional useful information of the directory name
/Users/xxx/Library/Application Supprt/Developer/Shared/Archived Applications

And sure enough, the directory was owned by Root and permissions were 755, so one quick chown and I was back in business.

I'm not sure how the permissions got screwed up in the first place or whether I did something that caused the permissions to get changed, but hopefully this will help someone who encounters the same problem.

Until next time...

Friday, August 27, 2010

[Node:Michael Migdol] [Edge:Attends] [Node:SVST Meetup on Open Graph Protocol] [Location:Facebook]

Sorry for the cryptic title, but I think you guys can probably figure it out. Last Monday I had a nice time at the Silicon Valley Semantic Technology (SVST) Group Meetup hosted by Facebook, where I got to learn a little bit about Facebook's Open Graph "protocol". I put "protocol" in quotes because Facebook's spin on this idea isn't so much as a protocol as it is an extended definition of HTML meta tag content in your web pages. Open Graph is simply a web of nodes (people, movies, etc. ) interconnected by predicate edges ("likes", "checked into", etc) that can be defined by this metadata content. Should sound very familiar to those of you familar with RDF graphs.

On the surface, the idea of using this "protocol" to create an interconnected web of things that can be "liked", or "attended" or "checked into" seems reasonable. Unfortunately, I really thing Facebook rushed this product out the door and dropped the ball in a few key areas.

The first deficiency by far was the lack of ability to query the Open Graph. Although this was my first SVST event, my take on things is that the bulk of the attendees are consumers, rather than producers, of semantic content. When we got to the Q&A part of the meeting, my hand shot up, but came back down when the attendee who was picked asked my question: "what about querying?" Facebook had no good answer, however, and it seemed to me that this functionality will be far off into the future. The only current real tangible output of OpenGraph seems to be the enhanced like button that shows how many people and which friends of yours like the web page that you are visiting, and to provide more content for your Facebook news stream. Facebook - please give us the ability to:
- Query for nodes on the graph with specific criteria, e.g. "tell me the movie liked by the most people in the 95124 zip code in the last 24 hours"
- Query for people's interests (for those who opt-in to making those interests public)
- Provide recommendations from within your graph

The second deficiency I noted was the lack of a "same-as" functionality in the same vein as the OWL:sameAs property. So if I "like" the Avatar node defined by IMDB, and you "like" the Avatar node defined by Rotten Tomatoes, there is no relationship between those two edges in the Graph. The guys at Facebook promised that they're working on this problem, but couldn't provide a timeline.

Finally, I don't think that Facebook did a very good job at separating the concept of a web page from a block of content on a web page. Take this blog for example -- the blog as a whole should be a node, and individual posts should be nodes, and comments on posts should be nodes. And I should be able to like (or dislike) any of the above.

I came away from the meeting feeling like Facebook had put a bit too much value in their design goal of making it super-easy to create OpenGraph nodes and, in doing so, both reinvented the wheel and made it less useful. The Semantic community has done lots of great work in defining standards for this kind of thing already -- let's put the brainpower at Facebook to work on using these standards to access the wealth of data and data consumers that exists under the Facebook umbrella!

Tuesday, August 24, 2010

Introduction

Welcome!

I've created this blog as a vehicle to provide yet another perspective on all things tech. I expect to focus on most of my areas of expertise: Mobile Computing, Speech Recognition, Software as a Service (SAAS)/Cloud Computing, and web application frameworks (particularly Grails, the framework for my current project). Hopefully I will have something of interest to you at some point. :-)

A brief biography: I'm a 38 year-old married father of two. I graduated from UT Austin with a EE degree in 1995. While at UT I had a variety of part-time jobs in areas including authoring automated psychoacoustic experiments, sonar software development, financial trading algorithms, and IEEE-1394 (Firewire) firmware programming. After graduating, my wife (who I met and married at UT) and I moved to Osaka, Japan, where I joined Matsushita Electric Works. While there, I worked on embedded software for building automation systems. We moved back to the US in 1997, where I was employed by AMD as a Systems Software Engineer, developing device drivers and OS ports for their embedded micro-controllers.

In January of 2000, I joined BeVocal, a startup founded by a friend and former Matsushita co-worker. BeVocal was focused on providing automated speech application solutions, and I did some of everything while I was there: speech application development, building of our VXML interpreter and telephony platform (which today hosts 100's of millions of calls per month), architecture, management of the team responsible for this platform and our BeVocal Cafe development environment, and bootstrapping of our first mobile application product (today known as "Nuance Mobile Care"). BeVocal was bought by Nuance in 2007, and my most recent work there was centered around management of our first Android port, and technical product integration of various mobile technologies that Nuance had acquired. Nuance and I parted ways this past June.

So that brings us to now! I am a co-founder at a startup called Infopixie, which aims to revolutionize how you get content from the web. I've had to jump head-first into the deep end and learn quickly about various "Web 3.0" technologies to gather content, learn about you and what you're interested in viewing, and provide you with a super easy way to view and manage your content. We have a tough problem to solve, but I really think we are moving in the right direction. I look forward to sharing more about Infopixie with you in the near future. (Let me know if you're interested in being a beta tester!)

If you have any topics you're interested in hearing more about, let me know. I look forward to chatting with you blog readers out there!