h1

+6-3=1

August 20, 2008

After quite a long break, I finally played an OTB game. Naturally, I expected to be out of form and as I was black against a higher rated player, I didn’t go in with too much pressure. Surprisingly, I played reasonably well and gained an advantage out of the opening. Sadly, I couldn’t hold on to my advantage and went down after the time-control (35 moves).

1. e4 c5 2. Nf3 d6 3. d4 cxd4 4. Nxd4 Nf6 5. Nc3 a6 6. f4 e5 7. Nf3 Nc6

I have started playing the Najdorf only recently (I used to play the Dragon but have given it up for the most part after a string of poor results). Online, I have primarily faced Be3, f3, Bg5, Be2 or Bc4. 6.f4 was new to me. After my 7th move, I was prepared for 8.fxe5 Nxe5 9.Nxe5 dxe5 10.Qxd8 Kxd8 where white has an advantage but with the queens off the board, I felt I had the time to manually castle queen-side. It turns out that my opponent had never faced 7…Nc6 himself and we were both in virgin territory.

8. h3 Be7 9. a4 Nh5!

White has been ignoring his piece development so far (his only developed piece is his knight on f3), so it is essential to target the dark-squared weaknesses on his king-side.

10. Be3 exf4 11. Bf2 Be6 12.Bd3 Qa5 13. O-O O-O 14. Nd5 Bxd5 15. exd5 Nb8 (Nb4) 16. c4 Bf6 17. Qe2 (Qc2) Nd7 18. Qc2 g6 19. Rab1 Rac8 20. b4 Qc7 21. Rfc1 Ng3? (Ne5!) 22. c5 Ne5 23. Nxe5 Bxe5 24. c6 Qe7? (Rf7!) 25.
b5 axb5 26. axb5 Qh4??

Until this point, we have both made minor inaccuracies but the game is still evenly poised. I have the extra pawn but my opponent has the initiative with his queen-side pawns storming up the board. At this point I debated between the text move and 26…f3, when I return the extra pawn but open up his kingside for attack. Alas, I went for the “safe” option but this would be my last chance in the game.

27. Qd1 Qe7 28. b6 f5 29. Bb5??

White is still winning after this move, but after 29.Ba6 (which is what I was expecting), it would have been time to resign.

29…Ne4 30. cxb7 Qxb7 31. Bc6 Qb8??

My last big blunder. I didn’t realise that with my rook on c8, I was trapping my own queen. 31…Qa6 was needed for any semblance of a chance. The rest of the game goes quickly.

32. Bd4 Bxd4+? (Rf7!) 33. Qxd4 Nc5 34. Qxf4 Nd3 35. Qd4 Nxc1 36. Rxc1 Rfe8 37. Rb1 (Ra1) Re5 (Re4) 38. Ra1 Re4 39. Qb2 Rxc6 40. dxc6 Re8 41. Ra7 Re1+ 42. Kf2 Re5 43. c7 1-0

h1

Agile2008

August 19, 2008

Agile2008 was a huge success. It was a very proud moment for me of course, with it being my first ever presentation at a conference. The talk was well attended with a decent amount of interest for TestRR.

With around 20 sessions running in parallel for each slot during the conference, it was kind of difficult to choose the session to attend. It was interesting that most of the sessions I went to were small ones, attended by only about 10-12 people and therefore quite interactive. Overall, they were also quite technical in their content and therefore captivated my interest. While talking about it within the team after my return, Uros made an insightful comment, “It sounds like you are more interested in the applied engineering aspects of Agile than the tools and practices”. I had never thought of it that way but on retrospect, that sounds about right.

I could provide a listing of all the talks I attended but that sounds like overkill. Instead, I will focus on my top three picks:

TDD in Concurrent Applications by Brett Schuchert & David Nunn

