Tuesday, March 22, 2011

twilio4j – Twilio Call Flows in Java

I have been using Twilio for over a year now for a variety of things, and I love those Twilio folks! They have well thought out APIs, easy to read documentation, responsive employees on their user forums, and a nice pay-as-you go model of doing business.

This past week I put together a Java library called twilio4j, which makes assembling call flow state machines easy and readable (and kinda fun IMHO!). This library offers some readability and maintainability improvements:

  • It is very easy to identify the states of my state machine, and how they connect.
  • Receiving parameters and maintaining user state variables is uniform in the API, and easy to read.
  • Composing outbound TwiML is pure java, but in a very declarative style, and very readable.

I’ve made twilio4j open source under the Apache 2.0 license, and hosted the project at code.google.com. The code is functioning, and I have the bulk of the documentation filled in. So it’s time to put it “out there” and get feedback and find bugs. A jar file can be downloaded here.

So, what does it look like?

(Want the short version? Here’s the Quick Start.)

Here’s an example application: I want to call my Twilio phone number and be prompted to enter a code, then be prompted to record a voice message, have the opportunity to hear it and re-record it if I want, and finally be able to accept the recorded message. The state machine on paper looks like this (click pic to enlarge):

TwilioCallFlowStateMachinenocode

First, you represent all the states of the state machine with an enumeration. Enumerated values are easy to spot in code, and their names can capture some meaning of the state, addressing one of my readability concerns. Our enum class looks like this:

public enum VoiceRecordState {
    GATHER_CALL_IN_CODE,
    CHECK_CALL_IN_CODE,
    RECORD_MESSAGE,
    REVIEW_MESSAGE,
    REVIEW_MESSAGE_CHOICE,
    MESSAGE_READY_GOODBYE,
}

 

And then you extend the (generic) TwilioStateMachineServlet<E> class, using your enumeration class to type the new class. It looks something like this:

public class VoiceRecordServlet extends TwilioStateMachineServlet<VoiceRecordState> {
    public VoiceRecordServlet() {
      handler(GATHER_CALL_IN_CODE).respondsWith(
          gather(
              say("Enter your 6 digit code.").voiceWOMAN()
          )
          .numDigits(6)
          .action(CHECK_CALL_IN_CODE)
      );
      // other state handlers here, etc.
      handler(CHECK_CALL_IN_CODE).respondsWith( ... );
      handler(RECORD_MESSAGE).respondsWith( ... );
      handler(REVIEW_MESSAGE).respondsWith( ... );
      handler(REVIEW_MESSAGE_CHOICE).respondsWith( ... );
      handler(MESSAGE_READY_GOODBYE).respondsWith(new TwilioHandler() {
          @Override
          public TwiML getTwiML(TwilioParameters params) {
            String recordingUrl = params.getUserParams().get("my_param_key");
            doTheThingWithTheRecording(recordingUrl);
            return say("The message is now ready. Goodbye.").voiceWOMAN();
          }
      });
    }
}

 

Whenever an ‘action’ (or ‘callback’) attribute is needed, TwiML expects a URL. But you just supply any of your enumerated states. The parent servlet class will map that enum into the path of the servlet’s url, e.g. /t/voicerecord/CHECK_CALL_IN_CODE  would be action url that our ‘gather’ uses in the example code above. It’s nice to know this, but frankly we should not have to worry about the actual urls. We can just think in the realm of our clean enum state names.

Note how the TwiML is expressed in a declarative style, although it is still just Java code. This resolves another of my readability issues. The trick was to use constructors with variable number of arguments. Then you can write a comma separated list of verbs in a nested style. One minor nit is that the corresponding attributes are chained on at the end of a verb, after the closing parenthesis, rather than appearing above like it would in XML. But I still find it much more readable than this:

                TwiMLResponse twiml = new TwiMLResponse();
                Gather gather = new Gather();
                gather.setNumDigits(1);
                Say say = new Say("Review your message.");
                say.setVoice("woman");
                gather.append(say);
                Play play = new Play(recordingUrl);
                gather.append(play);
                Say say2 = new Say("Press 1 to accept this recording. Press 2 to record the message again. Press 3 to quit.");
                say2.setVoice("woman");
                gather.append(say2);
                twiml.append(gather);

 

