Thursday, October 26, 2006

Oracle Linux Download

Yesterday, Oracle announced that they will be supporting Red Hat Linux, as well as releasing their own clone of the OS, just like CentOS and others have done. There had been speculation for weeks that Oracle was going to get into the Linux business, either by creating their own distribution, or purchasing a Linux company. While there have been some negative reactions to this announcement, I view it as a great opportunity, and a means of forcing more innovation into the market. The download itself is available here. You simply need to fill out a form to gain access to the files.

Those of you who have read previous entries in my blog may realize that I'm a fan of the Red Hat clone, CentOS. I've used it on laptops, desktops, and servers, and have had very few problems. The stability it provides is far better than I've experienced with Fedora, SuSE, or other distributions. However, if I wanted to install it in a corporate environment, my support options would be limited. Red Hat charges a great deal for support, even rivaling Microsoft in terms of pricing. With Oracle now offering support, the cost of deployment is greatly reduced. Some have argued that this is a low-blow by Oracle, as they are simply repackaging Red Hat's work and distributing it as their own. That is exactly what they're doing. However, that is the risk with open source software. Just because Red Hat is "loved" by the open source community doesn't mean it should be treated any differently.

For the past several years, open source advocates have argued that it is a superior business model. The thought that releasing a product as free, open-source software and charging support will give you a lock on a given market is laughable, at best. A company like Red Hat cannot compete with Oracle in terms of size and resources. As a result, they are vulnerable to having their prices undercut by a company that is able to deliver better support for less money. MySQL, Alfresco, and all other open-source companies are vulnerable to this as well. The key is not just providing support, but providing some additional value that your competitors are incapable of providing. And, currently, Red Hat is not taking these measures.

Red Hat provides no value-added resources on top of their existing OS distribution. For those individuals who don't need support, there is no incentive to purchase a commercial distribution of the product. They can simply download CentOS or one of the other clones that are available. Now that Oracle is offering cheaper support, there is even less of an incentive for a company to purchase support from Red Hat. If, however, Red Hat began to provide offerings that only they were capable of, they would regain their advantage in this area. Oracle has learned this lesson the hard way. After growing tired of being "just a database company", they began developing and acquiring applications that were designed to run on top of their database as a complete stack. By providing an all-in-one solution, companies were able to get all of their products in one place, from one vendor, with one support contract. Red Hat does have some closed-source applications in their product library, such as their Certificate System which was purchased from Netscape some time ago. However, this is such a niche product that it is not important to the majority of Red Hat's customers. Providing other applications, however, may provide them with an advantage. For example, an identity management system similar to Active Directory, using their LDAP directory server and Kerberos for single sign-on. Or, tightly integrated management applications for administering Apache, LDAP, MySQL/PostgreSQL, and other system services.

I am not surprised by Red Hat's sudden drop in share price following this announcement. For existing Oracle customers, there is now a very good reason to drop support contracts currently offered by Red Hat. For those not running Oracle on Linux, they now have a very good reason to think about it.

Monday, October 9, 2006

MySQL Master-Master Replication

I came across a great article detailing how to accomplish Master-Master Replication on MySQL 5. While there are other technologies that could be used to create high availability MySQL instances, such as MySQL Cluster, these often provide a less-than-ideal solution because of the additional requirements they impose (e.g. the use of in-memory databases or a different storage engine). The solution provided in the article is to use circular replication. That is, if we have 2 databases, A and B: A is a master to B, while B is a master to A. Because each node acts as both a master and a slave, changes to either database get propigated to the other. Take a look at the article to find out more.

Friday, October 6, 2006

Great Workspaces

I came across a great article depicting some of the best workspaces in companies around the world. Pixar, Google, Red Bull, etc. The pictures show just how creative these offices can be, and show that this creativity can lead to creativity in their employees. As someone who has worked in a private office, cubicle, and now a shared workspace, I can certainly understand how offices like this can make the daily 9-5 grind a much more pleasant experience.

Thursday, September 21, 2006

Complex Many-to-Many Relations in Hibernate

SQL makes querying structured data very easily, especially when the data in tables is related. Using simple joins, it's possible to query for data in two separate tables that shares a common element. For example, imagine if we have a table users_tbl that contains our user account information, and a table roles_tbl that contains our roles information. Additionally, we make use of a join table users_roles_tbl that allows us to have many-to-many relationships between our users and roles. To create our tables, we could use the following commands (on MySQL 5):


create table users_tbl (
id INT AUTO_INCREMENT NOT NULL,
username VARCHAR(32) NOT NULL,
PRIMARY KEY (id)
) TYPE=INNODB;

create table roles_tbl (
id INT AUTO_INCREMENT NOT NULL,
rolename VARCHAR(32) NOT NULL,
PRIMARY KEY (id)
) TYPE=INNODB;

create table users_roles_tbl (
user_id INT NOT NULL,
role_id INT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users_tbl (id),
FOREIGN KEY (role_id) REFERENCES roles_tbl (id),
PRIMARY KEY (user_id, role_id)
) TYPE=INNODB;