This was definitely one of the highlights of the conference for me. It became apparent within the first 15 minutes of this 3-hour session that none of the attendees was a novice at understanding and writing concurrent applications, so the session moved on to more advanced discussions quite quickly. While most of the concepts were general, most of the code we looked at was in Java. The two most important bits of advice I took from the session that it is important to actually understand (and read) bytecode to truly understand concurrent code and of the existence of a tool called ConTest which can be used to introduce delays between the execution of consecutive bytecode instructions, thereby increasing the probability of highlighting concurrency issues while testing. Both Brett and David were very impressed by our implementation of an optimistic concurrency control system within our code!

Pomodoro Technique by Staffan Noteberg

This was one of the rare slots when I couldn’t find anything obviously appealing, so I thought I would try something different. Turned out that going in with an open mind and no prejudices worked well for me and I was able to buy into the idea enough to warrant a trial of it - hopefully sooner rather than later. Essentially, it is a technique that helps you stay focussed on one task for short periods of time, thus enabling you to achieve measurable amounts of work every day. I could clearly see the transposition of the Agile sprint / story idea into a smaller scale, and definitely seems worth a try.

BDD using JUnit by Dan North & Elizabeth Keogh

This was my very first session of the conference and it left me feeling positive for the rest of the week. I hadn’t realized before the session that Dan was one of the founders of BDD. It was interesting to hear about the origins of BDD and to see a good pairing session which drove home most of their ideas. As an aside, I also found some useful things about JUnit4.4 and the Mockito framework. Enough that I would like to try and incorporate them into some real code soon for some first-hand experience.

h1

TestRR

July 30, 2008

As mentioned in my previous post, I have open-sourced a very generic and lightweight version of the robustness framework in Aloha. The name, very unimaginatively is TestRR, and is available on google code.

For the moment, the features and documentation available on the download are quite basic. The framework and basic functionality are there, and more will be added if there is any demand at all for it.

I hope people download it, try it and give me some feedback to improve it!

h1

Optimistic Concurrency Control

July 17, 2008

There are several important lessons that can be gleaned from Aloha and easily applied to other projects. One major lesson is the integration of robustness and performance builds into the continuous integration environment. This is of course the topic I am presenting on at Agile2008 along with Robbie and Fab (As a side-note, I am currently working on creating an extremely simple and lightweight robustness and performance framework based on Aloha’s model that can easily be integrated into any Java project with a minimum of fuss. I will be putting out what I have in open-source land, hopefully early next week. I also currently don’t have a name for this framework - I am calling it RPFramework for now, but would like a better, cooler name without the word framework in it. Any suggestions would be welcome!)

But what I want to focus on right now, as the title would suggest, is the optimistic concurrency control model implemented in Aloha. It isn’t the easiest or most intuitive mechanism to implement in any multi-threaded application. Most applications tend to go the pessimistic route with some locking mechanism, typically using semaphores or other language constructs such as the synchronized keyword. From my experiences in trying to describe how the optimistic model works to others (typically colleagues joining the Aloha team), developers, even highly experienced and competent ones, have loads of trouble really grasping the concepts behind it. Their first few dabbles at working with the implementation are inherently wrong as they struggle to cope with the intricacies behind the model.

One (un-scientific) way of gauging the effectiveness of the model is to look at other applications that use it. Among databases, Oracle is the only major player which uses such a model. All the others use a pessimistic locking mechanism - it is however hard to pinpoint this as the primary reason (or even one of them) why Oracle outperforms most other RDBMS. Java itself uses this modelin its implementation of the increment() method in AtomicInteger. And just last night, I read a very interesting article that delved into the depths of Transaction Memory (TM).

Transaction Memory attempts to make life easier for programmers who work on concurrent applications. It gives the developer two of the ACID properties that databases give you - atomicity and isolation. Therefore, you get to write code, never worrying about concurrency and synchronicity. TM allows you to work as if you were writing a single-threaded application, not worrying about locks et al. Now, how does TM work under the covers? There are two main flavours - STM and HTM (software and hardware versions). The hardware versions, as always, are better performing but much harder to implement and the more complex functionality is currently implemented in software. So what does TM have to do with the subject of this post? - Apparently, most TM systems are implemented using an optimistic model, using nearly all of the same mechanisms implemented in Aloha. There are two notable differences: its transparency to the developer and the retry mechanism. Let me first describe how the optimistic model itself works and then I will go into a bit more detail about these differences.

