Monday, March 30, 2009

Java Persistance with Hibernate - what I had to re-visit after reading the book

I recently had a JPA related issue and decided that it was high time to purchase a good book on the subject; particularly as JPA/Hibernate tend to play a very important role of course.


I ended up discovering "Java Persistance with Hibernate" (Bauer, King). This book is a revision of "Hibernate in Action" and it certainly reads as a complete reference to all things ORM. What attracted me to this book in particular was its coverage of JPA. I prefer to use JPA over Hibernate interfaces so that my ORM implementation choice is left open as much as possible. Having said that Hibernate gives me no reason to move to another ORM.


I must have been using Hibernate/JPA for the past 18 months. When I started I recall wanting to skim over the ORM subject as much possible in order to get things done i.e. I did not think deeply about the implications of some of my decisions; or rather I misinterpreted the way things worked e.g. the 2nd level cache. To this end here is a list of things that I found myself re-visiting:


* property annotations instead of field annotations;

* using the Collection interface instead of the Set interface;

* cache annotations;

* bi-directional one-to-many relationships;

* immutable entities


Using property access can boost performance when performing operations on a collection (also as opposed to a set). For example if you want to add to the collection Hibernate does not have to load the existing collection from the database. If you use a Set then it does in order to guarantee Set semantics.


Understanding the second level cache is very important and if you don't have time to learn it then disable it. It is also a good idea to understand whether you have immutable entities so that you can use the Hibernate @Entity(mutable=false) annotation and flag to the cache that the object is read only. This permits Hibernate a few optimisations including a minimisation on the number of update statements that require preparing at startup.


I always wondered why my bi-directional relationships were generating a join table. It finally sunk in that the mappedBy attribute on an association describes the foreign key. No more join tables for bi-directional relationships.


There's no excuse; just buy the book if you're doing any ORM. Having said that even before I made these changes Hibernate was performing exceptionally well!

Tuesday, March 17, 2009

Mutual SSL authentication and LDAP authorisation for ActiveMQ

I now have quite a lot of software infrastructure supporting the business of tracking things that move; particularly aircraft flights. The back-bone of all of this is ActiveMQ. ActiveMQ is an incredible messaging work-horse and easily handles 300 messages per second being received from a number of radar sensors.

I have also re-developed my Titan Class Vision client application using JavaFX (the subject of another blog entry). The goal of this re-write was to be able to deploy Vision on a wider variety of platforms than I could do so previously. Before the JavaFX version Vision was a turn-key hardware/software combination written in Objective-C/C++ and ran on Mac OS X only.

With the advent of the new version I am able to deploy Vision across the internet. Given this there are suddenly many more potential candidates in terms of users and I had a security question to consider: how can I demonstrate to whoever is concerned that I have made every effort to ensure that this sensitive real-time flight data is not being mis-used by anyone.

Authentication
My requirement therefore became one centered around SSL. ActiveMQ permits connections to be established using SSL. Server only authentication is fairly straightforward and covered here. What I was after though was client certificate verification; otherwise known as mutual SSL authentication. In a nutshell, the server verifies the client's certificate as one it trusts, and the client verifies the server's certificate as one it trusts.

When it comes to the client application, in my case the JavaFX application, you need to make sure that the client's keystore is accessible. Don't do as I did and try using the JRE's default keystore for this purpose. I just couldn't get that to work. Instead do as the ActiveMQ SSL page suggests and provide the client with its own keystore.

Another tip for the client, is to set the javax.net.ssl.* properties within the application itself; before you try establishing the JMS connection of course. I express the location of the client's keystore in relation to the user.home system property.

From a broker (server) perspective, one has to use a JAAS LoginModule that permits certificate based authentication. Fortunately I found an ActiveMQ-JAAS class named CertificateLoginModule (of all things). One very subtle thing to note: when specifying the use of this login module in activemq.xml you must use the jaasCertificateAuthenticationPlugin element instead of the jaasAuthenticationElement. I think that this is because the certificate login module requires a different login callback to obtain the client's certificate.

