Showing posts with label Software Engineering. Show all posts
Showing posts with label Software Engineering. Show all posts

24 December 2013

Trying Continuous Delivery - Part 3

Please note: I am making this stuff up as I go along, so I may have to update each post at a later stage to reflect some changes that I think might make things easier to manage.

In my previous post I set up the basic environment that will host my application for Integration work. Now I can start writing the application and put it into a continuous delivery pipeline. The purpose of these posts is only to put into practice some of the principles of continuous delivery, so I will not spend much time writing about the details of the web application. In fact, the application will be a simple noticeboard, where anyone can write a new post and anyone can read all posts in the system.

I set up my application in Eclipse as a Maven project and start writing my acceptance tests right away. I could use behavioral testing frameworks like JBehave or easyb, but I will keep it simple and use plain JUnit tests written in a format that still defines and asserts application behavior. Just as a useful practice, I like separating my tests into packages that reflect their general purpose:

Package Name Tests Purpose
mywebapp.tests.unit These will test small pieces of functionality. They run very fast and are not supposed to test multiple application layers, so they use test doubles (mocks, stubs, etc) when needed.
mywebapp.tests.integration These will test multiple application layers (e.g. services and data access) and run a bit slower than unit tests.
mywebapp.tests.acceptance Arguably the most important type of tests. These are translated directly from requirements/stories. They are usually the slowest tests to run.

This separation strategy allows me to do a couple of things that will be very useful once I start shaping my delivery pipeline (which will be the subject of a later post):
  • Run individual test suites from Eclipse by right-clicking the relevant package and choose Run As -> JUnit Test
  • Separate the test suites in Maven by using simple include directives for the Surefire Plugin and the FailSafe Plugin
  • Run faster tests early in the feedback cycle and slower tests only after all the faster tests have succeeded

More packages would eventually emerge as I start to automate other types of tests (performance, capacity, etc), but these will suffice for now.
All my acceptance tests will follow a given-when-then pattern, and my first acceptance test will check that anyone is able to add a new comment on the noticeboard.

package mywebapp.tests.acceptance;

import org.junit.Test;

public class AnyVisitorCanWriteNewEntryTest {
 
 /**
  * Loads the noticeboard page.
  */
 private void givenTheNoticeboardPage() {
  ...
 }

 /**
  * Fills the noticeboard form with a new entry.
  */
 private void whenAnonymousUserWritesNewNotice() {
  ...
 }

 /**
  * Submits the noticeboard form.
  */
 private void andSubmitsForm() {
  ...
 }

 /**
  * Examines the noticeboard table and tries to find the
  * unique comment that was used to submit the form.
  */
 private void thenAnonymousUserCanSeeTheNewEntryOnNoticeboardPage() {
  ...
 }

 @Test
 public void anonymousUserCanWriteNewEntry() throws Exception {
  givenTheNoticeboardPage();
  whenAnonymousUserWritesNewNotice();
  andSubmitsForm();
  thenAnonymousUserCanSeeTheNewEntryOnNoticeboardPage();
 }
}

The test will clearly fail because I haven't written any application code yet. It is meant to simulate user actions on a web interface, so it follows a WebUI-centric pattern:
  1. Load a web page
  2. Find some element on the page, e.g. an input box
  3. Perform some operation on the element, e.g. change one of its attributes
  4. Perform some action on the page, e.g. submit a form
  5. Check the result, e.g. different elements are displayed on the page
However, this doesn't necessarily have to be the case for acceptance tests on a web application: if the presentation layer is well decoupled from the business logic (as it should be), then acceptance tests could exercise the business logic directly instead. Either way, there are some important tradeoffs to consider. For example, a UI-centric approach will almost certainly depend on page elements having specific names and/or IDs, which can change many times during the application's development lifecycle and waste valuable feedback cycles and spend valuable development time maintaining and fixing tests that will seemingly break for no reason. On the other hand, one could argue that exercising the business logic directly and bypassing the presentation layer altogether does not actually simulate any user interaction and is therefore not good enough to be considered a "proper" acceptance test. My purpose here is to provide an example for continuous delivery, so my choice of following a UI-centred approach is irrelevant in this instance, but the main point here is that there must be automated acceptance tests as part of the continuous delivery feedback cycle.

From the acceptance test I can work my way down to more granular tests that eventually translate into application code, using a test-driven approach. I won't go into any of those details here, but I will introduce some profiles in my pom.xml in order to neatly separate the different types of tests for my delivery lifecycle.
<profile>
    <id>unit-tests</id>
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            ...
            <configuration>
              <includes>
                <include>**/mywebapp/tests/unit/**/*.java</include>
              </includes>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
    <profile>
      <id>integration-tests</id>
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
              <skipTests>true</skipTests>
            </configuration>
          </plugin>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-failsafe-plugin</artifactId>
            ...
            <configuration>
              <includes>
                <include>**/mywebapp/tests/integration/**/*.java</include>
              </includes>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
    <profile>
      <id>acceptance-tests</id>
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
              <skipTests>true</skipTests>
            </configuration>
          </plugin>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-failsafe-plugin</artifactId>
            ...
            <configuration>
              <includes>
                <include>**/mywebapp/tests/acceptance/**/*.java</include>
              </includes>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
It's probably worth noting the use of skipTests in the integration-tests and acceptance-tests profiles in order to specifically skip the execution of the Maven surefire plugin.

Why did I use profiles? Why didn't I just manage all tests in the main build block? I don't think there is one single right answer here, but my preference for continuous delivery is to neatly separate the different types of tests into individual jobs in Jenkins. By doing this, I can potentially distribute the load of slow acceptance tests across several Jenkins slaves running in parallel, based on the names of test classes or their sub-packages.

For now, I just need 3 Jenkins jobs to run my tests:

Job Name Execution
MyWebapp-Unit-Tests mvn clean test -P unit-tests
MyWebapp-Integration-Tests mvn verify -P integration-tests
MyWebapp-Acceptance-Tests mvn verify -P acceptance-tests

In particular, the job running acceptance tests will package the application into a war file and deploy it to Tomcat before executing the tests. This can be accomplished by using the maven tomcat plugin or Cargo. Yes, I could use Jetty just for testing, but I need to make sure that my acceptance tests are run against an environment that is as similar as possible to my production environment, so I don't want to test on Jetty if I already know that I'm ultimately going to deploy to Tomcat.

... TO BE CONTINUED ...

23 May 2013

Trying Continuous Delivery - Part 2

In the previous post I set up my development tools and my virtual UAT environment, using Subversion to control configuration changes to UAT. Now I can introduce automation in my setup.

There are several platforms that can help with automation, and they all do pretty much the same thing (execute jobs) in slightly different ways, but my vote goes to Jenkins for a few reasons:

  1. It's free.
  2. It's a mature platform with a very large user base.
  3. It is plugin-based, so I can install and configure only the functionality that I actually need.
  4. I can see fairly easily which plugins are actively maintained and use only those.
  5. It's really easy to use.

I could install Jenkins on my laptop using apt-get, but since I already have a local Tomcat I will just download the war file and run it inside my Tomcat container. Later on I will use Tocmat to also run Artifactory, but I shall leave that part for another post.