Another noteworthy item is that you are given access to a userParams object, which is a HashMap of Strings which gets persisted as a cookie to the Twilio client for the duration of the phone call. So there is a very easy place to keep some user application state, and it is consistently presented to you in a TwilioParameters arg in your handler.

Another thing to highlight here is that once you leave a particular state (from a Gather or a Record, or a StatusCallback – whatever), knowing what parameters to expect are now nicely organized in the API. You are always passed a TwilioParameters object, which lets you access everything. For example, to access the parameters posted by a Record, use the params.Record() object to maybe grab the recordingUrl for example:

        handler(REVIEW_MESSAGE).respondsWith(new TwilioHandler() {
            @Override
            public TwiML getTwiML(TwilioParameters params) {
                RecordParameters rp = params.Record();
                if ( rp.isHangup() ) {
                    doAbandonedCallCleanup();
                    return hangup();
                } else {
                    String recordingUrl = rp.getRecordingUrl();
                    params.getUserParams().put(U_RECORDING_URL, recordingUrl); // save the recording url for another state.
                    return gather(
                        say("Review your message.").voiceWOMAN(),
                        play(recordingUrl),
                        say("Press 1 to accept this recording. Press 2 to record the message again.").voiceWOMAN()
                    )
                    .numDigits(1)
                    .action(REVIEW_MESSAGE_CHOICE);
                }
            }
        });

 

I hope this post shows enough highlights to demonstrate some of the readability improvements that twilio4j offers. I am already finding it more fun to code with this library rather than forging machines from raw urls, cookies, and posted string params! :-)

A jar file can be downloaded here.

Monday, March 21, 2011

Fronting Eclipse Jetty server with Apache and mod_proxy

Lately I have been developing apps for App Engine and I use Eclipse with the Google Plugin for Eclipse. This means my development server in Eclipse is Jetty. In a previous post, I described how I used an apache server (with a “real” SSL certificate, etc.) to front my development machine for part of my development process. I used mod_jk to connect to my Tomcat server in eclipse.

But with the Google plugin for Eclipse, the server is Jetty – not Tomcat.  Jetty does permit a mod_jk connection, and I would have preferred to do this since I already knew how to do it. However, I could not find the Jetty config file, or otherwise control the parameters of Google’s Jetty instance. (I even asked the community…)

Further reading suggested that I should avoid mod_jk with Jetty anyhow. This particular page suggested that I use mod_proxy to do what I wanted. So I had some homework to do on mod_proxy. I knew the architecture would be the same as before, just with mod_proxy:

how_to_vpn_dev_mod_proxy 

After reading the Apache mod_proxy docs, I learned that what I want is referred to as a “reverse proxy” in their language. This is where you configure an public apache server to forward certain requests to specific backend servers. There are a variety of applications possible, like load balancing, and caching, etc. But all I needed was the simplest use case. I was quite relieved that setting this up was straightforward. The biggest hurdle in my case was the fact that I had to rebuild apache with the right options to include mod_proxy_http, which was not originally compiled on my box.

I had to add these entries to my httpd.conf:

LoadModule proxy_http_module libexec/apache22/mod_proxy_http.so

 

and:

##
# turning on a "reverse proxy" to reach the Jetty server on my dev box during development.
# read about it here:
http://httpd.apache.org/docs/2.2/mod/mod_proxy.html#proxypass
##
ProxyPass /dev
http://10.1.0.67:8888
ProxyPassReverse /dev http://10.1.0.67:8888

 

Lucky me. Loading http://mysite.domain/dev gave me content from my dev server. Happy day!

Note: the same caveats apply regarding cookies and domains. There are some additional apache directives, ProxyPassReverseCookieDomain, and ProxyPassReverseCookiePath to help rewrite those values, if you are setting them.

Fronting an Eclipse dev server with Apache and mod_jk

Last year I began to integrate Google Checkout into a project, and I needed my development server to respond to a “real” https request. I was developing with tomcat in eclipse, and I already had a production machine (co-located elsewhere) already running apache with SSL fully configured with a proper certificate for its domain, etc. I also had a VPN connection to that production machine from my internal network.