Instead of going into the textbook details of an optimistic concurrency model, let me describe the optimistic model as implemented in Aloha and why we chose this path. Since there is no locking in an optimistic model, any thread looking to read shared state can do so instantaneously. Writing is more complicated and can lead to delays and retries - but if the majority of data access is for reading (which is true in most applications), you really have reason to be optimistic. When you read shared state, what you really get is a cloned copy of the state. Any mutations on this state is local; it affects no one else. When you try to write the changes back into the state store, the following happens:

  • If no other thread has saved a newer version into the collection since the one you read, your changes are saved and no further action is required.
  • If some other thread(s) has saved newer changes, all your changes are rolled back and you get to try your entire “transaction” again.
  • If a ConcurrentUpdateBlock has rolled back 10 times, it isn’t retried again as it is extremely unlikely it will ever succeed.

The two main differences between optimistic concurrency control as implemented in Aloha and in a TM are:

  • In TM, the rollback and re-try mechanisms are done transparently. In Aloha, this is done explicitly by writing code which accesses and changes shared state in ConcurrentUpdateBlocks. Each ConcurrentUpdateBlock is then invoked through the ConcurrentUpdateManager which handles the retry mechanism for you. Therefore, the developer has to be very aware of shared state accesses and has to understand how the model works while writing code.
  • The other big difference is the retry mechanism. Aloha retries each ConcurrentUpdateBlock 10 times. The TM system that was described in the article I read implemented a more complex system using priority levels which increased with each failed commit. This is obviously better to guarantee fairness but we didn’t see the need to implement it until an issue became apparent.

And finally, Aloha’s state and concurrency models are easily extensible such that applications built on top of Aloha can use them with next to no effort.

h1

3 more months to Anand-Kramnik

July 8, 2008

I am still counting down the days to the big match coming up in October. Somewhat to my surprise, both players are choosing to stay reasonably active leading up to the match. I had assumed that they would both shut down tournament play post-Linares and start up their preparations. Instead, Kramnik’s just finished playing Dortmund and has apparently agreed to defend his title at the Tal Memorial in September. Vishy is playing the Mainz Classic (which he always seems to win) but more importantly, is playing the Grand Slam Final in September.

I guess the strategy is 3-fold:

  • Shake off the rust and get some real games under your belt.
  • Don’t let any opening secrets out of your bag. (I guess a side-effect of this will be that they won’t play any spectacular novelties and will probably not contend in these tournaments)
  • Play openings you wouldn’t normally play, just to make your opponent waste time conducting his due diligence on it. For example, Kramnik unleashed the Grunfeld at Dortmund. By the same token, I fully expect Anand to open with d4 and/or Nf3 at the Grand Slam Final - in fact, he might very well have to do that sooner or later in the match.

In addition, Kramnik’s agreed to lead the Russian team at the Chess Olympiad two weeks after the end of the match. Vishy hasn’t yet made a similar commitment to the Indian team and I don’t think he will either - the last Olympiad, where he rushed at the end of the M-Tel to join the team, was an absolute disaster for him.

And finally, I wonder what Kramnik’s ranking will be when the match starts. The next ratings are due to be published on October 1st and Ivanchuk, Carlsen, Morozevich and Topalov are all ahead of him in the live ratings.

h1

Cedric’s Coding Challenge

July 3, 2008

I thought I would take an attempt at the coding challenge presented by Cedric. Most of the solutions presented in the comments to the post seemed to involve brute force using regex manipulations, with people complaining that the implementations were quite slow. I thought I would try a different implementation, a highly customised one for the specific problem stated, but one which runs in 734ms on my not-so-fast development machine.

