Monday, February 28, 2011

The Problem with Component Based Web Architectures

Imagine the following scenario:
  1. You browse to a web site and log in.
  2. After doing whatever you needed to do, you log out.
  3. You keep your browser tab (which is now sitting at a login page) open.
  4. The next day you return to this browser tab and enter your username and password.
Assuming that you enter your username and password correctly, I think it is a safe assumption that the behavior you would expect is for the site to log you in. Anything else could be considered a bug.

Why do I bring up such a simple scenario?  Because if the website was written using JSF and they just used out of the box settings (for example what you would get if you use seam-gen with a Seam application), the above scenario will result in an error!

Component Web Architecture
Both JSF and ASP.NET use a component based web architecture. What this means is that the developer's markup language, whether it is aspx, jsp, or facelets, doesn't directly define HTML. Rather it describes a tree of components. The HTML is generated after the component tree has been built and populated, either by asking the components to render themselves, or by having a separate renderer that traverses the component tree.

Advantages
Component based architecture attempts to leverage the fact that almost all modern programming languages are object oriented. Components are just objects. By using a paradigm that most developers are familiar with the hope is that it'll be easy to use, maintain, extend, etc.

Problem
As you know, the web is stateless. The browser and web server interact by sending messages back and forth. However, the client (browser) and server are two completely disconnected entities. The conversation between them might not actually even be between two entities. A user might save a hyperlink from one page, and then follow the link from a different browser on an entirely different computer. Because of load balancing, or server restarts, the server that handles one message in a conversation isn't necessarily the same as the one that handled the previous message. And even if its the same server, the server might be in a very different state due to handling requests from other clients or other external changes like database state changes.

This presents a myriad of challenges to web developers. Web frameworks provide tools to overcome the above difficulties and still write useful applications. JSF and ASP.NET protect the developers from all of the ugliness through their use of components. Through clever use of cookies, hidden form variables, and session information, these frameworks provide the illusion that you have objects which persist from one page request to the next and that links, form fields, and buttons change the state and or perform actions on these objects.

The problem is that the long lived object model is a leaky abstraction over the reality of stateless messages being sent back and forth. My previous complaints about ASP.NET are caused by trying to paper over this leak.

Component Web Mismatch
To correctly process any form submission (for example login), the first thing the webserver has to do is recreate the exact same component tree structure that was used to generate the page. The appropriate components then get loaded with whatever form data data was submitted, and the component corresponding to the button that was clicked then can have its action called. The challenge is ensuring that the component tree structure that was used when the page was created is the EXACT SAME as the component structure on the form submission. For the simple cases, this is easy. But imagine that the component tree is based on a database query (e.g. listing of items on sale). It's possible that the database has changed between page load and form submission (e.g. items sold to another user). If you naively build the component tree the same way you did originally (e.g. by iterating over results from database), you will get a different component tree which can cause all types of wonky behavior. For example if the user tried to buy the 5th item on the list, they may end up purchasing the wrong item, since the 5th item they saw on the screen isn't the same as the 5th item currently returned by the database.

To solve this problem, when a form is created JSF saves the component tree so that it can be recreated when the form is submitted. This can be stored in a hidden field in the form sent to the client, but since this can be quite large it typically isn't done. By default, JSF saves this in a server session variable. However, if the session times out, this data is lost, and you can no longer recreate the state. This is the cause of the javax.faces.application.ViewExpiredException that happens in the scenario at the top of this post.

Solution
We need web frameworks that don't try to recreate the exact same state on two different requests. There are some very smart people who have (and continue to) work on JSF and ASP.NET. The fact that their solutions are so complex and still tend to drop people through the cracks (based on the questions out there on message boards) is indicative that the approach is flawed, rather than just the implementations. Frameworks like Ruby on Rails which ease the process of writing web apps without hiding the fact that each web request is its own independent action are the long term future.

Monday, February 21, 2011

Crop and Upload Image - Client Side

I want to be able to extend my moving icon program to allow a user to upload their own icon. However, I want to give the user the ability to crop the image so they just upload the portion that they want. Here is how I do that.

