Tuesday, April 17, 2018

AWS Step Functions Early Best Practice Learnings

Let's be honest. Anybody who has used AWS Simple Workflow knows there is nothing simple about it.  The "simple" part is that you don't have to maintain the state engine but it is so feature rich that actually using it can be tricky, which is why there are so many frameworks built on top of it.  (I know, I built my own).

AWS Step Functions is a welcome addition from Amazon that can be thought about as a watered down Simple Workflow that provides a flow language, which removes the need to create custom "deciders".

I've found it covers about 90% of what I need but I do miss some SWF features: N-branches, kicking off child-flows, etc.

One large feature I hope they add to Step Functions is a way to manipulate the output json before sending it to the next step in a process.

Best Practices:

  • Lambda functions should be written to be general, not specific to a single step function flow.
    •  Instead of writing an ExportS3ToCSVFile consider writing a more generic ExportS3ToFile lambda that is configurable for several output file types.
  • Lambda functions should, if possible, add result fields to the input json and output that json
  • Lambda functions should take any json shape as input as long as it's required fields are present.
    • Do not throw a validation error if extra fields are present in the input json.
Rationale:

It is much easier to chain lambdas within a step function flow if the above practices are followed.

Missing Functionality: Json Transformation

One large piece of the puzzle that AWS Step Functions currently does not provide is a way to transform the output json of one lambda before passing it into the next lambda step.

This is unfortunate because it forces you to write your lambdas to be workflow-specific, which violates the "write generalized lambdas" best practice.

Workaround

Step Function does provide an under-appreciated step type called "Pass".  Pass allows you to mock up an output and inject it anywhere in the input json doc.  This injection inspired me on how to create a workaround json transform lambda:


The gist of the idea (sorry) is that you use a Pass step to inject a "transformScript" field into the incoming json document.  That field contains all the transform code in plain javascript.   Then your step function flow calls the JsonTransform Lambda to act on that script.

For example I may have a lambda that returns: 

{
  "trace": "abc123",
  "field1": "value1",
  "field2": "value2"
}

and I want to add a third field that combines field1 and field2 and a dateUpdated field.  So I use a Pass step to inject a transformScript field that produces:

{
  "trace": "abc123",
  "field1": "value1",
  "field2": "value2",
  "transformScript": "event.field3=event.field1 + \" \" + event.field2; event.dateUpdated=new Date()"
}

Forwarding this to the JsonTranfrom lambda produces the expected json:

{
  "trace": "abc123",
  "field1": "value1",
  "field2": "value2",
  "field3": "value1 value2",
  "dateUpdated": "2018-03-19T21:20:08.571Z"
}

Downsides?

Some won't like the code smell of injecting JavaScript into their step-function flow.  My counter argument is:
  • The script is run inside a node.js sandbox so nothing too funky can happen
  • The transforming of json from the output of one lambda to fit the expected shape of the next lambda is flow code since that transform only matters to that particular step function flow.
Another downside is that it takes two step function steps to do a transformation.
  • A "Pass" step to inject the transform script
  • A "Task" step to run the JsonTransform lambda.
Currently there is no way around this.  One thing I do to keep things straight is name both the steps as:

"CreateFields" -> "CreateFields!"

Using the same name but adding a "!" to indicate the transform.  This makes it easier to visualize where transforms are happening in the flow.

Summary

I wrote this up quickly but hopefully it will help inspire more discussion on Step Function best practices and missing functionality Amazon may introduce in the future.  Step Functions are great but very limited in this first release.

Wednesday, August 29, 2012

Create Reminder in AppleScript with Intelligent Date Time Parsing

Although you can make a new reminder object in Mountain Lion's Reminders application directly with Apple Script you lose the cool built-in parsing that extracts the date/time settings.

For example we want to simply write "Bowling with Bob 6PM on Friday" and have Reminders parse out the date and time to create a reminder(name="Bowling with Bob", datetime="2012-09-01 18:00:00")

Here is the script, note that I have it in Alfred-friendly form since I use the Alfred app launcher:

on alfred_script(q)
try
  tell application "Reminders"
    activate
    activate -- Single activate doesn't always work, esp if Reminders is closed.
    show list "Reminders"
   
    tell application "System Events" to keystroke "n" using command down
    tell application "System Events" to keystroke q
    tell application "System Events" to key code 36 -- enter
  end tell
 