Jenkins is already a usfeul platform "out of the box", but for my setup I need to add more functionality. There are many plugins available to add new functionality to Jenkins and integrate with different tools and platforms. However, for the purposes of configuring a job, I want to avoid using plugins as much as possible, relying on shell commands instead. This is not always feasible or practical, but that's the intention. The reason is that in a contingency situation where Jenkins might be unavailabl e I can always retrieve the shell commands from the Jenkins configuration and execute them by hand if needed. If I had to rely on Jenkins plugins to do the work for me, I wouldn't be able to keep things going in a contingency scenario. For example, if one of the steps in a Jenkins job was to publish a file to Artifactory, and I used the Artifactory plugin to perform that step, then in the event that Jenkins becomes unavailable I would have to devise a different way to publish the file (for example using Maven, or logging in to Artifactory and using the deployment page). This, however, contravenes one of the principles of continuous delivery: executing a task the same way every time. On the other hand, if I use a sequence of shell commands to achieve the same purpose, then I can always retrieve that sequence of commands from source control and execute it by hand if necessary, thereby operating still within the guidelines of continuous delivery. In many cases I could even temporarily use a cron job until I manage to get Jenkins back online, and nobody would even notice the difference.

Having said that, there are plugins that relate to Jenkins administration activities that I am very fond of. One of them is the SCM Sync Plugin. This plugin allows me to easily store and maintain all the Jenkins configurations (global and job-specific) into source control. Once configured, every change I make to a job's configuration or to the global configuration will be automatically checked in to source control. I create a new subversion repository, configure the SCM Sync Plugin to hook up to it, and check that the initial import worked before I move on.

laptop$ mkdir -p ~/svnrepo/jenkins
laptop$ svnadmin create ~/svnrepo/jenkins
laptop$ svnserve -d -r ~/svnrepo --foreground --log-file ~/svnserve.log --listen-host 192.168.56.1


(a different shell)
laptop$ svn list svn://192.168.56.1/jenkins/trunk
config.xml
hudson.maven.MavenModuleSet.xml
hudson.scm.CVSSCM.xml
hudson.scm.SubversionSCM.xml
hudson.tasks.Ant.xml
hudson.tasks.Mailer.xml
hudson.tasks.Maven.xml
hudson.tasks.Shell.xml
hudson.triggers.SCMTrigger.xml
scm-sync-configuration.xml

This helps tremendously in a disaster recovery scenario where I might have to rebuild the Jenkins server from scratch, in which case I would simply check out the configuration from source control before starting up Jenkins. This is a very efficient and effective way of reconstructing the pre-disaster state of Jenkins exactly the way it was. Even in normal circumstances, I will now be able to revert to a previous known good state in case I happen to make some configuration changes and mess it up really bad, or even just if I don't particularly like the end result for whatever reason.

Now I can create a new Jenkins job that will update the Tomcat configuration on my virtual server whenever an updated configuration exists in source control. As soon as I create the job and hit the save button, its details will automatically be checked into source control thanks to the SCM Sync Plugin. The new job will be called "UAT_Update_Env" and its logical steps are going to be:

1) check Subversion repositories for changes and trigger the job when an update is available
2) SSH into the virtual server
3) update Tomcat6 configuration directories
4) restart Tomcat6 service
5) test configuration changes and fail the job if the test is unsuccessful

In order to SSH into the virtual server from a non-interactive session, I need first to add the virtual server's fingerprint to my local known hosts. I can do this by running an SSH session from the terminal on my laptop. On the virtual server I need to set up a user that will be dedicated to continuous delivery operations, and give this user some elevated privileges because it must be able to update the tomcat configuration and restart tomcat. I have chosen a user called "contdel".

laptop$ ssh contdel@192.168.56.2 pwd
The authenticity of host '192.168.56.2 (192.168.56.2)' can't be established.
ECDSA key fingerprint is 91:b2:ec:f9:15:c2:ac:cd:e2:01:d7:23:11:e0:17:db.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.56.2' (ECDSA) to the list of known hosts.
contdel@192.168.56.2's password: 
/home/contdel

In order to avoid interactive password prompts during the execution of the automated job, I choose to use private/public key authentication with SSH, so I generate the key on the virtual server and scp it to my laptop so that is visible to Jenkins. I could simply store it in Subversion, which is ok for the purposes of writing this series, but in a real production scenario I wouldn't do that.

There is one more thing I need to do on the virtual server, and it's to set up the correct access on the configuration files that I will update through automation and the restart script. I have already set up the user "contdel" on the virtual server, which is in the sudoers list. The permissions on the Tomcat configuration folder are set so that the owner (root) has read/write/execute rights, the group (root) has read/execute rights, and everyone else has read/execute rights.

UAT$ ls -lsah /etc/tomcat6
total 100K
4.0K drwxr-xr-x   5 root root    4.0K May  8 22:18 .
4.0K drwxr-xr-x  94 root root     12K May 20 22:30 ..
4.0K drwxrwxr-x   4 root tomcat6 4.0K May  8 22:18 Catalina
4.0K -rw-r--r--   1 root tomcat6 4.0K Jan 11 02:24 catalina.properties
4.0K -rw-r--r--   1 root tomcat6 1.4K Aug  5  2007 context.xml
4.0K -rw-r--r--   1 root tomcat6 2.4K Dec 13  2011 logging.properties
4.0K drwxr-xr-x   3 root tomcat6 4.0K May  8 22:18 policy.d
8.0K -rw-r--r--   1 root tomcat6 6.6K Jan 11 02:24 server.xml
4.0K drwxr-xr-x   6 root root    4.0K May  8 22:18 .svn
4.0K -rw-r-----   1 root tomcat6 1.5K Nov  4  2010 tomcat-users.xml
52K -rw-r--r--   1 root tomcat6  52K Nov 11  2011 web.xml

As it is, I still need sudo to do an svn update, which is fine from an interactive terminal, but it wouldn't work from a non-interactive Jenkins ssh session. If I did that, I would get an error similar to this:

sudo: no tty present and no askpass program specified
Sorry, try again.

This happens because the sudo command prompts for the user's password, which is obviously not going to work in a non-interactive session. What I can do is change the sudoers configuration to allow the user contdel to restart tomcat using sudo without a password prompt. The instructions on how to do so are on the Ubuntu help site. In the latest version of Ubuntu, the sudoers file will automatically include other configuration files stored in /etc/sudoers.d/ so I can avoid editing /etc/sudoers (which is a good thing) and add my changes to /etc/sudoers.d/contdel, so if I happen to mess up a directive I can easily revert back by removing the new config file and try again.

UAT$ sudo visudo -f /etc/sudoers.d/contdel
...
# Allow user contdel to restart tomcat via sudo without a password prompt
contdel ALL=(ALL:ALL) NOPASSWD:/etc/init.d/tomcat6
...

laptop$ ssh contdel@192.168.56.2 sudo /etc/init.d/tomcat6 restart
contdel@192.168.56.2's password: 
* Stopping Tomcat servlet engine tomcat6
...done.
* Starting Tomcat servlet engine tomcat6
...done.

Note that the password prompt comes from the local ssh command, not from the remote sudo command.

I can make it a little more secure by doing two things:

  1. Restrict the sudoers directive for contdel so that it only applies when the command comes from the local network.
  2. Use private/public key authentication for user contdel instead of username/password. This is particularly useful for Jenkins configurations that might or might not expose the password in the logs (or anywhere else).

However, I am still more interested in getting the update job going, so I'll go ahead and create the UAT_Update_Env job. In my previous post I set up the directory structure in source control that will hold all the relevant files in my project: not only the application code, but also all configuration files for the environment and the database script. The environment configuration files are in $SVN_ROOT/envsetup/[environmentID], which for UAT means $SVN_ROOT/envsetup/UAT. This job will therefore look for source control changes in svn://192.168.56.1/mywebapp/trunk/envsetup/UAT every five minutes, and if any changes are detected it will apply them via SSH to the UAT virtual server.

In terms of how the commands will actually be executed, I have two equally viable options:

  1. I tell Jenkins to open a local shell and execute remote commands on UAT via SSH.
  2. I tell Jenkins to start a slave on UAT that will execute shell commands locally