Technologies Used
I use the HTML 5 file capabilities to upload the image to the browser. I use the Jcrop plugin to jQuery to actually specify the crop region. I also leaned heavily on another demo I found on the web (http://www.pandamatak.com/people/anand/blog/2010/11/clientside_image_crop_and_then.html) to figure out how to do it, though I have tweaked it some.

HTML
First you need a place to drag the image too.
<div id='cropWidget' height='400' width='400'>  
  <div id='surface'></div>  
  <canvas id='canv1' height='400' width='400'></canvas>  
</div>
The surface is where an image can be dragged from your file system and dropped to. canv1 is the canvas object that will handle image manipulation.

Next we need a place to show the cropped portion of the image.
<canvas id='canv2' height='200' width='200'></canvas>  
The only other portion is a control to put the content, two text controls which will show the size of the cropped image, and a button to upload the image.
<input id="content64" name="content64" type="hidden" />
<div class='field'>  
  <label for="width">Width</label>  
  <br /> 
  <input id="width" name="width" readOnly="true" size="5" type="text" />  
</div>  
<div class='field'>  
  <label for="height">Height</label>  
  <br /> 
  <input id="height" name="height" readOnly="true" size="5" type="text" />  
</div>  
<div class='actions'>  
  <input disabled="disabled" id="imageSubmit" name="commit" 
         onClick="uploadSelection();" type="submit" value="Create Image" />  
</div>
There are a couple of things to note here. First the "readOnly" attribute on the text inputs. The user won't be entering the width and height, they will be calculated by JavaScript. Also, the imageSubmit button is disabled. It does not become enabled until an image has been uploaded. And then on click, the JavaScript function uploadSelection will be called.

CSS
The surface element and the canv1 element need to be superimposed on top of each other for this to look right. This CSS accomplishes this, plus adds a little bit of styling.
<style>  
  #cropWidget {position:relative; width 400px; height:400px; padding:5px;} 
  #surface {position:absolute; top:0px; left: 0px; background:blue;  
            width:400px; height:400px; z-index:100; opacity: 0.50;} 
  #canv1 {position:absolute; top:0px; left: 0px; border:1px solid blue; 
          width:400px; height:400px;} 
  #canv2 {position:relative; border:1px solid yellow; width:200px; height:200px;} 
</style>

JavaScript
Here is all of the javascript. I will explain the parts below.
  1 var cropWidget = document.getElementById('cropWidget');
  2 var surface = document.getElementById('surface');
  3 var canv1 = document.getElementById('canv1');
  4 var canv2 = document.getElementById('canv2');
  5 var imageSubmit = document.getElementById('imageSubmit');
  6 var ctx1 = canv1.getContext('2d');
  7 var ctx2 = canv2.getContext('2d');
  8  
  9 function resize(comp, width, height) { 
 10   comp.width = width; 
 11   comp.height = height; 
 12   comp.style.width = img.width + 'px'; 
 13   comp.style.height = img.height + 'px';
 14 } 
 15  
 16 function displayImage(img) { 
 17   resize(canv1, img.width, img.height); 
 18   resize(surface, img.width, img.height); 
 19   resize(cropWidget, img); 
 20   while(surface.childNodes[0]) surface.removeChild(surface.childNodes[0]);
 21   surface.appendChild(img); 
 22   ctx1.drawImage(img, 0, 0, img.width, img.height);
 23   jQuery("#" + img.id).Jcrop({ aspectRatio: 1, onSelect: cropImage });
 24 } 
 25  
 26 function loadImg(imgFile) { 
 27   if (!imgFile.type.match(/image.*/)) return;
 28   var img = document.createElement("img"); 
 29   img.id = "pic"; 
 30   img.file = imgFile; 
 31  
 32   var reader = new FileReader();
 33   reader.onload = function(e) { 
 34     img.onload = function() { displayImage(img); }; 
 35     img.src = e.target.result; 
 36     resize(canv2, 200, 200);
 37   }; 
 38   reader.readAsDataURL(imgFile); 
 39 } 
 40  
 41 function cropImage(c) { 
 42   var w = c.x2-c.x; 
 43   var h = c.y2-c.y; 
 44   resize(canv2, w, h); 
 45   ctx2.drawImage(canv1, c.x, c.y, w, h, 0, 0, w, h);
 46   $("#width").val(w); 
 47   $("#height").val(h); 
 48   imageSubmit.disabled = false; 
 49 } 
 50  
 51 function uploadSelection() { 
 52   var imgData = document.getElementById('canv2').toDataURL("image/png"); 
 53   $("#content64").val(imgData); 
 54   alert("This would've upload your image to a server, if there was one."); 
 55   return false;
 56 } 
 57  
 58 surface.addEventListener("dragover", function(e) {e.preventDefault();}, true); 
 59 surface.addEventListener("drop", function(e) {e.preventDefault();  
 60                                            loadImg(e.dataTransfer.files[0]);},
 61                          true); 
 62 ctx1.fillText("Drop image here", 60, 100); 
 63 ctx2.fillText("Preview", 60, 100);