One interesting sidenote to add. I was never good at combinatorics and I am probably quite wrong at this, but I figure that the number of combinations I have to worry about as the number of digits increase probably increases quite alarmingly. When dealing with 2 digit numbers, I had only C(2,2) = 1 combination to worry about. With 3 digit numbers, C(3,2) = 3 and with 4 digit numbers, C(4,2) = 6. So the next increase would give me C(5,2) = 10 different variations to worry about. This isn’t critical for this particular challenge, but for a real-world problem, I would have to generalise the solution!

public class CedricCodingChallenge {
  public static void main(String[] args) {
    CedricCodingChallenge codingChallenge =
      new CedricCodingChallenge();

    long startTime = System.currentTimeMillis();
    List<String> list = codingChallenge.populateList(1, 10000);
    long endTime = System.currentTimeMillis();

    System.out.println(Arrays.toString(list.toArray()));
    System.out.println(String.format(
      "Size of list: %d", list.size()));
    System.out.println(String.format("Biggest jump: %d",
      codingChallenge.getBiggestJump(list, 10000)));
    System.out.println(String.format(
      "Total time: %d", endTime - startTime));
  }

  private List<String> populateList(int start, int end) {
    List<String> whiteList = new ArrayList<String>();
    for (int i=start; i<end; i++)
      whiteList.add(String.format("%d", i));

    for (int i=0; i<=9; i++)
      removeDuplicatesFromWhiteList(whiteList, i);

    return whiteList;
  }

  private void removeDuplicatesFromWhiteList(
    List<String> list, int digitToRemove) {

    // Remove 2 digit numbers
    if (digitToRemove > 0) {
      String numberToRemove =
        String.format("%s%s", digitToRemove, digitToRemove);
      list.remove(numberToRemove);
    }

    for (int i=0; i<=9; i++) {
      if (i > 0) {
        // Remove 3 digit numbers where digit is the last 2
        String numberToRemove = String.format(
          "%s%s%s", i, digitToRemove, digitToRemove);
        list.remove(numberToRemove);
      }

      if (digitToRemove > 0) {
        // Remove 3 digit numbers where digit is the first 2
        String numberToRemove = String.format(
          "%s%s%s", digitToRemove, digitToRemove, i);
        list.remove(numberToRemove);

        // Remove 3 digit numbers where digit is the first and the last
        numberToRemove = String.format(
          "%s%s%s", digitToRemove, i, digitToRemove);
        list.remove(numberToRemove);
      }
    }

    for (int i=0; i<=9; i++)
      for (int j=0; j<=9; j++) {
        if (i > 0) {
          // Remove 4 digit numbers where digit is the last 2
          String numberToRemove = String.format(
            "%s%s%s%s", i, j, digitToRemove, digitToRemove);
          list.remove(numberToRemove);

          // Remove 4 digit numbers where digit is the middle 2
          numberToRemove = String.format(
            "%s%s%s%s", i, digitToRemove, digitToRemove, j);
          list.remove(numberToRemove);

          // Remove 4 digit numbers where digit is 2nd and 4th
          numberToRemove = String.format(
            "%s%s%s%s", i, digitToRemove, j, digitToRemove);
          list.remove(numberToRemove);
        }

        if (digitToRemove > 0) {
          // Remove 4 digit numbers where digit is the first 2
          String numberToRemove = String.format(
            "%s%s%s%s", digitToRemove, digitToRemove, i, j);
          list.remove(numberToRemove);

          // Remove 4 digit numbers where digit is 1st and 3rd
          numberToRemove = String.format(
            "%s%s%s%s", digitToRemove, i, digitToRemove, j);
          list.remove(numberToRemove);

          // Remove 4 digit numbers where digit is 1st and 4th
          numberToRemove = String.format(
            "%s%s%s%s", digitToRemove, i, j, digitToRemove);
          list.remove(numberToRemove);
        }
      }
  }