Suppose we have created 3 users: user_a, user_b, and user_c. Additionally, we have created 2 roles: role_a, and role_b. Our first user, user_a, is a member of role_a. Our second user, user_b, is a member of role_b. And, our third user, user_c, is a member of both role_a and role_b. We can see these users and roles displayed with the following SQL SELECT statement:


SELECT DISTINCT u.username, r.rolename FROM users_tbl u
INNER JOIN users_roles_tbl j ON u.id = j.user_id
INNER JOIN roles_tbl r ON r.id = j.role_id;


There are many instances in which we would like to find members of specific role, such as for access control. If we wanted to find all members of role_a, a simple modification to the previous SELECT statement will do the trick:


SELECT DISTINCT u.username, r.rolename FROM users_tbl u
INNER JOIN users_roles_tbl j ON u.id = j.user_id
INNER JOIN roles_tbl r ON r.id = j.role_id WHERE r.rolename = 'role_a';


In this example, user_a and user_c are returned, since they are the only members of role_a. But what happens when we want to perform a more complex query? For example, what if we only wanted to return users who are members of role_a AND role_b? This is where things become a bit more complicated. The IN statement allows us to perform a logical ORing of values. This is what we would use if wanted to find users who are members of role_a OR role_b. But what if we want users who are members of BOTH roles?

In this case, our SQL SELECT statement becomes a bit more complicated. Our SELECT statement would now look like this:


SELECT DISTINCT u.username FROM users_tbl u
INNER JOIN( roles_tbl r INNER JOIN users_roles_tbl j
ON ( ( r.id = j.role_id ) AND ( ( r.rolename = 'role_a' )
OR ( r.rolename = 'role_b' ) ) )) ON ( u.id = j.user_id )
GROUP BY u.id HAVING COUNT( j.user_id ) = 2;


As you can see, this SELECT statement gets very complex, very quickly. However, it accomplishes exactly what we like. Running this statement provides us with only user_c, the only user who is a member of both role_a and role_b.

Our question now becomes, how do we accomplish the same thing using Hibernate? For example, we have our core domain model objects, UserModel and RoleModel which have the appropriate Hibernate mappings configured. One way we could accomplish our goal is to create a many-to-many relation in Hibernate for our UserModel object that contains all the roles a user is a member of. We could then query on role_a, query on role_b, and then determine the intersection of those two Collections that are returned. However, this is a very expensive and time-consuming operation.

There is a much better way to accomplish our goal. We can create an additional domain model object that represents the data stored in our join table, users_roles_tbl. This object can be called UserRoleRelation. For these examples, I will be using XDoclet to generate our Hibernate mapping classes. Our UserModel class would look like the following:


/**
* @hibernate.class table="users_tbl" lazy="true"
*/
public class UserModel {

private Serializable identifier = null;
private String username = null;

/**
* @hibernate.id column="id"
* generator-class="native"
* type="java.lang.Integer"
*/
public Serializable getIdentifier() {
return identifier;
}

public void setIdentifier(Serializable identifier) {
this.identifier = identifier;
}

/**
* @hibernate.property column="username" not-null="true"
*/
public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}
}


Our RoleModel object would be configured similarly, but with the Hibernate table as roles_tbl. As for our UserRoleRelation object, it would be defined as:


/**
* @hibernate.class table="users_roles_tbl" lazy="true"
*/
public class UserRoleRelation {

private UserRoleRelationID identifier = null;
private UserModel user = null;
private RoleModel role = null;

/**
* @hibernate.id unsaved-value="any"
*/
public UserRoleRelationID getIdentifier() {
return identifier;
}

public void setIdentifier(UserRoleRelationID identifier) {
this.identifier = identifier;
}

/**
* @hibernate.many-to-one column="user_id"
* class="UserModel"
* not-null="true" cascade="all"
* insert="false"
* update="false"
*/
public UserModel getUser() {
...
}

/**
* @hibernate.many-to-one column="role_id"
* class="RoleModel"
* not-null="true" cascade="all"
* insert="false"
* update="false"
*/
public RoleModel getRole() {
...
}

...

}


You will notice the user of a class called UserRoleRelationID as our Hibernate identifier. This is due to the fact that XDoclet does not currently support composite primary keys. However, by defining our UserRoleRelationID class in the following way, we are able to get around this limitation and use our composite primary key:


public class UserRoleRelationID implements Serializable {

private Serializable userID = null;
private Serializable roleID = null;

/**
* @hibernate.property column="user_id"
* type="java.lang.Integer" not-null="true"
*/
public Serializable getUserID() {
return userID;
}


/**
* @hibernate.property column="role_id"
* type="java.lang.Integer" not-null="true"
*/
public Serializable getRoleID() {
return roleID;
}

public int hashCode() {
...
}

public boolean equals(Object o) {
...
}

...

}


Now that our Hibernate objects are defined, we need to create a Hibernate Query that accomplishes the same thing as our SELECT statement does. However, by working with Hibernate and utilizing our UserRoleRelation class, you will find the complexity of our query is greatly reduced. Our Hibernate Query can be defined as follows:


