Sunday, April 19, 2009

Tips on Using Amazon CloudFront

If you are serving a lot of content with an audience that is spread out over the globe, then most likely you are in need of putting your content on a CDN (Content Delivery Network).  There are a host of options for you such as LimeLight, BrightCove and Akamai to name a few, but I have found that Amazon offers much of the same functionality at a better price.  Moving your static content to Amazon's CloudFront can really reduce the load on your servers and certainly enables you to serve your content from a place that is closer to the request.  The best thing about it is that it is very very simple to do.  There are some pieces of information that can really save you alot of time and energy if you are converting your site to serve content from CloudFront.  Here are some things that were important to me and that I had to address when moving our site:
  1. Speed
    • Faster delivery of all content from edge locations close to the request
    • Faster load time of a page in the browser
  2. Ability to serve content over HTTP and HTTPS
  3. Simple, straight-forward development
Speed
Amazon CloudFront allows you to manage the content in your S3 buckets and enable them for delivery from edge locations all over the world.  To enable a bucket for CloudFront, it is a simple API call to create a Distribution.  Once your Distribution is provisioned, which usually takes less than 15 seconds in my experience, you will be given a Distribution url which will look something like: http://abcd1234.cloudfront.net .  This Distribution URL can be simply though of as another URL to that bucket in S3.  For instance, if you have a bucket in S3 named "mys3bucket" and you create a Distribution for that bucket, which returned http://abcd1234.cloudfront.net, then you could reference that bucket as http://s3.amazonaws.com/mys3bucket/ or as http://abcd1234.cloudfront.net .  The only difference between the two is that the CloudFront URL will be served from the location closest to the request and the content from that distribution can be served at 1,000 Megabits per second and even more if needed and requested.  For the easy creation of your S3 Buckets and CloudFront distributions, I suggest using S3Fox for Firefox.  With a simple right click on your bucket through S3Fox, you can create a new Distribuion in seconds.
Beyond just offering the serving of your content from an edge location at high throughput, CloudFront distributions have another advantage that can speedup the loading of your sites pages.  Using a single Distribution, you can assign up to 10 CNAMEs to it.  This can really make a large difference when serving content for a page.  Most browsers block and only allow 2-4 parallel connections to a single host when loading content.  Creating 10 CNAME entries all pointing to the same distribution allows the browser to make 10 times the parallel connections it could using a single entry.  I would suggest Steve Souders book High Performance Web Sites: Essential Knowledge for Front-End Engineers for more tips on this.

HTTP and HTTPS Delivery
One of the drawbacks to CloudFront is that it does not support HTTPS delivery of the content.  However, there is some good news here because S3 does support HTTPS.  Because the content that you need to deliver over CloudFront sits in an S3 bucket, then you can also deliver that same content over HTTPS but you cannot take advantage of the edge locations.  This is especially useful when you need to serve a secure site that has a bunch of images that you are already serving from CloudFront.  If you try to serve those images over HTTPS, your users will get a security warning or no image at all because the browser will block it.  So, when you need to serve those secure pages, simply switch your delivery URL to the S3 location instead.  A word of advice here is to create your S3 bucket without using any '.'s.  I know that this really messes with you if you want your URL to look like it is coming from your servers, but it will allow you to serve secure content from S3.  The S3 bucket is served over HTTPS using a wildcarded certificate.  This allows Amazon to serve your content using any prefix as long as it does not have any extra dots ('.').  If your bucket name is 'mys3bucket', then you can create a CNAME record that points 'mys3bucket' to mys3bucket.s3.amazonaws.com.  Because the wildcarded SSL certificate is to *.s3.amazonaws.com, then you can serve your content from https://mys3bucket.s3.amazonaws.com.  While this isn't the perfect solution, it is really nice for serving the same bucket content over HTTP or HTTPS.


Simple, Straight-Forward Development
I am not sure that building scalable applications is ever simple, but I am always looking for ways to make it extremely repeatative and simple for myself and other developers. Because we are using Java for most of our applications, the easiest way for me to do that was through a tag library.  I included all of the logic for switching between the different CNAMES for CloudFront delivery and logic for detecting a secure connection and need to switch to straight S3 delivery right in the tag.  There were some interesting details that were necessary to take full advantage of CloudFront.  One was coming up with a hash scheme for a URL so that it could be cached in the browser and distributing the calls across the CNAMEs evenly.  The second was to make sure that the tag was written so that it was at the request level instead of the page level.  This helped ensure that even an included JSP would not cause a narrowing of the number of CNAMEs that I could use.  This is the method in the tag that does most of the work
protected String getCDNUrl() {
   String result = null;
   if (((PageContext) getJspContext()).getRequest().isSecure()) { 
      result = getCDNSecureUrl();
   } else {
      result = getCDNUrls().get((1 <= getCDNCount()) ? Math.abs(getFile().hashCode()) % getCDNCount() : 0);
   }
   return result;
}
CloudFront has proven to be very useful to me.  I hope with a few of these tips that you can improve the performance of your site.  Please share your experience with me or any other tips that you might have!

Wednesday, April 8, 2009

Google AppEngine supports Java

I should be sound asleep resting up for a big day tomorrow, but I couldn't resist.  I saw an announcement hit Twitter that I have been waiting on for a long while.  Google's AppEngine team has announced support for Java and has also released an Eclipse plugin that makes development much easier.  They are letting the first 10,000 interested in Java into give it a test run, so go over and get signed up.  If you are running Ganymeade then you can grab the Google AppEngine plugin from the update site at http://dl.google.com/eclipse/plugin/3.4.