  private int getBiggestJump(List<String> list, int max) {
    int biggestJump = 1;
    int previous = 0, next = 0;
    ListIterator<String> iterator = list.listIterator();
    while (iterator.hasNext() || previous < max) {
      if (iterator.hasNext())
        next = Integer.parseInt(iterator.next());
      else
        next = max;
      int difference = next - previous;
      biggestJump =
        biggestJump >= difference? biggestJump : difference;
      previous = next;
    }
    return biggestJump;
  }
}
h1

500 and counting

June 30, 2008

It’s taken me nearly two years but I have finally notched up my 500th win playing pseudo-correspondence chess. My target at this site has been to win at least half of the games I play. At this point, I am some way off - currently my W/L/D record here stands at 500/377/137.
Hopefully I can get my next 500 a lot quicker!

h1

Time is of the essence

May 29, 2008

After last week’s game, it is now seven straight games since I’ve lost. White, I think, was slightly better through most of the game. Near the end of the game, even when I went up a pawn, he had plenty of chances to force a draw. Instead he failed to find the right moves in time trouble. I normally have a very strong instinct to play quickly in my opponent’s time pressure, not giving him a chance to think on my time. This time, I took efforts not to make that mistake - instead, I tried to choose continuations which would force him to calculate multiple variations at critical junctions. End result? I won on time. The game, as always, can be replayed here.

1. d4 d5 2. c4 c6 3. e3 Nf6 4. Nc3 e6 5.Nf3 Nbd7 6. Bd3 dxc4 7. Bxc4 b5 8. Bd3 Bd6 9. Qc2 Bb7

I have now acquired a copy of Fritz 11 and this position has occurred twice in its database. After 10.O-O O-O (which was played in the above games), it transposes into more common variations.

10. Ne4 Bc7 [10...Nxe4 11.Bxe4 Rc8] 11. Bd2 O-O 12. O-O Rc8 13. Nc5

I was fully expecting Bb4 at this point. I didn’t like Nc5 as after I capture the white knight, both recaptures help Black’s position. If Qxc5, Bb6 and I can start thinking of moves like c5, when I challenge white’s control of the center and my pieces start coming to life. If dxc5, as in the game, white loses his pressure on the c-file, he has no direct threats and I can start repositioning my pieces to more active squares.

13…Nxc5 14. dxc5 e5

The computer prefers Qd5 or a5. In fact I considered both of these moves but couldn’t come up with a clear plan. With e5, my idea was this. First of all, if white made some random move, I was hoping to play Re8, followed by e4, and I am clearly better. On the other hand, if white played e4 himself, he closes the light-squared diagonal, which is where he exerts the most pressure. By the same token, my dark-squared bishop is now useless as well, but with a closed position, I would have enough time to relocate my useless c8 rook and b7 bishop to more useful squares.

15. e4 a5 16. a3 Qe7 17. b4 Ra8 18. Bg5 Bc8 19. Qc3 Re8 20. h3 h6 21. Bc1

My opponent spent a very long time before playing Bc1. I was actually quite surprised; I felt he would play Bh4.

21…Nd7 22. Bc2

Getting out of potential traps like f5, exf5, followed by e4, forking the knight and bishop.

22…Nf8 23. a4 axb4 24. Qxb4 Nd7 25.Be3 Bb6 26. axb5 Rxa1 27. Rxa1 cxb5 28. Qxb5 Bxc5 29. Bxc5 Nxc5 30. Ra5 Nd7+-

Somewhere in my calculations, I had assumed that I would be up a queenside pawn at the end of these tactics. The realization hit home only after White’s 26th move.

31.Ba4 Qa3

By this time, my opponent was in serious time pressure. Being in a worse position, I wanted to make sure I didn’t make too many moves where his response was forced.

32. Qb3 Qc1+ 33. Kh2 Qf4+ 34. g3 Qxe4 35. Qd1 Qf5

With this, we both made the time-control - my opponent with just a few seconds left. With the time-control for the game only adding another 15 minutes for the rest of the game, he wasn’t quite out of the woods yet!