Lines 1-5 just create variables for the various HTML objects, and 6-7 get the drawing contexts for the canvases. resize (lines 9-14) is a helper method for changing the size of an HTML component. Lines 12-13 resize the display of the component, while lines 10-11 resize its internal structures, if the component is a canvas. displayImage (lines 16-24) first resizes the HTML components to fit the image. Then any previous image is removed from the surface. Then the image is added to the surface and drawn to the canvas. Finally jcrop is called to enable cropping of this image, calling the function cropImage when a region is selected. cropImage (lines 41-49) resizes canv2 and then draws the portion of the image onto this canvas. Then it stores the width and height into the HTML width and height components and enables the submit button.

Jumping to the end, lines 62-63 put some text onto the canvases. Lines 58-61 enable the drag and drop functionality, setting loadImg as the function that is called when a file is dropped. loadImg (26-39) first ensures that it is actually an image being dropped. Then it creates an HTML <img> tag to contain the image. Line 32 creates an HTML5 FileReader and line 38 reads the file that was dropped. The function defined on lines 33-37 is called when the file has been read. Line 35 sets the source of the <img> tag that was created a few lines above. Line 34 calls the displayImage function once the image has been loaded fully by the browser, and line 38 resizes the crop canvas to a fixed size of 200x200, which also has the effect of erasing any previous image drawn on it.

That is basically the extent of the drag and drop functionality. The only remaining method is uploadSelection (lines 51-56) which is called when the "Create Image" button is clicked. This function gets the base64 encoding of the cropped image and copies it to the HTML element content64 where, if this were actually connected to a server, it would be posted as part of the form back to the server.

Demo
Here is what all of this looks like.

Image Cropper

Drag and drop image here:

Cropped image is here:

Monday, February 14, 2011

Unit Testing - the Ugly

So you've weighed the benefits and drawbacks to unit testing and decided to do some unit testing. Now you will learn a rarely talked about fact, unit testing is ugly.

A unit test is composed of 3 steps:
  1. Set up state
  2. Execute component
  3. Validate state
For any type of complex components, the code for setting up the state and validating the state can be quite large and tedious. If you look at a set of tests for a single component you'll also find that they tend to be very redundant.

To a certain extent, there is nothing you can do. Comprehensive tests, by necessity, will contain a lot of data and a lot of validation. If you are a believer in the DRY principle, the redundancy will drive you batty. Through standard refactoring ideas you can pull out the common setup and validation code into their own methods and helper classes. However, there are two catches.

Almost the Same
Each of your unit tests are presumably testing different aspects of your component. As such the state will have to be different for each test. And the validation steps will be different for each test. Trying to parameterize your extracted setup and validation methods to support your scenarios can be as challenging a design problem as any portion of your application proper.

