Monday, 20 February 2012

Madura Bundles and data versioning

When building applications using Madura Rules we have some choices when defining the data for the decision tables. This is the data that tells the rules what combinations of settings work together. In the pizza demo this tells us, for example, what toppings and bases go with what pizza sizes. The data is not usually very large and is only ever read, not written.

In the pizza demo this data is held in an XML file stored as a resource on the classpath.

 There are two interesting issues to solve here.
  • Data stored in XML is all very well, but it is not so easy to maintain. This is especially true if you have multiple operators working on it at once.
  • There is no ability to version the data. Everyone using the application sees the same combinations and options, always the latest one. What if they want to see how things were last week? For example they might have saved a shopping cart from last week and want to order this week and the data changed meanwhile.
 As long as we keep things in XML we can solve the second problem quite easily with Madura Bundle. The XML file in the pizza demo is just a resource and distributing resources is supported by Madura Bundles. The idea here is that the application selects one of the available bundles (and the XML file is in the bundle). Extending the shopping cart example, when last week's shopping cart is picked it would hold the id of the bundle used to create it, and the application would pick that bundle before restoring the shopping cart. Everything would then work as normal.

You can add new bundles any time and new shopping carts would always pick the most recent bundle. Sometimes there would be a reason for an old shopping cart to run under the latest bundle, perhaps if we decided the old bundle was no longer valid for the business. Maybe it holds prices we don't want to support now.

The bundle can also hold rules and code if you want. You could put as much of the application you want into the bundle. Just where the balance should be is up to you.

Now, about that XML. Could we use a database? Actually yes. HSQL has a memory-only database option, including one that we can load from data held on the classpath. Naturally it is read-only and naturally you don't want to hold gigabytes of data in it. But you can use ordinary JDBC to access it, and that means you can use more interesting things like JPA on top of that.

To use this with Madura Rules you'd need to write some Java, namely you'd need to extend the AbstractDecisionTable to support the actual database structure you actually have. This is the simple bit. You define the data source like this:

<bean id="dataSource"
   <property name="driverClassName" value="org.hsqldb.jdbcDriver" />   <property name="url" value="jdbc:hsqldb:res:/testdb" />
   <property name="username" value="sa" />
   <property name="password" value="" />

This assumes you have a file called testdb.script in the top of your classpath. You can optionally add HSQL creates these files if you create a database using a URL jdbc:hsqldb:file:testdb (testdb can have a directory in front of it, this example uses the cwd).

H2 has a similar option, but I haven't used that yet so I'm not clear on the details.

Can we put the database into a Madura Bundle? There wouldn't be much point to this post if we couldn't, I guess. So here's how.

All you need to do is define the data source inside the bundle. I actually defined the data source, JPA entity manager and a DAO class as beans in the bundle. Note that the classes for these are on the classpath of the application. You don't have to bundle those classes (this is one of the differences between Madura Bundles and OSGi). The DAO class is exposed to my main application.

Because HSQL remembers the database name and assumes a request to open a database with the same name is the same database, you do need to ensure each bundle calls the database by a different name. This is simple enough

<bean id="dataSource"
    <property name="driverClassName" value="org.hsqldb.jdbcDriver" />    <property name="url" value="jdbc:hsqldb:res:${}-testdb"/>    <property name="username" value="sa" />
    <property name="password" value="" />

Using the symbol like this means the file this data source looks for is (in my case) bundle-1.0-testdb, bundle-2.0-testdb and so on. I also have to rename my testdb.script file to bundle-1.0-testdb.script etc.

Now this all works just fine in a simple test case except that HSQL adds a '/' to the front of the file name and Madura Bundles does not like this. I've added a small patch to Madura Bundles to overcome this, but it isn't quite enough to justify a release just yet. It will be in version 3.5.

Saturday, 18 February 2012

Madura Perspectives

Imagine you have a bunch of web applications you want to make available to your users. You want them all to share the same login and permissions. Actually you'd quite like to have a common look and feel across all of them too.

These applications need to be up 24x7 and you need to be able to update them without bouncing your server. Preferably any time of the day or night.

