Recap
In last issue's piece, I described what Broadvision was and gave a flavour for how it worked, looking in particular at extending some of its classes to work around design blind spots. I said that this time I'd look at database access and encapsulating the Broadvision API as well as more information on session management. The pressures of work have meant that I haven't had much time to prepare this piece as I'd like - ironically, I'm working on another Broadvision-powered web site and it's keeping me very busy!
Sessions
I touched briefly on the concept of web site sessions in the first article in this series and in order to explain what follows, I shall elaborate a little on session management. The web is essentially stateless: you visit a site, the browser requests the page, the server fetches (or generates) it and sends it to your browser. At that point, you can request another page from that site or another and the browser maintains only a history of which pages you've requested. The server on the other hand, spends its time fetching or generating pages in response to requests from any number of users. For a personalised web experience, someone has to maintain information about your choices within a web site, for example a 'shopping basket' within an e-commerce (electronic commerce) web site.
Broadvision chooses to maintain this session 'state' information on the server. When you connect to a Broadvision site, it allocates a unique session 'ID' and uses it in every generated link and form so that with each incoming request, it can work out which of its currently active users is making the request. Naturally, an application built using Broadvision will need to maintain its own state information: an example from our travel site is the set of destination / price range / date range choices that you make as you wander around the site.
Broadvision provides a very simple interface for application data: an associative array of strings called the application data dictionary. In C++ terms, this is effectively map<string,string> but wrapped up in a session ID based lookup mechanism (i.e., map< SessionID, map<string,string> >). The base Broadvision class, Dyn_Object, provides 'store_app_data' and 'find_app_data' methods to access the application dictionary (thus hiding the session ID lookup - the object already knows about session IDs).
The first part of the application we tackled was the foreign currency section of the site. We needed to store the user's choices of currency and amounts for the conversion process. We tried to fit in with Broadvision's application dictionary by mapping our array of currencies, rates and amounts down to strings and then converting them back when we needed them. It worked but it was painfully ugly and very inefficient. I looked in vain for a more generic way to deal with state data - Broadvision had none.
After a bit of thought about the way Broadvision worked, I realised that I could solve the problem with static member data - the Broadvision CGI application ran continuously in the background so static data would have a suitable lifetime. So I wrote a small template class called 'SessionStore<T>' to store data of type 'T'. Since I could now store arbitrarily complex data structures on a per session basis, I decided to allow only a single object of each type to be stored for any one session and to have it accessed through a writable reference, i.e., the one and only copy of each state object for a session lived in its own 'SessionStore' container. We didn't bother rewriting the currency handler - after all, it worked and we were somewhat pressed for time - but we used the new 'SessionStore' for our search context objects and our customised shopping basket. It's a trivial template class and it seems an obvious omission from the Broadvision framework. Another design blind spot.
See API?
I've been very critical about many aspects of the Broadvision architecture, taking up design issues with everyone from technical support up through the development team in the USA. One of my main gripes about the architecture was that most of the API smelled like C. You know what I mean, you've seen this in other frameworks: a huge slab of unrelated API calls disguised as methods in an umbrella class. Broadvision suffers from this in spades: pretty much the entire session management API lives in one class. In fact, it goes further than just session management, it also includes database access functions and many other tasks.
Naturally, I don't like this. If I'd wanted that architecture, I'd be programming in C. I like C++ because it can provide a better match to the problem domain by encapsulation. Part of our application generates emails from the web site to back office staff containing certain details of the user's profile (name, address etc). As part of our OO design, we wanted to pass a UserProfile object into the EmailRequest object so that it could interrogate the profile to fill out fields in the email message. Broadvision doesn't have a UserProfile object, instead it provides the equivalent of get_user_profile_field / set_user_profile_field as API calls. UserProfile became the first of many classes that we created to wrap up parts of the API so we could pass suitable objects around within our application. Like many such wrapping classes it is rather crude and was tedious to write:
class UserProfile { public: UserProfile() { } RWCString name() const { return get_user_profile_field("NAME"); } UserProfile& name(RWCString n) { set_user_profile_field("NAME",n); return *this; } // about a dozen similar methods };
Now, I don't think of myself as an OO purist, nor do I think I'm an unreasonable man, but given a framework for an application where concepts such as 'visitor', 'profile', 'shopping basket' are absolutely key, doesn't it seem somewhat disappointing that equivalent classes are not provided within the framework?
An object lesson
I'm going off on one of my tangents now, as I'm wont to do. I mentioned in the 'Recap' that the reason I haven't had as much time to work on this article as I'd have liked, is that I'm currently building another Broadvision web site. In fact, I'm not building it, I'm just designing it: through the vagaries of office politics and a natural evolution of my position at my client's, I no longer do any development per se, I'm a designer. I recently evaluated 'Together/J' from Peter Coad's Object International company ( http://www.oi.com ).
'Together' comes in several flavours dealing with C++ and Java (and other languages). I chose the Java version because I wanted to avoid the temptation of generating C++ and then slipping back into development (our target development language is C++). I downloaded the Whiteboard edition that provides object diagram functionality and the ability to browse 'use case' diagrams produced by the full edition. The object diagram editor has four panes: a navigation pane showing either a thumbnail of the entire diagram with a scrollable shadow or a directory hierarchy showing packages and their contents; an attribute pane allowing direct editing of object attributes; a diagram pane showing standard UML notation for classes and relationships; and a code pane showing Java class & method code for the corresponding elements of the class diagram. The latter is very slick: change the diagram or attributes and the code updates immediately, change the code and the diagram and attributes change to match. The whole product has a very responsive feel and is very flexible. I was sufficiently impressed to purchase the full version of the product which adds the ability to create and edit use case diagrams as well as state and sequence diagrams.
Using such a UML based CASE tool on a Broadvision project helps you separate the meat of the design from the continual workarounds that arise from struggling with the application framework. One of the first tasks was to create diagrammatic (and, hence, Java) representations of the main Broadvision classes on which to build the application-specific classes. It's been a much more pleasant way to work, being able to concentrate on the design and get that right. I'd like to think that the rigours of working within a strict OO design framework have made me a better designer overall and I'm off on a QA Training course for OOA/D using UML shortly so I'll probably write up specific pieces both on the course and the 'Together' tool in due course.
What's next?
We've recently completed the data model for the current project and we've taken on board much of what we've learnt about Broadvision over the past year. We're approaching the current project from a rather different angle to the one I've been describing here. In the next article, I'll look at database access, as promised, but with a 'compare & contrast' showing how we fought with the framework last time and how we're attempting to work within it this time, hopefully illustrating how suitable compromises make frameworks more effective. Since we're trying to stick more closely within Broadvision's framework this time around, I'll also look at other aspects of Broadvision. In the fourth and final article in the series, I'll go back to more of the C++ customisation we've done - on both projects - and again, make comments about blind spots within the framework.