on error a number b
  display dialog a
end try
end alfred_script

-- how to call:
--alfred_script("Bowling with Bob 6PM on Friday")

Tuesday, April 5, 2011

Fixing iChat GoogleTalk (gchat) connection issues.

iChat kept dropping my two Google chat accounts so I did some of my own Googling for a fix:

Create a script to reconnect iChat.


I created a file '~/dev/ichat-reconnect.sh':


#!/usr/bin/osascript


if appIsRunning("iChat") then
 tell application "iChat"
        if status is offline then
            log in
        end if
        set originalStatus to the status message
    end tell
end if


on appIsRunning(appName)
 tell application "System Events" to (name of processes) contains appName
end appIsRunning


Create a LaunchAgent


LaunchAgents are the OSX equivalent of cron jobs.

In the folder ~/Library/LaunchAgents/ I created a file 'com.ichat.reconnect.plist':


<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>label</key>
    <string>com.ichat.reconnect</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/gcoller/dev/ichat-reconnect.sh</string>
    </array>
    <key>OnDemand</key>
    <false/>
    <key>Nice</key>
    <integer>1</integer>
    <key>StartInterval</key>
    <integer>5</integer>
    <key>StandardErrorPath</key>
    <string>/tmp/AlTest1.err</string>
    <key>StandardOutPath</key>
    <string>/tmp/AlTest1.out</string>
  </dict>
</plist>


Basically it means run my ichat-reconnect.sh script every 5 seconds.

Tell launch agent about your file


Issue the command:

launchctl load com.ichat.reconnect.plist

To get it started. It will start automatically on reboots.

Thursday, March 17, 2011

Intellij IDEA: Key command for right-click context menu

Ever wanted that right-click menu without having to touch the mouse when using IDEA?

Note: This is for OS X, probably is available for Windows.

Turns out to be easy enough:
1) Open KeyMap in Settings
2) Find "Show Context Menu" in the "Other" folder
3) Assign it a key (I used F13 since it is easy to find and available)

Wednesday, January 5, 2011

Groovy to Scala: Closures

Defining a closure that takes two variables and returns a String.

Groovy:
{ x, y -> ...}

Scala:
(x:Stirng, y:String):String => { ... }

Note, Scala requires types for values x and y.

Groovy to Scala: Regular Expressions

Coming to Scala from Groovy/Java. Scala seems to have a bit more overhead in learning basic concepts so I'm planing on keeping my notes here in a series of short posts. This is not meant by any means to be comprehensive but just a "hello world" for each topic that I wish I had.

Regular Expressions:

Defining: Just add a ".r" after a normal string:

val regEx = "apple*".r

Using: Use in a typical Scala match statement:

val name = ....
name match {
  case regEx => // do your processing here
  case entry => // like default, possibly throw an error
}

Grouping:

val zipMatch = "(\\d+)-(\\d+)"
val zip = "12345-1234"
zip match {
  case regEx(num1, num2) => // num1 == 12345, num2 == 1234
  case entry => // do nothing
}

Friday, July 25, 2008

Stephen King's N







Amazon S3 Fast downloads to EC2 using Curl

Our company is building an application that depends heavily on Amazon's Cloud Web Services: Simple Storage Service, Elastic Compute Cloud, and Simple Queue Service. We are using Java for a lot of the business logic and Groovy for the 'glue code', interacting with frameworks, etc.

Anyway, I've spent some time tuning downloads and found out that I can get an order of magnitude faster download time if I shell out to 'curl' than if I use jets3t or the lower-level HttpClient. Note this speed-up only occurs when moving an S3 object to an EC2 instance, not when moving it outside the cloud (to my laptop for instance).

For some reason uploads using jets3t are very fast and we are guessing at this point that HttpClient (which jets3t depends upon) is causing the slowdown because it either can't (or hasn't been configured properly to) deal with the extra-large packet sizes that AWS allows within its cloud.

Being a developer on a schedule I punted and shelled out to curl using a signed-url that jets3t provides for my S3 Object get.

Here is the pseudo-code in Groovy (would be trivial to convert to plain Java) for the shell-out:


S3Service s3service = ... // Inject an instance of JetS3t S3Service
File file = ... // File representing download location on disk
S3Bucket bucket // Bucket object (could just be a string)