36. Qd5?

As so often happens, he blunders right after making the time-control. Both Ra7 and Ra8 probably lead to forced draws.

36…Nf6 37. Qd1 Qxh3+ 38. Kg1 Rf8?

I gave a good, long think about 38…e4, giving up the exchange but probably winning. But I was unable to calculate all the variations and decided to play it safe.

39. Rxe5 Bb7 40. Qd3 Rc8 41. Re1 Ne4 42. Bd7! Rd8 43. Bxh3 Rxd3 44. Bg2 Nf6 45. Ne5 Rb3 46. Bxb7 Rxb7 0-1

The position is probably quite drawish at this point, but my opponent, having burnt most of his remaining time on his 37th move finally ran out of time and I won my second game of the year on a time forfeit!

h1

Mock me not

May 20, 2008

This is a follow-up to an earlier post of mine, where I ranted about the (ab)use of mocks in unit tests. Last week, I got into a debate with one of my colleagues on the same issue. He was of the opinion that mocks were THE way to go in unit tests. You just don’t test with real stuff at unit test level. You mock out all your dependencies and only test your code.

I agree. In principle. But like all rules, there are some exceptions. Lets take an over-simplified example. Suppose we are writing the get() method in a DAO class, in test-first fashion. The test might initially look like this:

 @Test
 public void testTest() throws Exception {
  // setup
  Statement statement = EasyMock.createMock(Statement.class);
  EasyMock.expect(
    statement.executeQuery(EasyMock.isA(String.class))).
    andReturn(EasyMock.createMock(ResultSet.class));
  EasyMock.replay(statement);
  RTest test = new RTest();
  test.setStatement(statement);
  
  // act
  Object result = test.get("id");
  
  // assert
  EasyMock.verify(statement);
  assertNotNull(result);
 }

The simplest code to pass the test:

public ResultSet get(String string) {
  try {
   return statement.executeQuery("");
  } catch (SQLException e) {
   e.printStackTrace();
   return null;
  }
}

Now, we enhance the test and add more stringent assertions:

 @Test
 public void testTest() throws Exception {
  // setup
  String expectedSql = "select * from table where id='id'";
  Statement statement = EasyMock.createMock(Statement.class);
  EasyMock.expect(
    statement.executeQuery(expectedSql)).
    andReturn(EasyMock.createMock(ResultSet.class));
  EasyMock.replay(statement);
  RTest test = new RTest();
  test.setStatement(statement);
  
  // act
  Object result = test.get("id");
  
  // assert
  EasyMock.verify(statement);
  assertNotNull(result);
 }

Now, this code fails the test:

public ResultSet get(String string) {
  try {
   return statement.executeQuery(String.format(
     "select * from table where id = '%s'", string));
  } catch (SQLException e) {
   e.printStackTrace();
   return null;
  }
}

Some of the problems with this test:

  • Since the SQL query is part of the code, your test should assert on the correctness of the query itself. By typing it twice (once in the test, once in the code), how exactly do you achieve that goal?
  • Each unit test should test a single unit of code (in this case, a method). Assert on the expected result (of course!), but the test shouldn’t define the implementation. The second run of the test should not fail.
  • An integration test or acceptance test might show any possible problems with the query. But then again, those tests may not cover all boundary cases. The unit test is the closest place to the execution of that piece of code and is the right place to carry out that real test.

How would you do that? Simple. Use an in-memory database, run the CREATE script as part of the test setup (wow, this tests your create script as well - what a bonus!), populate data required for your test, execute your test method, and assert on the data. If you are testing get() functionality, assert on the returned data. If testing save() functionality, assert on the new data in the database. Your unit tests actually test your code (and SQL) without definining how the code should be implemented.

