ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Configuration Management in Java EE Applications Using Subversion

by Swaminathan Radhakrishnan
05/03/2006

The most critical asset of any enterprise application is the data that it stores. Today's enterprise applications are often required to not just store data, but also keep track of all the changes that are made. This expectation also permeates into an associated set of requirements, such as tracking the reason for each change, the time of the change etc. In many cases, the data tracking requirements apply to data that applications store in the form of documents. Using Subversion can help satisfy these complicated, albeit common, requirements.

The Data Challenge

Enterprise applications store critical pieces of data, and the requirements of such applications do not stop at just the create, read, update and delete (CRUD) operations on the data. Applications are expected to store the history of all the changes to such data. Furthermore, organizations have a variety of legal and regulatory compliance mandates that ultimately result in requirements to not just track every change to critical data assets, but also associated requirements such as who made the changes to the data, what time a change was made, what changes were made, why the change made, etc.

Application data also varies widely in form and size. Applications are required to store data varying from simple forms, such as strings and numbers, to complex types, such as documents that are stored as blobs and clobs. A certain class of applications deals very heavily with data in the form of documents uploaded into the application. Tracking data in complex forms such as documents can become a nightmare if traditional approaches, such as history tables, are chosen.

Tracking with History Tables

Relational databases are generally the preferred choice for storing application data. They help organize, store, and retrieve the data in a very efficient manner. Since application data is stored in these relational databases, applications try to use these databases for tracking historical data as well. The most pervasive approach to storing historical data is to have a time-stamped history table for every table that stores important application entities. Updates made to the main table result in actions that push out the previous values of data to the history tables. This is either done through triggers or by the applications themselves.

There are several issues with storing the historical information in history tables.

The relational databases should still be the repository for storing and retrieving transactional data. They excel in managing these critical data assets. The shortcomings listed above are confined to storing multiple versions of data entities within the relational data store and tracking such entities over time.

Subversion and JavaSVN

Subversion is a version control system that's a replacement for CVS, an older version control system. Subversion organizes data as files and directories in the form of a tree called a repository. Subversion tracks all the changes that are made to any asset stored in this repository. Subversion can have a centrally located repository and allows multiple concurrent updates to the repository. It can access its repository over HTTP and HTTPS using the WebDAV protocol, which helps avoid firewall issues that often crop up during deployment. Subversion works on the concept of copy-modify-merge, which effectively means that there is no requirement to lock an object before making changes to it.

JavaSVN is a pure-Java Subversion client library. It offers APIs for Java applications to interact with Subversion. JavaSVN offers low-level APIs that can directly interact with a repository or high-level APIs to manage working copies checked out from a repository.

Applications can use a combination of relational databases and Subversion to satisfy data management and data tracking requirements. Any updates that are made to the critical data assets present in the relational database would be accompanied with a commit into Subversion. Subversion would be the primary data source for use-cases for tracking, while the relational database would be used for all other purposes. An additional advantage is that due to Subversion's copy-modify-merge concept, there is no requirement to lock an object every time its retrieved from the relational database.

Learning by Example

Now that we've stated the problem and proposed a potential solution, let's use an example to illustrate the usage of Subversion and JavaSVN to solve the problem. We will store a sample domain object into Subversion using the JavaSVN API. We will also retrieve previous versions and display differences between two versions. Our sample domain object is the LoanData object as shown below. The complete code used in this article can be found in the Resources section at the end of this article.

public class LoanData extends BaseTrackingObject {
        private String loanId;
        private double loanAmount;
        private float  loanRate;
        private int    loanTerm;
        ......
        ......
}

An abstract BaseTrackingObject class defines the commonly required tracking information, such as modifiedUser, modificationDate, and modificationReason. It defines the abstract methods to set and retrieve the objectId that can acts as the primary key for the domain object. It also defines a utility method called getXmlRepresentation that we will use to convert the object representation to an XML format. This format will be used to store and retrieve data from Subversion.

Version Control with Subversion

Related Reading

Version Control with Subversion
By Ben Collins-Sussman, Brian W. Fitzpatrick, C. Michael Pilato

Initializing JavaSVN

The class SVNManager is our gateway to Subversion. It uses the low-level JavaSVN APIs to directly interact with the repository since we do not deal with working copies. We initialize the JavaSVN library to either interact with the repository over HTTP(S) or SVN(S). We have chosen to interact with the repository using HTTP (WebDAV), since this will lead to fewer deployment/firewall issues.

The library is initialized using the call DAVRepositoryFactory.setup(). The class SVNRepository has all the required methods to directly interact with the Subversion repository. This class is initialized using the SVNRepositoryFactory class by providing the root of the tree hierarchy in the repository. The class ISVNAuthenticationManager is used to provide the authentication information required for SVNRepository to interact with the repository.