Date date = s3service.getCurrentTimeWithOffset()
long secondsSinceEpoch = (date.time / 1000) + 60L
def url = new URL(S3Service.createSignedUrl('GET', bucket.name, key, null, null, s3service.AWSCredentials, secondsSinceEpoch, false, false))

def cmd = ['curl']
// I break up large downloads so here is an optional byte range.
cmd += ['--range', "${low}-${hi}"]
cmd += ['--show-error']
cmd += ['--connect-timeout', '30']
cmd += ['--retry', '5']
cmd += ['--output', file.absolutePath]
cmd += [url]

Process p = cmd.execute()

p.waitFor()
if (p.exitValue() != 0) {
throw new IllegalStateException("Curl process exited with error code ${p.exitValue()}")
}
LOG.info("${file.name} download completed")


One final note: this will capture a curl process error but not many of the errors that you could experience when working with S3. For example if the key did not exist, the curl process would succeed but the downloaded file would contain the Amazon error response xml instead of the intended file. So it is your responsibility to first do a s3service.getObjectDetails(..) to make sure the object exists, then you must check the downloaded content length (and possibly content type) to ensure that you received your object and not an error.

Apple Time Capsule Disk Naming Breaks Backups


Apple Time Capsule Ad
Originally uploaded by Feras Hares.
Took me awhile to figure this one out so I'll blog it here to help raise awareness.

I bought an Apple Time Capsule to add to my home network. I loved the idea of wireless backups using the cool Time Machine software. Anyway, I kept having issues where I could see the Time Capsule's disk in the finder but the back ups would fail with a warning about not being able to mount the disk.

There is a lot of long posts out on the webs but here are two things I tried:

1: Some people were having problems when the Time Capsule disk name was long/contained odd characters
It appears that the shared disk name (not wireless network name) needs to be fairly short ~25 chars. This is unfortunate because Apple software tends to default to a long name when you first set up a TC. (e.g. Joe Owner's Time Capsule).

Link to original thread on issue #1

-- note: this alone did not fix the issue for me but still seemed like a good idea

2: Delete the last entries in the sparse bundle
This one seemed to do the trick, got my Time Machine process past the 'processing' stage and into the actual backup (now at 1.2 GB out of 2.2 GB for the current job). The good thing is all my old backups (except the one day I deleted) still exist.

Here is a quote and a link to the thread that helped:
I just had the same problem. That error can be caused when Time Machine is attempting to hard link to a previously corrupted backup.

Try this:
1) Connect to the shared drive from your Mac
2) Mount the .sparsebundle on your Mac
3) Inside the .sparsebundle, expand the folder "Backups.backupsdb"/
4) Sort the contents by date
5) Delete the link "Latest" and also the most recent incremental backup folder (ie. "2008-06-18-075750")
6) Unmount the .sparsebundle
7) Go into Time Machine Preferences, choose "Change Disk..."
8) Select the shared drive (not the sparsebundle)
9) Click Start Backup. After "Preparing" for a little while, it start backing up again.
10) Click Enter Time Machine when it's done backing up. All of your old backups should be visible (whew!)
Link to original thread on issue #2


Of course the very first thing you should do is make sure you have the latest OS updates on the machines you are trying to back up. You should also make sure the hardware flash drivers are up to date on your Time Capsule

One final detail is that I also have an Airport Express attached to the network to extend the range of wireless reception and to share our printer. Not sure if this contributed to the issue (probably not)

Monday, May 19, 2008

Beyond Compare for OS X?


Many developers moving from Windows to OS X will find that one valued application "Beyond Compare" has no mac equivalent.

Recently a new visual diff application has come out that works very well (for a 1.0). It integrates nicely with Textmate and the OS X terminal. While not as feature-complete and polished as Beyond Compare it does give you the meat & potatoes of what you need - folder and file diffing.

I encourage you to try it out but remember this is 1.0 software. For example, see my screenshot of a folder diff. Notice how the file/folder names are abbreviated even when there is ample room for display.

Friday, May 2, 2008

Intellij IDEA, OS X Leopard Spaces, Fixed with J2SE 6

Looks like the Java 1.6 update finally fixes the Java Swing/Spaces issues that have been so annoying for the last 10 months or so. Yay.

Monday, March 31, 2008