Just to keep things simple at this point in time, I'll go for the second option and use the SSH Slaves plugin that comes with the latest Jenkins "out of the box". I therefore need to create a new Jenkins slave called "UAT" that will run on my virtual server. Under the hood, Jenkins will connect to my virtual server via SSH, upload the slave configuration and binaries, and launch the slave process on the virtual server. When I set up my job in Jenkins, I will configure it to run only on the newly configured slave, and by so doing all commands and script will be executed by the slave on the virtual server. The root Jenkins directory on the new node will receive all files from the Jenkins master. These files will change often and after a job gets executed a number of times they can build up fairly quickly. I don't want to spend any effort cleaning them up regularly, so I'll just use /tmp as the root Jenkins directory. All files in there will disappear after each reboot.


The UAT_Update_Env job has the following characteristics.
  1. The job is restricted to run onthe "UAT" node only.
  2. The job monitors the configuration files in Subversion for changes every 5 minutes.
  3. The job executes svn update /etc/tomcat6 followed by sudo /etc/init.d/tomcat6 restart.



Running the job verifies that it's all good.

Started by user anonymous
Building remotely on UAT in workspace /tmp/jenkins_slave/workspace/UAT_Update_Env
Cleaning local Directory .
Checking out svn://192.168.56.1/mywebapp/trunk/envsetup/UAT at revision '2013-05-23T16:59:19.322 +1000'
At revision 17
no change for svn://192.168.56.1/mywebapp/trunk/envsetup/UAT since the previous build
[UAT_Update_Env] $ /bin/sh -xe /tmp/hudson603902310922720127.sh
+ svn update /etc/tomcat6
At revision 17.
+ sudo /etc/init.d/tomcat6 restart
* Stopping Tomcat servlet engine tomcat6
...done.
* Starting Tomcat servlet engine tomcat6
...done.
Finished: SUCCESS

In order to test that Tomcat is actually working, I have to fire an HTTP request to the Tomcat server and check that it doesn't spit out an error. To do this I create a new Jenkins job called UAT_Verify_Env that uses curl to connect to the virtual server on port 8080. If a connection error occurs, curl will return an error and the job will fail. Since I am interested in verifying a successful connection and not in the actual output of the curl command, I can redirect the output to /dev/null. The UAT_Verify_Env job therefore executes one simple shell command:

curl http://192.168.56.2:8080 -o /dev/null

This job will be executed on the Jenkins master, not on the UAT slave, in order to verify external connectivity within the virtual network. Running this job on the UAT slave will only verify connectivity to the local network adapter.

Now I can test the full cycle: check out the tomcat configuration to a local workspace on my laptop, then change the HTTP connector port in server.xml, commit the changes, wait for Jenkins to figure out there are modifications in the source repository and fire the update job, then wait for the job to complete and use curl to check that tomcat is actually now listening on the new port. If I change the port to 8081 I will see the job UAT_Verify_Env fail, which is a good thing because it will prove that I have a way to quickly feed back a wrong environment configuration.

Started by user anonymous
Building on master in workspace /var/local/jenkins/jobs/UAT_Verify_Env/workspace
[workspace] $ /bin/sh -xe /tmp/tomcat6-tomcat6-tmp/hudson1161186187774233720.sh
+ curl http://192.168.56.2:8080 -o /dev/null
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
curl: (7) couldn't connect to host
Build step 'Execute shell' marked build as failure
Finished: FAILURE

Changing the port back to 8080 and checking in the changes to source control will get everything back on a happy path.

Started by user anonymous
Building on master in workspace /var/local/jenkins/jobs/UAT_Verify_Env/workspace
[workspace] $ /bin/sh -xe /tmp/tomcat6-tomcat6-tmp/hudson2858331979742011098.sh
+ curl http://192.168.56.2:8081 -o /dev/null
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100  1887  100  1887    0     0   260k      0 --:--:-- --:--:-- --:--:--  921k
Finished: SUCCESS

In a similar fashion as for Tomcat, I can extend this process to also update the MySQL configuration. I will skip the detailed description here, but it follows exactly the same principles as above: make sure that user contdel can perform an svn update on the MySQL configuration folders and files, allow user contdel to restart MySQL via sudo without a password prompt, and add the relevant shell commands to the existing job configuration, then amend the UAT_Update_Env and UAT_Verify_Env jobs in Jenkins to account for canges in MySQL configuration.

13 May 2013

Trying Continuous Delivery - Part 1

Please note: I am making this stuff up as I go along, so I may have to update each post at a later stage to reflect some changes that I think might make things easier to manage.

In a previous post I introduced this series looking at continuous delivery from a practical standpoint, taking a simple web application and automating its lifecycle. This post outlines the initial setup of the continuous delivery system. These are the steps I will follow:

  1. Install VirtualBox, Subversion and Tomcat on my laptop
  2. Prepare Ubuntu Server virtual machine with Subversion and Tomcat
  3. Set up SVN repository and user access list on my laptop
  4. SVN checkin Tomcat settings from virtual server
  5. SVN checkout Tomcat settings onto my laptop
  6. SVN checkin modified Tomcat settings from my laptop
  7. SVN update Tomcat settings on the virtual server
  8. Restart Tomcat on the virtual server and verify changes
Installing VirtualBox and Subversion on my Ubuntu laptop is straightforward with apt-get,
but the Ubuntu Software Centre makes it even easier.


I can then prepare the virtual server by downloading the relevant ISO image and hooking it up to the virtual DVD of a new virtual machine. After installing Ubuntu Server I have a new environment ready to be configured as my integration box.


By default, the virtual machine has a NAT network adapter and later I will configure it to have a host-only connection with a static IP address to my new virtual server, so I can work with it also when I'm not connected to my home network. A host-only connection is also useful to simulate environments that wouldn't normally be connected to the Internet and that's ultimately the kind of adapter that I want to be working with. Before I do that, I need to install/update the relevant tools on the virtual machine: SSH Server, Subversion, Tomcat6, and MySQL, plus a few basic packages to facilitate administration and remote view.

VM$ sudo apt-get update
VM$ sudo apt-get install openssh-server subversion tomcat6 mysql-server X.org zerofree make gcc linux-headers-$(uname -r)

After I got my baseline software on the virtual machine I can create a host-only adapter in VirtualBox (File / Preferences / Network), so I will have two adapters: one NAT adapter (the default) and one host-only adapter.
After all updates are installed, I won't need the NAT adapter any more (at least until the next major package upgrade), so I can either disable it in VirtualBox, or disconnect it (untick the "Cable Connected" check box in the VM settings) without actually removing it altogether. If I ever need to install a new package or perform a major upgrade, I just re-enable the NAT adapter very easily and then disable it right away.


I need my host-only adapter to have a fixed IP address (no DHCP). The reason for using fixed IP addresses is twofold: firstly, because I want my continuous delivery scripts to be simple and deterministic without having to install a name server/DNS just for this system; and secondly, because a production-like infrastructure would most likely use fixed/reserved IP addresses anyway. I don't want to install DNS or any other name resolution service, and I only need to set up just a few virtual machines, so I'll just maintain all hostnames and addresses in /etc/hosts.

127.0.0.1       localhost
192.168.56.1    VM-host      # The VM host
192.168.56.2    UbuntuSRV    # The baseline VM
192.168.56.3    Integration  # The integration VM
192.168.56.4    UAT          # The user acceptance VM
192.168.56.5    PROD         # The production VM

I decide to assign IP address 192.168.56.1/24 to the host-only adapter in VirtualBox, for no particular reason other
than because it's the default address that VirtualBox is using for me. It follows that the IP address on my virtual
server will have to be in the format 192.168.56.xxx in order to communicate with the host (my laptop).
Change the interface configuration on the VM.