My solution was to setup a mod_jk connection from apache directly to my development machine. I configured it so all incoming traffic with url “/b/*” would be directed to my development machine, and served back out through the public server, via https, etc.

how_to_vpn_dev_mod_jk

Here’s what I did:

1) I added the mod_jk module in my httpd.conf. (Note that you may need to compile a mod_jk.so file, which is beyond the scope of this post.)

LoadModule jk_module libexec/apache22/mod_jk.so

 

2) I added configs for mod_jk in httpd.conf:

<IfModule jk_module>

# Where to find workers.properties
# Update this path to match your conf directory location (put
# workers.properties next to httpd.conf)
JkWorkersFile /websites/yourpath/etc/apache22/workers.properties

# Where to put jk shared memory
# Update this path to match your local state directory or logs directory
JkShmFile     /websites/yourpath/log/mod_jk.shm

# Where to put jk logs
# Update this path to match your logs directory location (put mod_jk.log
# next to access_log)
JkLogFile     /websites/yourpath/log/mod_jk.log

# Set the jk log level [debug/error/info]
JkLogLevel    info

# Select the timestamp log format
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "

</IfModule>

## my mod_jk mounts, same for SSL section too
Include etc/apache22/mod_jk_mounts.conf

 

3) I created the worker.properties file:

# Define workers using ajp13
worker.list=worker1,worker2

# Set properties for worker1 (ajp13)
worker.worker1.type=ajp13
worker.worker1.host=localhost
worker.worker1.port=8009

# Set properties for worker2 (ajp13)
worker.worker2.type=ajp13
worker.worker2.host=10.1.0.67
worker.worker2.port=8009

 

4) I created the mod_jk_mounts.conf file:

<IfModule jk_module>

# the /a path is for the local prod tomcat
JkMount  /a/* worker1

# the /b path is dev, points to 10.1.0.67
JkMount  /b/* worker2

</IfModule>

 

5) I restarted my apache server. I loaded My website with a URL like “http://mysite.domain/b/quux” instead of “http://mysite.domain/a/quux”. Or I’ve used the dev url for testing with Google Checkout, or Twilio.

A minor caveat: If you are using cookies, and you are setting a specific domain, be mindful that the http client is connecting to your public server, and you will only receive cookies that match the domain of your public server.

I have also used this technique to aggregate backend servers during development to solve “same origin policy” problems with javascript elements. Very useful.

This has worked like a champ…  …until I began using the Google Eclipse Plugin, which uses Jetty as a server rather than Tomcat. But how I solved that is the next story…

Sunday, March 13, 2011

Doodle 4 Google (for grownups)

Today I sat at the table with my daughter while she was working on her Doodle 4 Google entry. I was doodling on one of the entry templates with a pencil, and sketched out this crazy thing.

doodle4google

I like computing machine thingies, and this just “came out” of my pencil. It was almost as fun as writing code!

Wednesday, March 9, 2011

Unable to compile JSP on AppEngine

If you are seeing this JSP compile error in eclipse:

An error occurred at line: -1 in the generated java file

 

The correct answer is to move you AppEngine SDK jar higher in the build path so that the correct JspFactory is found by the class loader. (There is a class by the same name in the GWT jar file.)

Kudos to Mike’s post here, where he gives specific instruction, approximately this:

1) Right click project, select “Properties”
2) Select Java Build Path
3) Select Order and Export
4) Move “App Engine SDK” up so it is above “GWT SDK”

Android 2.3.3 “Gingerbread” is killing my battery!

I have a Nexus One which recently received the Gingerbread update. I’ve noticed that my battery life has not been as good as before. Over night last night I captured some evidence. I put the device on the nightstand, and this morning it was dead. Frankly, I had barely used the device that day. The disturbing part is the amount of battery consumed by Android OS.

I plugged it into my USB and I grabbed these screenshots:

gingerbread_os_eats_battery  gingerbread_os_eats_battery_2 gingerbread_os_eats_battery_info

I have found similar complaints at phandroid, and here, and there is a bug report thread already started.

I would love to be able to drill down deeper and find out what is actually chewing up battery.