SELECT DISTINCT j.user AS p FROM UserRoleRelation j
INNER JOIN j.user AS urj
WHERE j.role.rolename IN (:roleList)
GROUP BY j.user HAVING COUNT (j.role) = :roleCount


To execute this query, our Java command would look something like this:


/*
* The query we defined above
*/
String queryString = ...

/*
* Declare a List containning the names of the roles we
* wish to match on
*/
ArrayList roleList = new ArrayList();
roleList.add("role_a");
roleList.add("role_b");

Session s = ....
Query q = s.createQuery(queryString);
q.setParameterList("roleList", roleList);
q.setInteger("roleCount", roleList.size());

List users = q.list();


You will see that the results returned by our Hibernate Query are exactly the same as those returned by our SQL SELECT statement, but with a greatly reduced amount of complexity.

This type of scenario is very common when dealing with many-to-many relations. For example, a site that maintains user profiles and keywords describing those profiles. We could modify our code slightly to allow a user to search for profiles that contain the keywords "jazz" and "hamburgers". Another example would be an online shopping site. It's very easy to search for the keyword "dogs", or search for the keyword "books". Our approach now allows us to search for books about dogs. Additionally, we can modify our approach to allow for less refined matches. For example, modifying the last line in our query to read:

GROUP BY j.user HAVING COUNT (j.role) >= :roleCount

will allow us to match on a variable amount of roles. If we have a list of 25 roles, we could search for those users that are members of 10 or more by changing the roleCount variable to 10 instead of roleList.size().

As you can see, using Hibernate in our Java application makes performing these complex queries much easier than coding in raw JDBC.

Wednesday, August 9, 2006

JUnit Tests for Spring and Hibernate

There are two big things that I love about the Spring Framework:

  1. It has excellent Hibernate support through the use of its HibernateTemplate class
  2. It makes writing JUnit tests much simpler, yielding better code
However, up until now I hadn't been able to find a good way to combine these two beneftis and JUnit test Hibernate-based code that I had written using Spring as my foundataion. (Mostly because I'd never really bothered putting any effort into figuring out the best way to do it) Up until now, I had my JUnit tests create a Spring ApplicationContext, and simply obtained my Hibernate DAO and ran my tests. While this did accomplish my goal of determining if my Hibernate-based code was working properly, it was poor design, and was terribly inefficient.

Recently, however, I came across a great article explaining how to unit test Hibernate mapping configurations. The article goes into how to create your session factory, and wire up your DAO with a new HibernateTemplate. It even gives tips on how to setup an in-memory HSQLDB database for testing. This reduces the overhead needed to perform your tests, as you don't need a full-fledged SQL database to run your tests against.

If you write any Hibernate-based code and are looking for a means of testing your Hibernate configuration, this is article is a great referrence.

Friday, July 28, 2006

OdyssiCS v0.1 Released

At long last there is something coming out of my Odyssi CS project. Version 0.1 has just been posted on the project page. It is important to note, however, that this release should be considered alpha quality at best. A great deal of functionality has been left out of this release, and virtually all of the installation and configuration must be done manually. The main reason behind this release was just to get something out of the door. Some initial screenshots can be found here. The interface is pretty rough, and I've nearly completed a much more user-friendly interface that will be included in the next version. Version 0.2 will represent a pretty large redesign of the application, and should have much more functionality included.

At this point, the following is included in version 0.1:

  • Submit client certificate requests via a web browser (IE or Mozilla-based browsers)
  • Download an approved certificate into the client browser
  • Registration Authorities may approve or reject pending certificate requests
As you can see, the functionality is pretty minimal. In fact, the following are things that are not included in this release and are important to make note of:
  • Registration Authorities authenticate to the CA using a username/password combination managed by Tomcat (or whatever application server you have deployed to) -- NOT client certificates
  • Registration Authorities must have the "registrationAuthority" role in order to access the RA administration pages
  • No certificate extension support is included for any certificate generated by the CA, although this will be implemented very shortly. (There are a couple of bugs that need to be worked out)
  • No certificate revocation or CRL support is included
  • You may not currently specify which Java cryptography provider you wish to use
  • All installation and configuration must be done manually
The following libraries were used for this version of the project:
Version 0.2 will maintain its use of Spring and Hibernate, but Struts may be replaced by another web application framework. In addition, I plan to use more of the design patterns found in Core J2EE Patterns and Core Security Patterns to create a more robust and secure application. I'll also be writing some articles about these patterns, outlining how they are being used in the application.

I have also released version 0.1 of the Odyssi ASN.1 library. This library provides the ASN.1 encoding for certificate and certificate extensions. It is extracted from the BouncyCastle JCE library, and has been modified to work with any Java crypto provider. A pre-build JAR is already included in the Odyssi CS distribution, so you will not need to build it separately. However, it is provided as a separate download for you, if you wish.

For those of you who are brave enough, Version 0.1 can be downloaded here. Make sure you read the INSTALL.txt and README.txt files for information on how to configure and install the web application. And, as always, please make use of the forums for any questions, comments, or criticisms you may have. Good luck!