This rationale has been used (extensively) in Aloha’s unit tests. As you might expect, we use Hypersonic to unit-test the DAO classes. Similarly, we use SipUnit to test code which sends and receives real SIP. It provides several benefits to the use of mocks to test SIP:

  • SIP is an incredibly complex protocol. There is no easy way to validate that a newly created SIP message is valid by itself, or valid in the current message flow. If JAIN-SIP sends out the message with no exceptions and it is duly received by SipUnit, we can rest assured that the message is valid.
  • Mocking out SIP responses also results in unmanagable tests. Since the required data in a JAIN-SIP response object is several levels deep, constructing the mocks is quite complicated. If any code is refactored later, the changes to the tests are very time-consuming, without providing the necessary confidence that the changes to the code are accurate. (A by-product of the test defining the implementation)
  • Because SIP is truly real-time, it is essential to test various race-conditions and concurrency issues. While our robustness and performance builds go a long way to achieving this goal, our unit tests provide our first level of sanity checking. With the current unit test setup, it is much easier to write failing unit tests when fixing a concurrency issue than it would with mocks!

This is not to say that we don’t use mocks in Aloha. Infact, they are used extensively across the codebase, just not in ALL places.

h1

What is Aloha?

May 9, 2008

Is it:

  1. Hawaiian, for hello and goodbye?
  2. The Web21C team shirt, represented here by Eastmad?
  3. The newly open-sourced SIP Application Server?

The problem with most SIP Application Servers is that they are built on the SIP Servlet specification. This usually leads to unwieldy implementations that are over-engineered and inflexible. Our goal with Aloha was to create something simple, easy to scale and can easily be embedded into our different voice services. The API we expose to applications is purposefully quite coarse, hiding the horrible workings of SIP and media negotiation from them. (Of course, none of this is really “hidden” - you can always get your hands dirty now that this is open-sourced!)

Dependency Injection has become a very common pattern in writing testable code. Spring Beans lend themselves to this pattern naturally, whereby you can inject properties into beans via constructors and/or setters. We therefore decided to use singleton Spring beans for most of our core classes. As long as you are thread-safe while storing and accessing data, the beans are just an implementation of a finite state machine. You have the added bonus of being able to run the application server within any Java container, as long as you can load your Spring application context!

The beans themselves are layered at different layers of abstraction. The “dialog” layer is at the lowest level of the stack, responsible for sending and receiving SIP messages. Beneath the covers, it uses the open-source JAIN-SIP stack to handle a lot of the internal workings of SIP (such as the resending of messages, handling of retransmissions, working out of timeouts etc). The “callLeg” layer is a thin layer above dialog, meant to abstract most of the SIPpy stuff from the higher levels. Unfortunately, there is still some level of leakiness present at this layer as evidenced by the following declaration in the CallLegBean interface:

void reinviteCallLeg(String callLegId, MediaDescription offerMediaDescription, AutoTerminateAction autoTerminate, String applicationData);

It is a clear violation of the intended abstraction to expose details of media negotiation above the callLeg layer. It has been a story on our backlog for quite a while now to fix this but has consistently moved down the prioritization list!

Moving on, the next layer of abstraction is the “call” layer. This is where the interesting stuff starts (in terms of telephony, it isn’t very interesting if you are on a call by yourself. The most basic call would be to connect two people on a single call). Most applications using Aloha will use the API exposed by CallBean. In addition to calls, there are “media” level beans (both for call legs and for calls), which allow the application to create calls using the media server. The media beans themselves are defined in the SimpleSipStack project; ConvediaMediaBeans is an implementation using Convedia’s media server (The media server talks MSML and MOML over SIP). ConferenceBean leverages the media server and allows applications to create conference calls, while MediaBeanImpl can be used for the playback of announcements, collecting dtmf, recording etc.

All of the actions exposed via the API complete asynchronously. On completion/failure, applications are notified of events using the listener pattern. Listeners can either be added via the application context or directly from code. Listener interfaces are defined at each layer and applications can choose the events they are interested in. Infact, beans defined at each layer in Aloha register themselves as listeners to the corresponding beans in the layer beneath them.

Having provided the basic structure, I will next tackle the testing strategy used in Aloha.