public void initRepository() {
        //initialize the system to work over http
        DAVRepositoryFactory.setup();
        ............
        //point to the root folder of repository
        SVNURL svnUrl = SVNURL.parseURIEncoded
                        ("http://localhost/repos/");
        //initialize the SVNRepository
        theRepository = SVNRepositoryFactory.
                        create(svnUrl);
        //Creates the Auth manager with our user
        //and password credentials
        ISVNAuthenticationManager authManager =
                new BasicAuthenticationManager
                (name, password);
        //provides the credentials to the
        //SVNRepository
        theManager.setAuthenticationManager
                (authManager);
        ........
}

Storing Data in Subversion

Subversion stores data in a hierarchical format, meaning we have to come up with a standard for storing our domain entities. For the sake of this example, we will store all the domain objects under the folder DomainObjects. The class of the domain object will determine the sub-directory under which all the domain objects of that type will be stored. The individual domain objects are stored as XML files with the name of their primary keys.

To store the LoanData domain object, we will invoke the checkInObject method of our SVNManager object. An implementation of the ISVNEditor obtained through the SVNRepository object is used to create or update revisions of our domain object in the repository. All the operations performed on the editor will be committed only when the closeEdit method is called. The SVNDeltaGenerator class is used to obtain differences between the version currently in the repository with one that is being updated. Subversion receives updates for new versions in the form of such deltas rather than the entire object to improve network performance.

public SVNResult checkInObject(
                BaseTrackingObject obj){
        .....
        //Obtain the editor handle from the
        //repository
        ISVNEditor editor = theRepository.
                getCommitEditor(obj.
                getModificationReason(), null);
        ....
        //create the file at the specified path
                editor.addFile(path, null, -1);
        }
        else {
                //file is already present, so open
                //the file in the repository
                editor.openFile(path, -1);
        }
        ....
        String checksum = deltaGenerator.
                        sendDelta(path,
                        new ByteArrayInputStream(
                        obj.getXmlRepresentation().
                        getBytes()),
                        editor, true);
        .....
        editor.closeEdit();
        ...
}

Retrieving Change History

To retrieve the history of versions for a particular domain object, we will call SVNManager's getRevisionLog method. The latest revision number of the repository can be obtained using SVNRepository's getLatestRevision() method. The SVNManager.log method helps obtain the log entries associated with each revision. The log entries contain information such as when was the revision created, who created the revision, what was the revision number, etc. A call to the SVNManager.getFile method helps retrieve a particular revision from the repository.

public List getRevisionLog(BaseTrackingObject
        obj, long startRevision,
        long endRevision) {
        .....
        if(endRevision==-1) {
                //obtain the latest revision from
                //the repository
                endRevision =
                        theRepository.getLatestRevision();
        }
        //retrieve the various log entries for
        //the particular object path
        Collection revisionLogs = theRepository.
                log(new String[] {path}, null,
                        startRevision, endRevision,
                        false, true);
        ....
        //Obtain the contents of the object at
        //the specified revision
        theRepository.getFile(path, revisionInfo.
                getRevisionNumber(),
                new Properties(),
                xmlFileContentsStream);
        ....
}

Retrieving Differences Between Versions

To determine the differences that exist between two revisions, the SVNManager.showDifferences method is used. The JavaSVN class SVNDiffManager has the required implementation for getting the differences between two versions. A handle to this class can be obtained using the SVNClientManager class. The SVNDiffManager's doDiff method has a default implementation for returning the differences in a fixed format into the OutputStream passed in as a parameter. We can have custom implementations for getting the differences between two versions through the ISVNEditor class, but we will go with the default implementation for this example.

public String showDifferences(
                BaseTrackingObject obj,long revision1,
                long revision2) {
        ....
        //Create an instance of SVNClientManager
        //providing authentication
        SVNClientManager ourClientManager =
                        SVNClientManager.newInstance(
                        options, "name", "password");
        //Obtain the handle to DiffClient
        //from the client manager
        SVNDiffClient theDiff = ourClientManager
                        .getDiffClient();
        ....
        theDiff.doDiff(svnUrl, SVNRevision.
                create(revision1), svnUrl,
                SVNRevision.create(revision2),
                false, false, diffStream);
        ....
}

Conclusions

Enterprise applications deal with requirements to not just store and retrieve critical data assets, but also track the historical changes to such data. The traditional approaches to solve these requirements using relational databases do not present an elegant solution. Subversion, a version tracking system offers the required amount of support for tracking our sample LoanData object over time. The JavaSVN APIs were used for tasks such as storing the loan object, retrieving the object, retrieving the log of changes that are made to the object, and also showing the differences between two versions of the object.

We have examined only a basic set of features but there is plenty of support from Subversion for far more sophisticated requirements that are not uncommon for an enterprise-level application. Happy exploring!

Resources

Swaminathan Radhakrishnan works as a senior technical architect for Infosys Technologies, Ltd.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.