VM$ sudo vi /etc/network/interfaces

...
auto eth0
iface eth0 inet static
address 192.168.56.2
netmask 255.255.255.0
gateway 192.168.56.1
...

Restart the network interface on the VM.

VM$ sudo ifdown eth0
VM$ sudo ifup eth0
ssh stop/waiting
ssh start/running, process 1234

Test communication from virtual machine to laptop host.

VM$ ping -c 3 VM-host
PING VM-host (192.168.56.1) 56(84) bytes of data.
64 bytes from 192.168.56.1: icmp_req=1 ttl=64 time=0.828 ms
64 bytes from 192.168.56.1: icmp_req=2 ttl=64 time=1.03 ms
64 bytes from 192.168.56.1: icmp_req=3 ttl=64 time=1.893 ms

--- VM-host ping statistics ---
3 packets trasmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 0.828/0.919/1.037/0.090 ms


laptop$ ping -c 3 UbuntuSRV
PING UbuntuSRV (192.168.56.2) 56(84) bytes of data.
64 bytes from 192.168.56.2: icmp_req=1 ttl=64 time=1.54 ms
64 bytes from 192.168.56.2: icmp_req=2 ttl=64 time=0.760 ms
64 bytes from 192.168.56.2: icmp_req=3 ttl=64 time=0.737 ms

--- UbuntuSRV ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.737/1.012/1.540/0.374 ms

Now we can use the NAT adapter for internect connections, and the host-only adapter for connections to 192.168.56.xxx addresses , so the routing table needs to reflect this separation.

VM$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.56.1    0.0.0.0         UG    100    0        0 eth1
10.0.2.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
192.168.56.0    0.0.0.0         255.255.255.0   U     0      0        0 eth1

VM$ ping -c 3 www.google.com
PING www.google.com (74.125.237.178) 56(84) bytes of data.

--- www.google.com ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2014ms
It looks like the default route is through the host-only adapter, and no route leads to the Internet. I need to change that, so I'm going to use the VirtualBox NAT adapter 10.0.2.2 as a gateway for the default route. This way I can have a host-only connection and a NAT connection, both working, and with Internet access.
When I want a strict host-only connection, all I have to do is untick the "Cable Connected" box in the VirtualBox settings for my virtual server's NAT adapter.

VM$ sudo route del default
VM$ sudo route add default gw 10.0.2.2 dev eth0
VM$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.0.2.2        0.0.0.0         UG    100    0        0 eth1
10.0.2.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
192.168.56.0    0.0.0.0         255.255.255.0   U     0      0        0 eth1

VM$ ping -c 3 www.google.com
PING www.google.com (74.125.237.178) 56(84) bytes of data.
64 bytes from 74.125.237.178: icmp_req=1 ttl=64 time=19.9 ms
64 bytes from 74.125.237.178: icmp_req=2 ttl=64 time=10.7 ms
64 bytes from 74.125.237.178: icmp_req=3 ttl=64 time=12.6 ms

VM$ ping -c 3 VM-host
PING VM-host (192.168.56.1) 56(84) bytes of data.
64 bytes from 192.168.56.1: icmp_req=1 ttl=64 time=0.834 ms
64 bytes from 192.168.56.1: icmp_req=2 ttl=64 time=1.11 ms
64 bytes from 192.168.56.1: icmp_req=3 ttl=64 time=1.876 ms
Looks good, so I'll just make this route persistent and move on.

VM$ sudo vi /etc/network/interfaces

...
...
up route add default gw 10.0.2.2 dev eth0
...
...

VM$ sudo service networking restart
So far so good. Let's get Subversion running and test connectivity from the virtual machine. We'll need a local repository first of all, then run the standalone Subversion server listening on the adapter used for the
VirtualBox host-only connection.

laptop$ mkdir -p ~/svnrepo/mywebapp
laptop$ svnadmin create ~/svnrepo/mywebapp
laptop$ svnserve -d -r ~/svnrepo --foreground --log-file ~/svnserve.log --listen-host 192.168.56.1

VM$ svn info svn://VM-host/mywebapp
Path: mywebapp
URL: svn://VM-host/mywebapp
Repository Root: svn://VM-host/mywebapp
Repository UUID: cbcda313-59ce-438c-844d-f6a9db7a4d30
Revision: 0
Node Kind: directory
Last Changed Rev: 0
Last Changed Date: 2013-05-13 12:57:36 +1000 (Mon, 13 May 2013)
It looks like I have a good baseline virtual server that I can use as a template to create my other virtual environment. Let's start with the Integration environment (INT). I only have to clone the baseline VM in VirtualBox using the 'linked clone' option, also making sure I reinitialise the MAC address of all network cards.





What I want to do now is to import the existing Tomcat configuration from the INT virtual machine into Subversion. This way I will be able to control any modifications to these settings from my development laptop. When I check in new versions of the settings, I will shut down the relevant services on the virtual machine, update the settings' working copies, and restart the services in order to pick up the new settings. In fact, I can even manage these changes from Eclipse, together with the application source code. It is easy to create an empty folder structure in Eclipse on my laptop host and then commit it to Subversion. I can then checkout the relevant folders in my virtual machine, svn add the relevant configuration, and check it all in.

INT$ svn import /etc/tomcat6 svn://VM-host/mywebapp/trunk/envsetup/INT/tomcat6 -m "Initial import for tomcat6 config"
INT$ sudo svn co svn://VM-host/mywebapp/trunk/envsetup/INT/tomcat6 /etc/tomcat6 --force

I need to sudo these commands because these directories are usually owned by root, and adding them to Subversion is an operation that needs write access because it will automatically generate some synchronisation metadata files and folders in the working copy directory. Back on the laptop host, I now need to update the Eclipse project and I will be able to manage configuration changes from there.
To test that this arrangement actually works, I can use Eclipse to change the Tomcat HTTP port on the guest virtual machine. I'll change it back afterwards.
[server.xml (snippet)]
...
    <!-- changed from port 8080 to port 8081 -->
    <Connector port="8081" protocol="HTTP/1.1"
               connectionTimeout="20000"
               URIEncoding="UTF-8"
               redirectPort="8443" />
...


INT$ sudo /etc/init.d/tomcat6 stop
 * Stopping Tomcat servlet engine tomcat6 [OK]

INT$ sudo svn update /etc/tomcat6
U   /etc/tomcat6/server.xml
Updated to revision 6

INT$ sudo /etc/init.d/tomcat6 start
 * Starting Tomcat servlet engine tomcat6 [OK]

Now pointing my browser from the laptop host to the guest address http://Integration:8081 shows the Tomcat welcome page.


This means that now I can successfully drive any Tomcat configuration changes from my development environment.
Also, it means that I can safely revert to any previous revision of the Tomcat configuration if something goes wrong.
In fact, I'll just test that now.

[in my workspace folder]
laptop$ svn merge -r COMMITTED:PREV .
--- Reverse-merging r16 into '.':
U    envsetup/INT/tomcat6/server.xml

laptop$ svn commit -m "Reverting previous commit"
--- Reverse-merging r16 into '.':
U    envsetup/INT/tomcat6/server.xml

INT$ sudo svn update /etc/tomcat6
U    /etc/tomcat6/server.xml
Updated to revision 17.

INT$ sudo /etc/init.d/tomcat6 restart
 * Stopping Tomcat servlet engine tomcat6 [OK]
 * Starting Tomcat servlet engine tomcat6 [OK]