Self Contained
One goal of unit testing is that each test is self descriptive. That way when the testFrombulatorWithNull fails you can quickly narrow down the problem. Unfortunately, if you have refactored your unit tests according to the DRY principle, the setup/validation information won't be solely in the specific test method. You'll have to understand how the helper methods and classes interact so you can understand the test so you can understand what went wrong.

Example
Imagine we modify the PhoneFormatter example from before to now take a formatting string in its constructor. Now, we have a lot more cases to test, which might look something like this:
  1 import static org.junit.Assert.*;
  2 import org.junit.Test; 
  3 import org.junit.Before; 
  4  
  5 public class PhoneFormatterTest {
  6  
  7   private PhoneFormatter mFormatter; 
  8  
  9   @Test public void testWithSpace() {
 10     mFormatter = new PhoneFormatter("(###) ###-####"); 
 11     check("(757) 555-1234", "7575551234"); 
 12     check("(757) 555-1234", "17575551234"); 
 13     check("555-1234", "5551234"); 
 14     check("911", "911"); 
 15   } 
 16  
 17   @Test public void testNoSpace() {
 18     mFormatter = new PhoneFormatter("(###)###-####"); 
 19     check("(757)555-1234", "7575551234"); 
 20     check("(757)555-1234", "17575551234"); 
 21     check("555-1234", "5551234"); 
 22     check("911", "911"); 
 23   } 
 24  
 25   @Test public void testNoParen() {
 26     mFormatter = new PhoneFormatter("###-###-####"); 
 27     check("757-555-1234", "7575551234"); 
 28     check("757-555-1234", "17575551234"); 
 29     check("555-1234", "5551234"); 
 30     check("911", "911"); 
 31   } 
 32  
 33   @Test public void testDots() {
 34     mFormatter = new PhoneFormatter("###.###.####"); 
 35     check("757.555.1234", "7575551234"); 
 36     check("757.555.1234", "17575551234"); 
 37     check("555.1234", "5551234"); 
 38     check("911", "911"); 
 39   } 
 40  
 41   /* Test more formats here */ 
 42  
 43   private void check(String expected, String number) {
 44     checkImpl(expected, number); 
 45     checkImpl(expected, "9" + number); // check leading 9 
 46   } 
 47  
 48   private void checkImpl(String expected, String number) {
 49     PhoneNumber pn = new PhoneNumber(number);
 50     assertEquals(expected, mFormatter.format(pn)); 
 51   } 
 52 }
As you can see the unit test is now a lot longer. And this is for testing a very simple class that takes a very simple input and has a very simple output. Imagine how much uglier this would be if PhoneFormatter took more complex objects as parameters to its methods and returned objects?

Even with such a simple class under test there is a ton of duplication, each unit test method is almost identical to each other. This has the usual problems of cut-and-paste. For example, if you want to add international numbers to your test, you need to make sure you add this to each and every method, and it could be easy to miss one.

There's also the possibility for confusion. Notice the method check (lines 43-46) checks numbers both with and without a leading 9. A future developer, if they don't carefully follow the code, may not expect this, making it unclear why a particular test fails. In this case, it is probably worth this extra indirection to cut the size of the test class almost in half, but you are bound to come up with examples that aren't so clear.

Conclusion
Obviously, the goal to find the happy medium where your test code is structured to minimize redundancy while still maximizing readability, just like writing any other code. However, the goals of unit testing (test component, find bugs, show example of how component is used) are not the same as writing other code. This means that you may make different decisions as part of your design trade-offs than you would elsewhere in code. When combined with the fact that comprehensive unit tests can't be concise (otherwise they wouldn't be comprehensive), it is likely that at least some of your unit tests will be ugly.

Monday, February 7, 2011

Java Web Services Without Code Generation

With Java 6 it is possible to use annotations to create web services and to create clients to consume those web services without using 3rd party libraries like Axis. If you have control of both the server and the client code you can easily write these pieces without any code generation tools. Here is an example of that.