CertificateLoginModule is only half the picture in the same way that authentication is only half the picture. Authorisation is required and the CertificateLoginModule has to be extended to support this; the login module does not know how to authorise a certificate. I can help there as I have provided this code. More on that later though (I insist on you reading the rest of this entry!).

Finally on authentication, you need to tell ActiveMQ that you want to perform client certificate authentication; it will not do it unless told to do so. You do this by specifying "needClientAuth=true" parameter on the ssl transport in activemq.xml.

You might want to disable the other connectors and open up your firewall just for 61617 SSL connectivity. I think that once you make authentication a priority with the broker then you need to give up the non-secure connectors.

Authorisation
With authentication done (I know who you are, you know who I am), I needed to deal with authorisation (now I know who you are, what am I going to allow you to do). For this I wanted to centralise my user and group/role information. ActiveMQ allows you to specify user/group associations in its configuration file, but I wanted to do what all grown-ups do: specify my users and groups in a centralised LDAP directory.

The CertificateLoginModule requires extension to specify how authorisation is done. I have created a CertificateLoginDirectoryRolesModule that will take the subject DN from each client certificate presented (there can be many but typically just one), and then call upon my LDAP store to determine which groups the DN is a member of.

I have set up my LDAP server (ApacheDS - fabulous) to allow anonymous access but also enabled access controls. This means that, by default, the LDAP server permits very little authorisation with just admin access. I then created a group named "activemq" off the "groups" node and used an ACI to allow anonymous searching of that group. I ended up with a group hierarchy as follows:


ou=system
ou=groups
ou=activemq (anonymous users can see this and below)
cn=jms-services
cn=activemq-users
cn=com.classactionpl.javaFlightTopic.Subscribers


Correspondingly here is my authorisation mapping within activemq.xml:


<authorizationPlugin>
<map>
<authorizationMap>
<authorizationEntries>
<authorizationEntry queue=">"
read="jms-services"
write="jms-services"
admin="jms-services" />

<authorizationEntry topic=">"
read="jms-services"
write="jms-services"
admin="jms-services" />

<authorizationEntry topic="com.classactionpl.javaFlightTopic"
read="com.classactionpl.javaFlightTopic.Subscribers" />

<authorizationEntry topic="ActiveMQ.Advisory.>"
read="activemq-users"
write="activemq-users"
admin="activemq-users" />
</authorizationEntries>
</authorizationMap>
</map>
</authorizationPlugin>


What the above states is that jms service providers, such as my Camel based applications, can effectively publish and subscribe to anything. However my client belongs to the javaFlightTopic.Subscribers group and the activemq-users group and so can only consume from a specific topic and perform all required advisory services; the latter being an ActiveMQ requirement.

It is possible to express the authorisation mappings in an LDAP store as well. We'll see if the need surfaces.

Source Code
I have created an open-source project named jaasloginmodules that hosts this JAAS login module and have tested and used the classes. The CertificateLoginDirectoryRolesModule is ready for download and use.

Tuesday, March 3, 2009

When is a POJO no longer a POJO

When is a Plain Old Java Object no longer a Plain Old Java Object?

My take on POJOs is that they are no longer POJOs when there is an import statement for something not conforming to the expression "java*.*".

I see quite a lot of frameworks and toolkits claiming benefits around POJO oriented development. However either through annotations, extension, implementation etc. they require dependencies on non JRE artifacts.

This situation is quite OK of course; I just think that it is useful to distinguish a POJO from its dependent form. For example if you're looking for Object Relational Mapping then you might want to consider the Java Persistence API instead of being directly dependent on an implementation; such as Hibernate. Maintaining this distinction allows you to consider migration strategies to other implementations if required.

Sometimes you need to be dependent on a toolkit or framework and that is of course perfectly acceptable. Being aware is the significant point.