Now pointing my browser from the laptop host to the guest address http://Integration:8081 spits out a browser connection error, but http://Integration:8080 shows the Tomcat welcome page as expected.
To recap, this is what I have so far:
  • a physical host (my laptop) with Tomcat, Eclipse, and VirtualBox
  • a Subversion server running on the physical host, using a local directory as a repository
  • a virtual server (my baseline) with Tomcat, Subversion and MySQL, connected with the physical host using a host-only adapter
  • a virtual server (my integration server) cloned from my baseline, with its Tomcat configuration in the Subversion repository

and this is what I can do with the current setup:

  • change the Tomcat configuration of the virtual server from my development IDE on the physical host
  • easily pick up the latest Tomcat configuration changes on the virtual server
  • easily revert to any previous revision of the Tomcat configurations

Next post will introduce the element of automation in configuration changes.

21 April 2013

Continuous Delivery in Practice - Introduction

I am a big fan of automation, and this passion of mine has taken me to become also a big fan of Continuous Delivery. I may have not searched hard enough, but I can't seem to come across some practical examples of Continuous Delivery, so I decided to make my own for two reasons:
  1. to help myself in learning more about the pratical applications of Continuous Delivery
  2. to hopefully help others in doing the same
So this post is to start a multipart series on the practical implementation of Continuous Delivery for a very simple web application. I will not write much about the application itself because my main focus is to put together and describe the infrastructure and the mechanisms that keep the system always in a releasable state.
It is worth noting that Continuous Delivery does not imply Continuous Release. The idea is to make sure that the system as a whole (not just the application) is always in a releasable state. This way, a release becomes purely a business task that doesn't require lots of coordination efforts with a number of technical teams.

This post series is going to be loosely based on the book "Continuous Delivery" by Jez Humble and David Farley, which is (needless to say) a must read for anyone interested in this topic. The book outlines some fundamental principles of Continuous Delivery:
  1. Create a repetable, reliable process for releasing software
  2. Automate (almost) everything
  3. Keep everything in version control
  4. If it hurts, do it more frequently and bring the pain forward
  5. Build quality in, right from the start
  6. Done means released
  7. Everybody is responsible for the delivery process
  8. Continuous improvement
I will explicitly refer to most of these principles as this post series evolves. It is worth noting that, because of principle n.3, I will make right away a critical decision: if it can't be source-controlled, it won't be part of my technology stack. In other words, I will only use tools/packages/platforms driven by (mostly) text-based configuration (properties files, xml files, etc).

In the first post I will be setting up some virtual environments right from the start before any code is written, and by driving environment changes through source control.

Finally, the obligatory disclaimer:
Please note: I am making this stuff up as I go along, so I may have to update each post at a later stage to reflect some changes that I think might make things easier to manage.

02 June 2012

Boken design series: Gamevil's Fantasy War

With this post I will start a series dedicated to broken design. What's broken design? It's about things that we use that are fundamentally flawed. It's about stuff that seems obvious to some people but not to product designers.
I will start the series with a post about a game for the iPhone that I recently tried. The game is called Fantasy War, produced by Gamevil, and can be found on the Gamevil website and on iTunes.
The purpose of the game is to become the leader of a fantasy realm. You can be human, orc or elf, and you progress through the game by completing some quests, battling the forces of evil in some dungeon, and waging war against other players. As you accumulate experience and resources, you can improve the infrastructure (gold mine and lumber mill), learn new skills (cloth making, leatherworking, etc), upgrade your weapons, and hire stronger and more powerful armies. The graphics resembles some 1990s arcade games, but it's still not bad for a mobile game. The rules are fairly quick to learn without explicit instructions and the level progression gradually unlocks more interesting content. The developers make money selling "runestones", which allow you to do a lot more stuff and get really powerful armies.
Sounds ok, right? Well I actually like the game. It's very easy to play and the content is not bad at all. So where's the problem? What's the broken design?
Well, the problem is that this is not an iPhone game, or at least it's not meant to be. When you install the game, you actually install a simplified internet browser with little or no content. Every time you interact with the game, you actually navigate through a web page. All commands, all icons, all images, are just hyperlinks on a web page. Hang on, so where's the problem? I use internet all the time with my iPhone, and I have a lot of games on my iPhone that need an internet connection to work. No big deal. No, indeed, it wouldn't be a big deal... if the game wasn't so connection greedy! There is no content on the iPhone. Everything needs to be downloaded from the internet, including all the images (and there are a LOT of images!). In other words, this game assumes that it is running on a permanently connected device on broadband speeds... which is exactly the opposite of what an iPhone is. My user experience with this game goes something like this:
  1. start the game
  2. watch the rotating "loading..." icon for a few seconds
  3. watch a dark screen behind the "loading..." icon
  4. watch the text labels of the home screen appear behind the "loading..." icon
  5. watch the graphic background appear behind the "loading..." icon
  6. watch the images loading in chunks behind the "loading..." icon
  7. home screen fully loaded
  8. tap on any activity
  9. watch the rotating "loading..." icon for a few seconds
  10. repeat from point 3