Intellij IDEA, OS X Leopard Spaces, pretty good solution

As of this writing some Java-based apps still don't work well with OS X 1.5.x (Leopard) Spaces feature. Specifically, if you are in a different space than where the IDEA window currently resides, command-tabbing to IDEA won't switch you to IDEA's space. Another side effect is that clicking the IDEA icon in the doc wouldn't bring you to the correct space either.

I noticed awhile back that if you had two idea projects open in the same space then the problem was solved, Spaces would then work properly with IDEA. The problem was the pain in the butt factor of having a blank 2nd project open (extra memory and thread resources).

I noticed that having any of IDEA's modal dialog windows open (like preferences) also solved the problem. Today it finally dawned on me to try IDEA's help window and, yes, it worked!

Solution
Simply open up the IDEA help window and put it behind your working project window. It won't work if you minimize the help window - it has to be open. Works great and doesn't suck resources.

Another solution that works, if you don't mind it, is to float one of the windows (e.g. the debug window). This works well if you have two monitors. Simply click the "float" button on any of the windows and move it out of the way.

Let me know if you have any other ideas or if there is a permanent solution available.

Wednesday, November 21, 2007

Tile OS X Terminal window to Finder with AppleScript

I updated my AppleScript that will tile the terminal window nicely below the frontmost finder window.

For some reason setting the Terminal window bounds is very buggy. I resorted to using the deprecated methods {position and size} and have to loop it three times to ensure that it positions correctly.

I tied this to command-; in my app launcher, QuickSilver



tell application "Finder"
--make new Finder window
activate
if not (exists window index 1) then
make new Finder window
end if
set {x1, y1, x2, y2} to bounds of window index 1
end tell

tell application "Terminal"
activate

if (count of windows) is 0 then
tell application "Terminal" to activate
tell application "System Events" to tell process "Terminal" to keystroke "t" using command down
end if

-- For some reason it can take up to 3 times for Terminal to be properly adjusted
set L to {1, 2, 3}
repeat with xxx in L
set {xx1, yy1, xx2, yy2} to bounds of window index 1
set position of window index 1 to {x1, y1 + {yy2 - yy1}}
set size of window index 1 to {x2 - x1, y2 - y1}
end repeat

end tell

Saturday, November 3, 2007

OS X Leopard - Improved Terminal (almost perfect)

Sorry iTerm, my long friend. I may be giving you up now that Leopard's terminal has tabs and an over-all improved UI. Terminal starts up fast and is pretty dang stable. One gripe - "Why can't I name the tabs!" Ugh, this seems so obvious a feature. Yeah, you can use AppleScript to set the name of the overall window but that isn't all that helpful when you have multiple tabs open. Come on, this has got to be a two-day feature (one to implement, one to test).

Weird thing is that somewhere in Apple-land there is a small team that is responsible for maintaining Terminal. And they are programmers so you'd think that they'd make the shell as nice as possible and think of these features on their own.

Anyway, bravo for the improvements that did make it in but a small "boo" for my couple of wasted hours trying to figure out how to ActionScript custom tab names. Oh Apple, when you fall short, it really hurts.

OS X Leopard - Incompatibilities

I'm an early-upgrader to Leopard. After a week I'm digging it quite a bit. This is my small list of bitches in a otherwise pretty nice OS. I think a lot of the inconsistencies with the Java-based programs has to do with Apple's new GUI rendering. I don't know much more about it but I'm guessing that it broke (well damaged) a lot of SWT and Swing widgets. Mostly I'm posting these in case somebody else is running into the same issues:

  • Intellij - Works but now the UI is unstable. Switching tabs don't always work and a few other creeks and groans. I use this every day so it is a bit painful.

  • Popcorn 3 - Bought this last week for my iPhone so I could scale down movies and move them over. This won't even start up. Funny thing is Popcorn 1 still works fine. Roxio has a message on their site that a fix is forthcoming.

  • TextMate - Breaking Input Managers was an issue from day one. There is a work-around for the "edit in textmate" bug that works well but it is slightly kludgy.


I don't entirely blame Apple, seems to me if you are a maker of Apple software you'd do some work with the pre-releases to ensure your software still works with the upgrade.

Java 6 on OS X