Server
To create your web service, perform the following steps.
  1. Create a directory for the project.  (mkdir server;  cd server)
  2. Create a package for your code.  (mkdir hello)
  3. Create the interface for the web service.  (hello/HelloPort.java)
      1 package hello; 
      2  
      3 import javax.jws.WebService; 
      4  
      5 @WebService 
      6 public interface HelloPort {
      7   String talk(String msg); 
      8 }
  4. Create the web service.  (hello/Hello.java)
      1 package hello; 
      2  
      3 import javax.jws.WebService; 
      4 import javax.xml.ws.Endpoint; 
      5  
      6 @WebService 
      7 public class Hello implements HelloPort {
      8   public String talk(String msg) { 
      9     return "Hello, " + msg; 
     10   } 
     11  
     12   public static void main(String[] argv) {
     13     Endpoint.publish("http://localhost:8080/Hello", new Hello()); 
     14   } 
     15 }
  5. Compile your code.  (javac hello/Hello.java)
  6. Run the server.  (java hello.Hello)
Now that your server is running, you can view the wsdl by going to http://localhost:8080/Hello?wsdl. If you wanted to, you could've skipped the step where you created the HelloPort interface (and had Hello not implement any interface), but we will use HelloPort in the client.

Client
To create a client that consumes this web service:
  1. Create a directory for the project.  (mkdir client;  cd client)
  2. Create a directory for the Hello interface.  (mkdir hello)
  3. Copy the interface.  (cp ../server/hello/HelloPort.class hello)
  4. Create the client code (Client.java)
      1 import java.net.URL; 
      2 import javax.xml.namespace.QName; 
      3 import javax.xml.ws.Service; 
      4 import hello.HelloPort; 
      5  
      6 public class Client {
      7  
      8   static class HelloService extends Service {
      9     public HelloService() throws Exception {
     10       super(new URL("http://localhost:8080/Hello?wsdl"), 
     11             new QName("http://hello/", "HelloService")); 
     12     } 
     13     public HelloPort getHelloPort() { 
     14       return super.getPort(new QName("http://hello/", "HelloPort"), 
     15                            HelloPort.class); 
     16     } 
     17   } 
     18    
     19   public static void main(String[] args) throws Exception {    
     20     HelloService hs = new HelloService(); 
     21     HelloPort port = hs.getHelloPort(); 
     22     System.out.printf("ws returns [%s]%n", port.talk(args[0])); 
     23   } 
     24 }
  5. Compile the client  (javac Client.java)
  6. Run the program  (java Client <message>)  or
    java -Djava.util.logging.config.file=log.prop Client <message>
And just like that you have written both a client and a server which talk to each other via SOAP. The secondary run line (with the -Djava.util... option) is there in case you don't want to see the logging messages that get printed by default when you run the client.

The URL specified on line 10 is the same URL that you specified on line 13 of the server up above, except with a "?wsdl" parameter. The QName defined on line 11 comes directly from the WSDL. If you look at the wsdl, http://localhost:8080/Hello?wsdl, it'll look something like:
  1 <?xml version="1.0" encoding="UTF-8"?> 
  2 <!--  
  3   Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.1.6 in JDK 6. 
  4 --> 
  5 <!--  
  6   Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.1.6 in JDK 6. 
  7 --> 
  8 <definitions  
  9   xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
 10   xmlns:tns="http://hello/" 
 11   xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
 12   xmlns="http://schemas.xmlsoap.org/wsdl/" 
 13   targetNamespace="http://hello/" 
 14   name="HelloService"> 
 15   <!-- service definition here --> 
 16 </definitions
The targetNamespace and the name (lines 13 and 14 above) correspond to the QName used in the client.

Final Thoughts
This example was for a very specific case where you control both the client and server code and want it to be standalone. Of course, your web service needs may not match this model. If you already have a web container (Tomcat, etc), you can always deploy the Hello web service in that, rather than running it as a standalone application. And, of course, you could write your client in any language, as long as it conforms to the WSDL. Or you can just give customers the WSDL and let them write their own client.

If you have to write a client and don't have handy interface classes (hello.HelloPort in the above example), you can do that as well. You'll just have to generate classes using the wsimport command which comes with Java, but that is beyond the scope of this particular blog post.