It was extremely simple to go through the happy path after installation and get my first GWT application up and deployed.  In fact, Google provided all of the code.  Nothing better than a full example to get you going.
After installing the plugin, you will see that there are three new buttons that have been added to the toolbar.
 
They are for creating a new web application, compiling and deploying to AppEngine.  All that you need to do is click on the first button, name your application and give it a package.  After that, simply right click on the project and choose Run As > Web Application.  This will activate your local AppEngine installation and show the application which simply asks your name and gives a confirmation.
 Congratulations!

Friday, April 3, 2009

OAuth and Twitter

 Earlier last month, Twitter released OAuth access to their site and to the API.  Excited about the possibility of integrating some DigitalChalk functions with Twitter, I decided to take a couple of hours to play around with it and see what it offered.  There are lots of examples on the Twitter API site, but none of them are in Java.  What?  Really?  Maybe because it is so easy to do, but I do think that it is worth posting about.  First, a little about the flow of getting your access credentials from Twitter.  The diagram below shows the order of events and some pieces of the data that you need to get started.


For my implementation example I am using a Java OAuth library written by John Kristian, Praveen Alavilli and Dirk Balfanz.
  1. You must get your Consumer Key and Consumer Secret by registering your application on Twitter.com
  2. The goal is to get an Access Token and Secret that you can use to read and/or write to a Twitter users information without having to ask for their username and password everytime.  We also don't want to have to store that information because they may change it on Twitter.com and we don't want to have to synchronize the information. (The user doesn't really want to give that information to us anyway)
  3. The first step is get request a Request Token from Twitter.  You do this by using a timestamp, nonce, oauth version, and your consumer key and signing it with your consumer secret.  Then a request can be made to http://twitter.com/oauth/request_token including the signature.
  4. public TwitterTokenPair getRequestToken(TwitterConsumerCredentials credentials) throws TwitterOAuthException {
       TwitterTokenPair result = null;
       OAuthAccessor accessor = newAccessor(credentials);
       OAuthClient client = new OAuthClient(new HttpClient3());
       try {
          client.getRequestToken(accessor);
          // Build the token pair to return
          result = new TwitterTokenPair();
          result.setToken(accessor.requestToken);
          result.setTokenSecret(accessor.tokenSecret);
       } catch (Throwable t) {
          throw new TwitterOAuthException(t);
       }   return result;}
    
  5. Twitter will generate a Request Token and Token Secret and send them back
  6. Save the Request Token and Token Secret off for later use.  We will use them again after the user has granted us access.
  7. Build up a the URL for a user to access, sending them to Twitter, to grant us access.  This will be done by sending them to http://twitter.com/oauth/authorize?oauth_token=.  You can optionally add a callback URL on the parameters or just rely on the one that you entered on the Twitter site when registering your application.
  8. public String getAuthorizeUrl(String token, String callbackUrl, Map callbackParameters) {
       Map parameters = new HashMap();
       parameters.put("oauth_token", token);
       if (null != callbackUrl) {
          parameters.put("oauth_callback", callbackUrl + "?" + asQueryString(callbackParameters));
       }
       return authorizeUrl + "?" + asQueryString(parameters);
    }
    
  9. If you user grants you access, then your callback URL will be called.
  10. Upon recieving the callback, you now need to request and Access Token from Twitter.  This is very similar to step 3 except you will be using a different URL and the Request Token and Secret that you saved off in step 4.  You will sign your request with your consumer secret and the token secret and send the request for the Access Token to http://twitter.com/oauth/access_token.
  11. public TwitterTokenPair getAccessToken(TwitterConsumerCredentials credentials, TwitterTokenPair requestTokenPair) throws TwitterOAuthException {
       TwitterTokenPair result = null;
       OAuthAccessor accessor = newAccessor(credentials);
       accessor.requestToken = requestTokenPair.getToken();
       accessor.tokenSecret = requestTokenPair.getTokenSecret();
       OAuthClient client = new OAuthClient(new HttpClient3());
       try {
          client.getAccessToken(accessor, HttpClient.GET, null);
          // Build the token pair to return
          result = new TwitterTokenPair();
          result.setToken(accessor.accessToken);
          result.setTokenSecret(accessor.tokenSecret);
       } catch (Throwable t) {
          throw new TwitterOAuthException(t);
       }
       result;
    }
    
  12. Twitter will generate an Access Token and Token Secret and send them back.  At this point, the Application is added to the users Connections tab.
  13. Once you parse the Access Token and Token Secret out of the response, you can use them from that point forward to make Twitter API calls on behalf of the user that granted access.
That is it!  Now you have successfully acquired the Access tokens and can perform the Twitter REST API services for that user.  Because I have granted DigitalChalk the to update my status, the application can do so at its hearts content.


TwitterHttpCommand twitterCommand = new TwitterHttpCommand(credentials);
try {
   Map parameters = new HashMap();
   parameters.put("status", "Just finished OAuth integration implementation of Twitter and DigitalChalk in Java!  Woohoo!");
   response = service.execute(twitterCommand, HttpMethod.POST, accessTokens, "http://twitter.com/statuses/update.xml", parameters);
   printInputStream(response.getResponse());
} catch (TwitterOAuthException ex) {
   System.out.println(ex);
} catch (IOException ioex) {
   System.out.println(ioex);
}



UPDATE: I have been asked to include the code for this experiment.  You will need to download the Java OAuth Library and include it in the classpath as well as enter your Consumer Key and Secret into the twitter.properties file.  The Test.java class should walk you through how I used it to test access to Twitter.  Let me know what you think and if you would like to see anything else added.  You can download the code here: http://bit.ly/Pz55C