Hey Apple, please be more vocal with your plans for Java. I love my mac and want to continue using it as my main development machine. I don't doubt good things are happening - just would like more (good) buzz out there. I've seen many Java developers be converted to mac (me included - now I own three macs and an iPhone) and then go on to spread the good word.

13949712720901ForOSX

Monday, September 17, 2007

Groovy XML Markup Builder CDATA block

This is one of those things that turns out to be easy but takes a bit of time to figure out.

Using Groovy's builders there are times where you want more control, like when you want to pump out a CDATA block. This turns out to be quite straight forward:

out = new StringWriter()
xml = new groovy.xml.MarkupBuilder(out);
xml.test_xml {
normal("Some text in a normal element")
data('') {
out.write("<![CDATA[Hello]]>")
}
}
println out

Basically since we have reference to the writer we can dump out anything directly to it. I'm sure this works with most builders.

The resulting xml block should look like:

<test_xml>
<normal>Some text in a normal element</normal>
<data><![CDATA[Hello]]></data>
</test_xml>


UPDATE EDIT:

Please note that MarkupBuilder also gives you the special field 'mkp' to do similar actions.

For example:

def out = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(out)

xml.test_xml {
normal("Some text in a normal element")
xml.mkp.yieldUnescaped('<data><![CDATA[Hello]]></data>')
}

println out


Also works and probably is more-correct.

Sunday, August 26, 2007

OS X Finder iTerm applescript window tiling

If you are an OS X developer no doubt you use iTerm. If you haven't set up your unix environment to synchronize between Finder and OS X you should. There are a number of examples but here is the ones I used since I'm a zsh fan:

iTerm Terminal Customization

Now the other thing I wanted was to have a quick-key combination to display both the front finder and iTerm windows and position them nicely tiled. This way I could work in an IDE but have my bare-bones development windows appear in a snap. Here is my applescript code:


tell application "Finder"
--make new Finder window
activate
if not (exists window index 1) then
make new Finder window
end if
set {x1, y1, x2, y2} to bounds of window index 1
end tell

tell application "iTerm"
activate
if not (exists window index 1) then
set myterm to (make new terminal)
tell myterm
launch session "Default Session"
end tell
end if

set {xx1, yy1, xx2, yy2} to bounds of window index 1
set bounds of window index 1 to {x1, y2, x2, y2 + (yy2 - yy1)}
activate
end tell


Store the script in your home/Library/Scripts folder and from there you can assign a hot-key.

Why don't I just use PathFinder? Well I own it and used it quite a bit but there are things that just make me angry about it that hopefully they'll fix.


1. It is a resource hog
2. It doesn't allow me to simply type a path into an input box (actually almost all OS X Finder clones have this issue). OS X developers seem to like the mouse and drilling down.
3. The UI can be quite busy and some things that should be simple (finding files in the current directory or below for example are a pain in the butt.


Anyway, now that I'm doing quite a bit of development on my mac I'm using QuickSilver, Finder, and iTerm with applescript to glue them together. Simple.

What inspired me to dump PathFinder? This great podcast that showed how much hidden value is in the limited basic Finder application and how a little knowledge can make it much better suited to your workflow.

Living with the Finder

Thursday, August 2, 2007

Tomat 5.5 Spring 2.0 log4j debugging

Had to dig for this one a bit to get spring framework debug to show up.

Set up logging in Tomcat 5.5


Note: CATALINA_HOME is probably the environment variable pointing to your tomcat installation.
* put log4j and commons-logging in ${CATALINA_HOME}/common/lib
* put a basic log4j.properties file in ${CATALINA_HOME}/common/classes

for example:

log4j.rootLogger=DEBUG, stdout
log4j.logger.org.apache=INFO

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n

Note: I turned org.apache to INFO to ensure I don't get a few miles of digester logging
Note: Of course you could add file appenders if you wish - I'm keeping this minimal

Make sure commons-logging.jar is not in your WEB-INF/lib directory of your web-app. That seemed to be the thing that switched off the availability for Spring debug logging. I'm sure there are other ways to get this going but this worked for me.

Thursday, July 12, 2007

TextMate ZSH ZShell

TextMate has a bias toward bash and doesn't pick up your environment's path if you are using zsh.

See Shell Commands: Search Path for details

I added the following to my .zshrc file to create/update a .profile file whenever I log into zsh

ECHO PATH=$PATH > .profile


Now bundles like Subversion work as they should :-)