Your users often have several of these applications active at a time in multiple browser windows and they copy/paste stuff from one to the other. That gets a bit ugly. You would rather when stuff happens in one application the others already know about it. But you don't want to bind all these applications together, maybe they have different development teams and coordination is too hard.

Let's assume you don't have an enterprise authentication system to handle your common login yet, so you need to pop your own login page (but changing to the enterprise authentication system you implement later has to be a minor reconfiguration).

Welcome to Madura Perspectives Manager.

Madura Perspectives Manager is a Vaadin web application which acts as a frame to hold sub applications. The frame has a login page which is skipped if the user is already logged in. Permissions are derived based on the users' roles using Spring Security and these permissions are propagated to the sub applications.

This is what the frame looks like:

This layout is just an example, you can customise this endlessly if you want, either by changing the CSS settings or by changing the code. The Perspective Manager is just a template or example rather than a fixed product. This version shows:
  • Who is logged in.
  • A title (top right). This comes from a properties file.
  • A logo. This is defined in the CSS file which refers to a logo image. It is fetched using the Vaadin theme mechanism.
  • A list of sub applications the frame found when this session started.
  • A simple menu (File, Help with an entry under each, in this case Logout and About).
The menu is the basic menu our framework starts out with. This will get added to when we pick a sub application.
So let's pick a sub application by clicking on the User link on the left.

Now the UI for User displays. That User sub application is not initialised until we first pick it, because you might have a lot of applications and this user in this session might not ever pick them all, so we lazily initialise.

The User sub application is a simple Vaadin Form mapped to an object. It is just a demo so we don't do much more. But it can get as complicated as you like.

Let's try the pizza sub application.

If you have looked at our pizza order demo you may recognise this, it is the pizza configuration screen from that demo. Same rules, same objects. So this is a Vaadin application which uses Madura Objects and Madura Rules. It all works the same way, ie if you pick a Size the topping options etc constrain. Required fields are enforced by greying the 'Save' button.... oh, wait there is no Save button.

No, but there is an extra option on the menu: Edit. If we open that menu item we will see the Save menu item under it and this is greyed when we have a required field not filled in. These extra menu items appear when we pick this application. We can flick back to the User application and the menu will dynamically redraw leaving out the now inapplicable menu items. If we flick back to the pizza application the menu items will be restored.

There are two other things on this screen that need mentioning.

First the Madura logo appears at the bottom. This is in the space controlled by the sub application. In this case the sub application has done what the frame did, it used a Vaadin theme resource to display an image. What it shows is that the sub application is using the same theme as the frame. You can change the theme for the frame and all the sub applications will change automatically. So you naturally get the same look and feel across the whole collection of applications just by using Vaadin's themes consistently.

The second thing is the field near the top: User Name. You'll see that the alignment is a little different. This is because it is not actually part of the pizza form, just a field on the layout. We put it there because it is loosely coupled with the Name field in the User application. if we flick back to User and enter a value in Name, then flick back to pizza we would see that the User Name field has the value we just entered. There is a blackboard system that allows the sub applications to publish and subscribe to information items, thus avoiding that copy/paste annoyance mentioned earlier.

Because these sub applications are Madura Bundles you can add new ones and update old ones (and even delete them) on the fly. Users who have logged in will see the same sub application set until they log out. New users logging in will see the latest sub application set.

Finally a note about permissions. If you're familiar with Madura Objects you will know that fields can have permissions assigned to them. This, with the permissions assigned to the current user, influences how the fields are rendered (invisible, read-only, read-write). Because the pizza sub application uses Madura Objects the permissions the frame derived from the login are available to the sub application. In fact you can also assign a permission to the whole sub application to allow some users to access it, and refuse access to others.

There is an on-line demo of this on rhcloud, although to keep it available to everyone at the same time it doesn't let you change the bundles around. But you can run it locally too. You just grab the project from GitHub. The bundles are in separate Github projects but that is covered in the documentation.

Why did we call it 'perspectives'? Because the way the applications switch is a bit like what you see in Eclipse when it switches perspectives.