In the end, I spend more time watching the rotating "loading..." icon that actually playing the game. Add to this the fact that there is no fault tolerance built in this simple browser, and you also get to build familiarity with the "check your internet connection" error, after which you can only shut down the game and start it again.
Also add to this the fact that dungeons are time limited (you need to kill the boss within 'N' minutes, or you have to wait for an hour or more before you can try again) and you can see how you can quickly build up frustration.
The cherry on the cake is that if something goes wrong with the game you get some Korean error message popping up, which is ok if you can read and understand Korean (I can't do either).
The verdict: broken design!
This is a basic architectural requirement for anything that is supposed to run on an iPhone: it must (please note, "must", not "should") be built for a seldom connected environment with less-than-broadband speeds. If that's not the case, then don't bother.







05 May 2012

Quality Control



There seems to (still) exist a great deal of confusion in many teams about the concept of continuous integration (CI).

The mighty Martin Fowler explains it in one of his seminal articles (http://martinfowler.com/articles/continuousIntegration.html)

Wikipedia defines CI as the principle of implementing "continuous process of applying quality control" (http://en.wikipedia.org/wiki/Continuous_integration)

Just googling up the term produces nearly 6 million references (http://www.google.com/search?hl=en&q=continuous+integration&btnG=Google+Search&meta=) so one might infer at least two things out of this:

1) that the topic is well covered
2) that the topic is very popular

Why, then, have I found it so difficult to find a proper implementation of continuous integration?

Perhaps it depends on what I mean by "proper implementation", so I thought about what I would really expect to receive from a "proper" CI implementation. In other words, what is the quantifiable value that a good CI process adds to my team?

My concept is very much in tune with the Wikipedia definition, in that CI is one of the many means to an end: quality control. The value of a CI process is in quality control, so it is useless if it does not deliver the means to check the quality of the deliverables.

27 May 2011

What's in a number?


Imagine this discussion.


Developer 1: In the past week our code coverage raised from x% to y%


Developer 2: That's pretty good. There was is still quite a bit of dead code after the recent refactoring so I think we'll get to z% by next week.


Developer 3: I still think we should aim for 100%.


User/Analyst/Sponsor: What does that mean?


Developer 1: It shows how much of the whole code base we can actually test as part of our build process.


User/Analyst/Sponsor: Ok, and why is that important?


Developer 2: It's important to know how much is not covered.



User/Analyst/Sponsor: Oh, so you know how much more you have to work before you get to 100%


Developer 3: Well, not exactly. Our policy is to aim for z%


User/Analyst/Sponsor: Hmmm, and what's the use of z%? What does that mean to us?


Developers: What does it mean to you? Well it means that... hmm... that you know... it means... well... er...




That's not quite my direct experience, but I recently had to explain code coverage to non-coders and I realised how little thought some developers have put into the practical importance of code coverage outside the developers' domain.

I think most developers should be familiar with the concept of code coverage, which is simply the number of lines of code that get executed as part of the test suite expressed as a percentage of the total number of lines of code written. Full coverage (100%) means that my tests manage to execute (cover) all of my code. Less than full coverage means that there is some code that is not being tested. I'm not just talking about unit tests: I am also including integration tests and automated UI tests into the picture.

There have been countless debates on how much coverage is good coverage, including various tools and methodologies, so I won't go into a lot of detail here and I don't want to entertain anyone with yet another post on the miracles of test-driven development (TDD). The main concern here is that we cannot measure how "important" a line of code is with respect to another line of code. If we could, we would be able to come up with an "ideal" coverage target and be happy with that. Unfortunately in practice when I have anything less than full coverage I cannot tell whether the "uncovered" code is going to eventually bite me with a vengeance, or whether it will ever get noticed at all. It all depends on usage.

What's the meaning of an x% code coverage to me as a developer? In all practical terms, if it's less than 100%, then it means nothing. So either I strive for full coverage, or why should I bother? That is the basis upon which code coverage sometimes is ignored altogether, or is used only to produce colorful block charts for project status reports.


Now what does that mean to testers? Testers usually understand the concept of code coverage and many are wise enough to avoid taking code coverage as a measure of quality of deliverables. Why is that? Well, quite simply, my automated tests might well test 100% of my code base, but that doesn't mean they actually test the right thing in the correct way.


So what does code coverage means to me as a tester? In general terms, a code coverage of x% means that, for every new feature I add to the product, or every bug I fix, I have roughly an x% degree of confidence that I am not going to break existing features or resurrect old bugs (provided my tests actually test behavior - not just classes and methods - and that each new bug is reproduced with a test before it gets fixed).

But what's the meaning of it to users and analysts? When they see the regular progress report on the system being developed, what information does code coverage convey to them?


The meaning of code coverage to me as a user is actually more important than one might think. Roughly speaking, a code coverage of x% means that with every action I perform on the system, there is roughly a (1-x)% probability that something will go wrong.
To put it slightly differently (or perhaps more precisely), an x% code coverage means there is roughly an x% probability that my interaction with the system produces the expected result because that's what it was programmed to do. Conversely, there is roughly a (1-x)% probability that my interaction with the system produces the expected result by chance.   The degree of criticality of the system then dictates the degree of acceptance or tolerance for failure (or tolerance to chance), which we can match against code coverage.

19 March 2010

Software Engineering Wishlist #1

This is just the beginning of a wishlist for my software engineering and development world. Things I would like to be able to do, technology trends etc. I expect this wishlist to be continuously expanding, so this is only part 1.

I want to be able to...
...check my continuous integration status and logs from FaceBook.
...get my continuous integration results via SMS.
...update Bugzilla/Jira/whatever discussion threads via Twitter
...do some pair programming with something like Google Docs
...do some pair programming with a some kind of real-time plugin for Eclipse
...bring online and shut down continuous integration nodes with Google App Engine
...bring online and shut down full QA stacks with Amazon EC2
...manage user stories with my smartphone
...write actual code with my smartphone on the train and upload/synchronise it later
...use my smartphone as a code repository for small projects
...use my smartphone's voice recognition capabilities to actually dictate code to it
...perform code reviews with some kind of real-time plugin for Bugzilla/Jira/whatever

More to come.
M.

18 February 2010

Eclipsed by an Ant

I've been stuck on a web project for a while. Well, not really on the project itself, I've actually been stuck on running it with the GlassFish Tools Bundle for Eclipse v1.2.

The bundle comes with two version of GlassFish: v2.1 and v3 Prelude. I never had problems running the project on v3, but every time I tried to use to v2.1 my Eclipse would get stuck during the publishing phase with this message: "Publishing to Bundled GlassFish, waiting for virtual machine to exit..."

It turns out that the difference in the publishing phase between the bundled GlassFish v2.1 and v3 is that the former uses Apache Ant. So I checked the Ant runtime home entries on Eclipse and found out they were messed up, pointing at the Axis2 plugin! I must have happened during the installation of the Axis2 Code Generator Wizard. Fixing the Ant home entries finally allowed me to publish the project to the bundled GlassFish v2.1

Here are the steps in Eclipse 3.4 (Ganymede):
  1. Click on Windows -> Preferences
  2. Expand the Ant node and select the Runtime child node
  3. In the Classpath tab, select Ant Home Entries from the list
  4. Click the Ant Home button to bring up the folder browser
  5. In the folder browser, navigate to the folder where Ant was installed
  6. Click OK to go back to the Eclipse preferences dialog
  7. Click Apply, then click OK
Done.
M.

31 July 2009

Lost in Reflection

The time for a new project had come. I was, as usual, excited about the idea of sowing the seed of a new technological creature and bear it for the initial gestation period, until it was mature enough for someone else to take over the incubation and seeing it through to begin its autonomous digital life.

This time it was about web services. We had already decided to write them in Java, and we had a rough idea of the interfaces we were going to expose, but the rest was "carte blanche". This was particularly exciting because, even though I had worked with web services on other projects, this was the first time I was actually going to design web services from the start, so there I was, rolling up my sleeves and reading the Axis2 reference pages.

The other exciting part was that someone else was going to develop an application that would consume my web services, and due to certain timing requirements in their development lifecycle they would have to do their work in parallel with mine. This introduced an interesting challenge: the interfaces would have to be pretty much rock-solid long before any actual web services code was written.

No problem, I thought. We already knew more or less what interfaces we were going to expose, so I can literally code them in Java, build the WSDL from the compiled interfaces and ship the WSDL to the developers on the "consumer project". This way, we can then work out the web services implementation and the consumer implementation independently.

First things first: the contract is not just about the information that a specific method takes as arguments and the information it will spit out as a response. There is a bit more to it. For example, we knew that in our implementation of the web services every method should have a set of "standard" arguments in its request, like the id of the application that originated the request, or the id of the specific language requested by the consumer (e.g. EN, DE, FR, etc). We also knew every method response must include some sort of status code and status message. Finally, we wanted to also include support for digitally signing both the request and the response.

Easy, I thought, we would obviously stick all of these in the relevant super-classes in our implementation, so I created the related super-interfaces in the package that I would use to create the WSDL.

Just for clarity, it worked out to be something like this:

// Base interface for all requests and responses
public interface WebServiceMessage {
    public String getSignature();
    public void setSignature(String signature);
}

// Base interface for all requests
public interface WebServiceRequest extends WebServiceMessage {
    public String getLanguageId();
    public void setLanguageId(String languageId);
}

// Base interface for all responses
public interface WebServiceResponse extends WebServiceMessage {
    public int getStatusCode();
    public String getStatusMsg();
}

// The request for the method doSomething() in the web service
public interface SomeRequest extends WebServiceRequest {
    public int getSomeArg();
    public void setSomeArg(int someArg);
}

// The response for the method doSomething() in the web service
public interface SomeResponse extends WebServiceResponse {
    public int getSomeResult();
    public void setSomeResult(int someResult);
}

// This is the actual web service
public interface MyWebService {
    public SomeResponse doSomething(SomeRequest request);
}

Just to prove the concept, I carried on and mocked up a basic implementation for a couple more methods, then packaged and published the mock service and proceeded to give it a simple test through SoapUI. I gave SoapUI the address of my service endpoint and it quickly digested the web service's WSDL, presenting all the right methods, which was good... until I asked SoapUI to generate a sample request for one of the methods.

The request was incomplete. There were some elements completely missing. So I checked the WSDL and... shock, horror... the schema for SomeRequest only showed some members but not others!
<complexType name="SomeRequest">
        <complexContent>
          <extension base="impl:SomeRequest">
            <sequence>
              <element name="someArg" nillable="true" type="xsd:int"/>
            </sequence>
          </extension>
        </complexContent>
      </complexType>

Where were the inherited members, like the language id? Lost. Gone. No trace.

Did I use the wrong arguments in java2WSDL? Nope. It turned out that java2WSDL did not support interface inheritance. Digging around online forums and googling up "java2WSDL support inheritance" unfortunately confirmed that. Apparently this is a limitation of the reflection engine they use in Axis2, to be rectified in a future release.

This exercise has now resulted in a couple of learning points for me.

  1. even when you think you have identified your assumptions, think again: there are a lot of implicit assumptions (these are the assumptions that are not actually spelled out) in a piece of architecture, and "support for inheritance" is one of those
  2. after you have identified your assumptions, check them out: it turned out that java2WSDL supported class inheritance, but not interface inheritance
  3. hold on writing your mocks until you've been through the above: I could have saved a whole day of refactoring if I had waited mocking up the web service until I had checked the WSDL generation

Mea culpa.
M.

15 April 2009

Silly Application!

Note to self: always define in detail what parts of what file or resource in what module needs to be localised.
We have this JSF-based web application where the UI is a bunch of .xhtml files, and we sent it off for localisation through third parties.

Sure enough, they did a good job with the strings. We switch the locale, and everything comes up in a different language. Perfect. Then we noticed a bunch of forms would not submit any more, often throwing some javascript errors.

It didn't take long to find the problem: they had localised the names of the controls in the .xhtml files too!
Silly application! If it expects data from a control named "user" on an English locale, why can't it work out that on a French locale the data should be picked up from a control named "utilisateur" instead? :-)

M.

07 April 2009

Architectural Inertia

Inertia: indisposition to motion, exertion, or change (source: Merriam-Webster Online Dictionary)


What happens when you subscribe to some web-based service and then you forget your password? No big deal, you can usually click on a convenient hyperlink next to the logon box labelled "Forgot your password?". That usually takes you to some sort of password-retrieval process involving the service provider sending you an email with a specially crafted URL; you then open that email, click on that URL, and enter a new password in the web form that has just opened up for you. Job done.

There are many variations on the theme, some of which are more involved than others, for example asking a whole set of personal questions to confirm your identity, but the general idea is always the same, and in many cases you can also retrieve your username through a similar procedure. These are good examples of helping users help themselves when a problem arises, or automated technical support.

Web applications, however, are moving away from usernames in favor of some other identifier that is more directly linked with the user, like the user's primary email address.

A number of web application designers, therefore, thought it would be a good idea to keep the same theme for automated tech support by offering right next to the logon box a convenient hyperlink labelled... "Forgot your email address?"

:-)

31 March 2009

The Cloakroom Pattern

I am writing this in the context of a web application.

In the "good old days", browsers were single-viewed: there was no such thing as tabbed browsing. If you wanted to browse multiple web pages at the same time, you had to open multiple browser windows. Web architectures, application servers and frameworks have evolved to satisfy this single view of the world by managing context at three different levels: application, session and request.

A request exists when the client sends a message to the server. The moment I open my browser google up some stuff, a request springs to life. This can store transient (short-lived) information, like the terms I want to search for, which are unlikely to be re-used after the request has been satisfied.
At the application level, a context exists for the entire life of the application, until the server is restarted. For example, I might need to audit certain user activities to a database, so I might want load the database connection details when my application starts up and store them in the application context.

Somewhere between the application context and the request context, a web application might also want to manage a user session. For example, when I am browsing my web mail, there must be some kind of information that persists between one request and the other, so that for example I don't need to login again if I move from one mail item to the next.

How does caching takes place and why?

Let's say I sell pizza, and that part of my web application has a search function that goes through my catalog and returns to the user all the pizza styles that match the user's choices. Let's also say that the application stores the search criteria in an object that can be cached, let's call it searchObj, so if the user refreshes the page (without changing the search criteria), the application saves time and resources by simply re-using the same data instead of making a new round trip to the database.

According to what we said above, if searchObj needs to be persisted across requests, it makes sense to cache it at the session level.

So here I am as a potential customer using this pizza web application, searching for pizza that contains ham, so I type "ham" in the input box, click the submit button and look at the resulting list. All the listed pizzas have ham in the ingredients. If I happen to refresh the browser, the application simply re-uses the same list without making a new round trip to the database.

Now let's say I open a new browser tab (not a new browser window) to display the results for a different search. This time I want to search for pizza that contains olives, so I type "olives" in the input box, click the submit button and look at the resulting list. All the listed pizzas have olives in the ingredients. Great.
Now I go back to the previous browser tab, the one with ham-based pizzas, and hit the refresh button. All the listed pizzas now have olives in the ingredients.


What happened?

It happened that searchObj was overwritten by the second search, but how?
Let's think of this scenario in a different way. Let's say I need some milk, and that I suffer from particularly bad memory, so before I forget I decide to write "Milk" on a post-it note and stick it to the door, then I go to get your jacket, car keys, etc. Now let's say my lodger, the sentient invisible pet chimp Iain, needs some fruit juice but instead of writing "Fruit Juice" alongside "Milk" on my post-it note, he decides to replace my post-it note with another one saying "Fruit Juice". Now I'm ready to go out, but of course I have forgotten what I needed to buy, so I pick up the post-it note from the door and happily go on to buy... fruit juice!

In this example, the post-it note is searchObj, Iain and I are the request-scope beans activated from two different tabs of the same browser, and the door is the session. Assume my house only has one door, the entrance/exit door (multiple tabs on the same browser share the same session).


How can we solve the problem?

In terms of "post-it note on the door", it's fairly easy: we draw two squares on the door and label them "Marco" and "Iain". Now we can use our own post-it notes, as long as we stick them in our own designated area on the door.


How does that translate into a web application?

We need to think of this type of context as sitting somewhere between the request scope and the session scope. If we think of each browser tab as a different "view" of our user session, then we can talk of view-level context and view-scoped objects. However, this is not a built-in functionality in the well-known web application frameworks or containers, so we need to simulate it, but how?

In the above example, we said the door represents the session, so we need to stick into the session some kind of container that can hold labelled compartments. How about a Map, for example a Hashtable? Yep, could do with one of those, but how do we actually generate the keys? In other words, how do we make sure that each tabbed view of the same browser unequivocally identifies itself when posting information to the session and retrieving information from the session?

I'm not sure we can handle the "unequivocally" part, but here's what I would do: I would use the coat check pattern, also referred to as the cloakroom pattern. I don't think you'll find that in reputable software engineering books, so don't bother looking.

This is a snippet from Wikipedia's definition of "Cloakroom".
"Attended cloakrooms, or coat checks, are staffed rooms where coats and bags can be stored securely. Typically, a ticket is given to the customer, with a corresponding ticket attached to the garment or item."

In particular, you'll see that tickets are generally given away in sequential order, and that you don't actually need to show personal ID documents when picking up your coat: you simply produce the ticket that was given to you when you gave them your coat. For our web application, the tickets are issued by some sort of decorator class that intercepts HTTP requests and does something like this...
  1. check if there is a querystring variable called "coatNum" (or whatever you fancy)
  2. if there is one, do nothing, otherwise increment an internal counter and use it to decorate the querystring by appending the "coatNum" variable name and value
For a JSF application, this might be a phase listener (maybe for the RESTORE_VIEW event?). For an IceFaces application, things have already been worked out in the form of the "rvn" querystring variable.

For added security, some might argue that the view number should not be easily guessed, so sequential numbering is actually discouraged, but remember that we are talking about different tabs or views of the same browser window. In any case, just for clarity, I will stick to a counter that gets incremented every time it's used.

There is a second part to this: once we know what view number the request wants to use, how do we use that view number to organise our view-scope objects? We said we could use a Map, but where does that live? In the real life coat check scenario, let's say at a classical concert, there can be multiple coat rooms, with each coat room used by multiple punters. This might suggest that the Map holding view-scoped objects should be application-scoped. Even though it is possible to do so, it would require additional overhead in terms of resources, because *all* view-scoped objects for *all* users in the entire application. Also, we would have to write additional code to manage object expiry and cleanup, otherwise we would see the Map growing to infinity and beyond. There are also some security and privacy concerns, since every request would have access to *all* view-scoped objects.

One solution is therefore to stick the Map in the session, or a session-bound bean. As a result, the internal counter that identifies the view number must also be session-bound, so that it starts at zero every time a new session is generated.

In summary, here is what I would do every time a new request comes in:
  1. use an internal session-bound variable to generate view identifiers (e.g. a counter) and a session-bound Map to cache view-level objects
  2. intercept requests and check for a query string variable that identifies the view number
  3. if it's not present, then decorate the query string with a variable that identifies the view number
  4. if the session-bound Map already contains an object for the given view number, then discard the object received from the request and re-use the cached object instead, otherwise take the object from the request and cache it
  5. process the request and return a response to the client
Hold on a minute... what if the request actually uses more than one object?


In that case we don't simply have a session-bound Map, but rather a Map of Maps. In other words, the session-bound Map will still be keyed by view ID, but the value for a given view ID will be a Map of objects, keyed by object ID. We can therefore talk about a "view map" being a collection of "object maps".
This is the revised workflow:
  1. use an internal session-bound variable to generate view identifiers (e.g. a counter) and a session-bound Map (the view map) to cache view-level object maps
  2. intercept requests and check for a query string variable that identifies the view number
  3. if it's not present, then decorate the query string with a variable that identifies the view number
  4. for the view-level object that should be cached, find its identifier (might well be a simple obj.toString() call)
  5. if the view map contains an object map for the given view ID, then retrieve it, otherwise create a new object map and put it in the view map for the given view ID
  6. if the object map contains an object with the same object ID, then discard the object received from the request and re-use the cached object, otherwise take the object from the request and put it in the object map
  7. process the request and send a response to the client
The mechanism can also be extended with an expiration algorithm that kicks in on steps 5 and 6, so that cached objects are refreshed from time to time if needed, but that is another matter altogether.

23 October 2008

Expressions in IceFaces Navigation Targets

I must write this lest I forget.

I've been working on an exception handling mechanism for a JSF-based application using IceFaces, and I was thinking... "why oh why can't we dynamically navigate to error pages using a navigation rule?"
Navigation rules in the faces-config.xml look like this:
<navigation-rule>
    <from-view-id>/somePage.xhtml</from-view-id>
    <navigation-case>
        <from-outcome>error</from-outcome>
        <to-view-id>/errorPage.xhtml</to-view-id>
    </navigation-case>
</navigation-rule>
That is fine but very limiting for my navigation purposes. What if I want to navigate to a different page according to the actual error code? In other words, I want to be able to do this:
<navigation-rule>
    <from-view-id>/somePage.xhtml</from-view-id>
    <navigation-case>
        <from-outcome>error</from-outcome>
        <to-view-id>/#{errorBean.errorCode}.xhtml</to-view-id>
    </navigation-case> </navigation-rule>
Well, it seems that I can't do that with the Sun JSF RI or IceFaces, so I decided to make it happen. I figured that if I wanted to add expression evaluation in a navigation target URL I needed to write a view handler. With IceFaces, the application view handler is com.icesoft.faces.facelets.D2DFaceletViewHandler, which is reponsible for setting up the direct-to-DOM rendering etc, so I needed to extend that class and find what methods I needed to override in order to get me to where I wanted to be. After a bit of experimentation I found there are two scenarios:
Scenario #1: Navigation Rule With Redirection
This is where the navigation rule has a <redirect/> tag. The method in D2DFaceletViewHandler that handles this is
public String getActionURL(FacesContext context, String viewId)
Scenario #2: Navigation Rule Without Redirection
This is where the navigation rule does not have a <redirect/> tag. The method in D2DFaceletViewHandler that handles this is
public void renderView(FacesContext context, UIViewRoot viewToRender)
The way I decided to process the expression is very simple, almost elementary:

  1. Parse the URL/ViewId looking for a sub-string that begins with '#{' or '${' and ends with '}' 
  2. capture the sub-string and create a value binding to evaluate the expression 
  3. Replace the expression in the URL/ViewId with the actual value 
  4. Process the newly evaluated URL/ViewId

Before I get any comments on step 1... no, I don't like regex because my brain just doesn't get it, and it takes me considerably longer to figure out a regex pattern to capture such a simple substring than to actually write a few lines of code to do the parsing.

So here's my (edited) code.
/**
* Constructor
*/
public MyViewHandler(ViewHandler delegate) {
    super(delegate);
}

/**
* Processes a view id that may contain an expression, by evaluating the
* expression and replacing the expression tag in the original view id with
* the expression result.
*
* @param context The faces context.
* @param viewId The view id to process.
* @return The processed view id.
*/
private String processViewId(FacesContext context, String viewId) {
    String processedViewId = viewId;

    int startExpression = processedViewId.indexOf("{") - 1;
    if (startExpression > 0) {
        char expChar = processedViewId.charAt(startExpression);

        // expressions start with # or $
        if ((expChar == '#') || (expChar == '$')) {
            int endExpression = processedViewId.indexOf("}", startExpression);

            if (endExpression > startExpression) {
                // viewId contains an expression
                String expression = processedViewId.substring(startExpression, endExpression + 1);

                try {
                    ValueBinding vb = context.getApplication().createValueBinding(expression);

                    if (vb != null) {
                        String evaluatedExpression = vb.getValue(context).toString();

                        // replace the expression tag in the view id
                        // with the expression's actual value
                        processedViewId = processedViewId.replace(expression, evaluatedExpression);
                    }
                }
catch (ReferenceSyntaxException ex) {
                    // do nothing: processedViewId = viewId;
                }
            }
        }
    }

    return processedViewId;
}

/**
* Used to process a URL that may contain an expression. If a navigation
* rule in the faces configuration file has a <redirect> tag, this
* method will be used to process the URL specified in the
* <to-view-id> tag
*
* @see javax.faces.application.ViewHandler#getActionURL(FacesContext, String)
*/
@Override
public String getActionURL(FacesContext context, String viewId) {

    String processedViewId = super.getActionURL(context, viewId);
    processedViewId = this.processViewId(context, processedViewId);

    return processedViewId;
}

/**
* If a navigation rule in the faces configuration file does not have a
* <redirect> tag, this method will be used to process the URL
* specified in the <to-view-id> tag
*
* @see com.icesoft.faces.application.D2DViewHandler#renderView(FacesContext,
* UIViewRoot)
*/
@Override
public void renderView(FacesContext context, UIViewRoot viewToRender)
throws IOException, NullPointerException {

    String viewId = this.processViewId(context, viewToRender.getViewId());
    viewToRender.setViewId(viewId);

    super.renderView(context, viewToRender);
}
To use my spanking new view handler I just have to change the application section in the faces-config.xml file:
<faces-config>
    <application>
        ...
        ...
        <view-handler>
            <!--
            com.icesoft.faces.facelets.D2DFaceletViewHandler
            -->

            myViewHandler
        </view-handler>
    </application>
</faces-config>
M.