Simplifying Java with Jakarta Commons Lang

by Harshad Oak
04/18/2005

Abstract

As enterprise Java developers, we are routinely required to implement functionality like parsing XML, working with HTTP, validating input, and processing dates. The Jakarta Commons project is an attempt to create components that can take care of all such commonly used tasks, freeing up your time to focus only on core business solutions. In this article we will provide a quick introduction to the Jakarta Commons project and then demonstrate how the Lang component within Jakarta Commons can be used to handle and simplify everyday Java tasks such as string manipulation, working with dates and calendars, comparing data objects, and sorting objects. For all examples, we will use the latest version of Lang, version 2.1.

Editor's Note: The code in this article is written to the RC1 release of Commons Lang. The final release is due out soon.

Introduction to Commons and the Lang Component

Jakarta Commons is a project meant solely for reusable Java components. The project has dozens of components for simplifying Java development and addressing one particular requirement well. The range of available components is huge, and they aren't limited for use only in Java applications of a particular type.

The projects are classified into two parts:

  1. Commons Proper: Projects in the Commons Proper can be termed as ready for production use.
  2. Commons Sandbox: Projects in the sandbox are still in the experimental stage.

There are currently 33 projects in the Commons Proper and 22 projects in the works in the Commons Sandbox so there is something in there for every kind of Java project.

The Lang component is one of the more popular components in Jakarta Commons. Lang is a set of classes that you wish were present in J2SE itself.

In this article we will look at some of the most useful features of Lang. Note that you can do everything that Lang does using just the basic Java classes, however it is far easier to use Lang than it is to study, understand, and write the code yourself. Even if you can write superb code, using the tried and tested capabilities of Lang will be quicker and will save considerable review and testing cycle time. Lang comes with proper JUnit test cases and as it is used so widely, it has been well tested by its creators as well as by the real world.

An important feature of Lang is its simplicity. New Java components generally are so complex that unless you know A, B, C, and D technologies, you will not be able to use that component. Often, it is difficult to even understand what a component is trying to achieve, let alone actually use the component. This is not the case with most of the Commons components. Components like Lang are easy to use and very useful to a Java beginner as well as an advanced Java user.

If you need star endorsements before you adopt a technology, try this: Search for commons-lang*.jar in the directory where you keep your Java software. You will be surprised. Tomcat, Struts, Hibernate, Spring, and WebWork are just some of the popular Java projects that make use of Lang.

Let's start with string manipulation with Lang, a task that most Java developers have to do almost every day.

Playing with Strings

Any application irrespective of whether it is a Swing-, J2EE-, or J2ME-based one, has to work with strings. So although working with a string in Java is fairly simple, if you wish to modify and manipulate the string based on certain conditions, things don't stay that simple. You have to hunt for obscure methods in the various string-related classes and then somehow get them to work together to get you the desired result. While there are some Lang methods that do overlap with methods present in J2SE, in most cases one Lang method provides the functionality of many J2SE methods from various classes, working together to get you the desired output.

The Lang component has many classes dedicated to string manipulation. We will now use a basic Java application to demonstrate some of the more useful classes and methods.

String manipulation is generally involved when your application takes input from a user and you either do not trust what the user will enter or the user might enter data in various formats but you only wish to work and store in one format.

Credit card number rules work somewhat like this, so if I tell you I have a MasterCard and the card number begins with 4, you'll know I am lying right away. Refer to Anatomy of Credit Card Numbers

As an example, you have a form with an input box for the user to enter a license key. You wish to allow a key in the format 1111-JAVA. The things you have to provide for are:

  1. Check for null and empty string.
  2. Ignore white spaces.
  3. The license key is case-sensitive.
  4. Split the key string using the - sign, and then check if the first part is all numeric and the second part contains characters only from the set of valid characters "J", "A", "V", "A".
  5. Both parts should have four characters.
  6. The fourth digit in the first part should be a "0".

Only if the key fulfills all these conditions do you want your application to go to the trouble of going to the database and checking if the key is a legitimate one.

Can you do all this without spending a fair amount of time browsing through the API documentation for the String, StringTokenizer, and other classes? I can't, so I will now try to manage the validation using the Lang component.

Listing 1. The checkLicenseKey() method

/**

 * Check if the key is valid

 * @param key license key value

 * @return true if key is valid, false otherwise.

 */

public static boolean checkLicenseKey(String key){

    //checks if empty or null

    if(StringUtils.isBlank(key)){

        return false;

    }



    //delete all white space

    key= StringUtils.deleteWhitespace(key);



    //Split String using the - separator

    String[] keySplit = StringUtils.split(key, "-");



    //check lengths of whole and parts

    if(keySplit.length != 2

        || keySplit[0].length() != 4

        || keySplit[1].length() != 4) {

        return false;

    }



    //Check if first part is numeric

    if(!StringUtils.isNumeric(keySplit[0])){

        return false;

    }



    //Check if second part contains only

    //the four characters 'J', 'A', 'V' and 'A'

    if(! StringUtils.containsOnly(keySplit[1]

            ,new char[]{'J', 'A', 'V', 'A'})){

        return false;

    }



    //Check if the fourth character

          //in the first part is a '0'

    if(StringUtils.indexOf(keySplit[0], '0') != 3){

        return false;

    }



    //If all conditions are fulfilled, key is valid.

    return true;

}

In Listing 1, we utilize various methods provided in the org.apache.commons.lang.StringUtils class to validate a string according to all the rules that we defined earlier. The isBlank()method checks if the string is empty or null. The deleteWhitespace() method ensures that the string is free of white spaces. We then split the string using the split() method and validate the two portions using the isNumeric(), containsOnly(), and indexOf() methods.

Note that even though the indexOf() method is already present in J2SE, using the indexOf() in StringUtils is a better choice. Unlike the J2SE indexOf() method, with the StringUtils indexOf() you do not have to worry about null. Triggering NullPointerException is believed to be the most common error committed by Java programmers. Lang will ensure that you do not commit the same mistake. Even if you pass a null to the indexOf() method or any other method for that matter, it will not throw a NullPointerException. In the case of indexOf(), it will simply return -1.

So in just a few lines of pretty straightforward code, we have been able to achieve what would otherwise have taken many more lines of code and a lot more trouble. If we execute the checkLicenseKey() method using a main method as shown in Listing 2, you will get an output as shown in Listing 3.

Listing 2. The main() method

public static void main(String[] args) {

    String []key= {"1210-JVAJ","1211-JVAJ", "210-JVAJ", "1210-ZVAJ"};

    for (int i=0; i < key.length; i++){

        if(checkLicenseKey(key[i])){

            System.out.println(key[i]+ " >> Is Valid");

        }

        else{

            System.out.println(key[i]+ " >> Is InValid");

        }

    }

}

Listing 3. The output

1210-JVAJ >> Is Valid

1211-JVAJ >> Is InValid

210-JVAJ >> Is InValid

1210-ZVAJ >> Is InValid

While org.apache.commons.lang.StringUtils has most of the methods meant for string manipulation, there are other classes that can also help. CharUtils and CharSetUtils provide utility methods for working with characters and character sets respectively. WordUtils is a class first seen in version 2.0 and is meant to house utility methods specifically for working with words. However, as there is significant overlap between what you can do with strings and words, this class does seem a little unnecessary. RandomStringUtils is a class that can help generate random strings based on various rules.

We will now look at another useful facet of Lang: the ability to work with dates and times.

Time Tricks

Working with dates and times in Java is quite a tricky task. Using java.text.SimpleDateFormat, java.util.Calendar, java.util.Date, and so on, takes some getting used to, and it requires a pretty sound understanding of each of the classes and interfaces involved to be able to play with dates and times.

The Lang component drastically simplifies working with dates and formatting them. You can easily format a date for display, compare dates, round or truncate dates, or even get all dates in a certain range.

Listing 4. Working with dates and times

public static void main(String[] args) throws InterruptedException, ParseException {

    //date1 created

    Date date1= new Date();

    //Print the date and time at this instant

    System.out.println("The time right now is >>"+date1);



    //Thread sleep for 1000 ms

    Thread.currentThread().sleep(DateUtils.MILLIS_IN_SECOND);



    //date2 created.

    Date date2= new Date();



    //Check if date1 and date2 have the same day

    System.out.println("Is Same Day >> "

        + DateUtils.isSameDay(date1, date2));



    //Check if date1 and date2 have the same instance

    System.out.println("Is Same Instant >> "

        +DateUtils.isSameInstant(date1, date2));



    //Round the hour

    System.out.println("Date after rounding >>"

        +DateUtils.round(date1, Calendar.HOUR));



    //Truncate the hour

    System.out.println("Date after truncation >>"

        +DateUtils.truncate(date1, Calendar.HOUR));



    //Three dates in three different formats

    String [] dates={"2005.03.24 11:03:26", "2005-03-24 11:03", "2005/03/24"};



    //Iterate over dates and parse strings to java.util.Date objects

    for(int i=0; i < dates.length; i++){

        Date parsedDate= DateUtils.parseDate(dates[i],

        new String []{"yyyy/MM/dd", "yyyy.MM.dd HH:mm:ss", "yyyy-MM-dd HH:mm"});



        System.out.println("Parsed Date is >>"+parsedDate);

    }



    //Display date in HH:mm:ss format

    System.out.println("Now >>"

        +DateFormatUtils.ISO_TIME_NO_T_FORMAT.format(System.currentTimeMillis()));

}

Listing 4 demonstrates some of the capabilities of the org.apache.commons.lang.DateUtils and org.apache.commons.lang.DateFormatStringUtils classes. There are many other methods that do the same thing but take various forms of input. So in all probability, if you have to parse or format a date, you should be able to do that in a single line using one of the methods provided.

The output on executing the code in Listing 4 is shown in Listing 5.

Listing 5. The output

The time right now is >>Sat Apr 09 14:40:41 GMT+05:30 2005

Is Same Day >> true

Is Same Instant >> false

Date after rounding >>Sat Apr 09 15:00:00 GMT+05:30 2005

Date after truncation >>Sat Apr 09 14:00:00 GMT+05:30 2005

Parsed Date is >>Thu Mar 24 11:03:26 GMT+05:30 2005

Parsed Date is >>Thu Mar 24 11:03:00 GMT+05:30 2005

Parsed Date is >>Thu Mar 24 00:00:00 GMT+05:30 2005

Now >>14:40:43

In Listing 4, we have created two dates with a difference of one second between them. Then we check if the dates are the same using the isSameInstant() and the isSameDay() method. Next we round off and truncate the date before we take up the special case of date parsing using various formats specified in an array.

Often when you are integrating your application with third-party applications, you are not one hundred-percent sure of what the input might be. I had once worked on integration with a legacy application that always seemed to have three answers to every question. So if you have to parse dates provided by such an application, you need to provide for three or four different date formats. The parseDate() method usage in Listing 4 does just this. So even if the input varies, it is able to parse the date. Also note that the patterns in the array are not in the same order as the input and yet the method finds the appropriate pattern and parses accordingly.

Finally, we format and print the date as per the ISO_TIME_NO_T_FORMAT format, which is HH:mm:ss. We will now look at using Lang to generate the commonly used method, toString().

Generate the toString() Method

Methods like equals(), toString(), and hashCode() are used on a regular basis. However, when it comes to actually writing implementations for these methods, not only are most of us reluctant to do that but we are also not sure how exactly and easily to write them. The builder package provides utility classes that can help you easily create implementations for these methods. In most cases it just takes one line of code. We will look at the toString capabilities of Lang.

The toString() method

You might have noticed in Listing 4 that even if we just pass an object of java.util.Date to System.out.println(), the output we get is a proper display of the date and time. This is possible because when you just pass an object reference, the toString() method gets called automatically. So in our example we are essentially calling the toString() method of the java.util.Date class and the proper output we get is because someone has taken the trouble to override the toString() method from the java.lang.Object class in the java.util.Date class.

If the toString() method is not overridden, the output you get is just the name of the class and the hashcode. No data in the class will get displayed. So if you have written a new class and wish to get a proper output on print, you need to override the toString() method in your class.

Listing 6. The toString() method

public class Computer {



    String processor;

    String color;

    int cost;



    /** Creates a new instance of Computer */

    public Computer(String processor, String color, int cost) {

        this.processor=processor;

        this.color=color;

        this.cost=cost;

    }



    public static void main(String[] args) {

        Computer myComp=new Computer("Pentium","black",1000);

        System.out.println(myComp);

    }

    

    public String toString(){

        return ToStringBuilder.reflectionToString(this);

        /*

        return ToStringBuilder.reflectionToString(this

            , ToStringStyle.SHORT_PREFIX_STYLE);

        return ToStringBuilder.reflectionToString(this

            , ToStringStyle.MULTI_LINE_STYLE);

        return new ToStringBuilder(this)

            .append("processor", processor).toString();

         */

    }

}

Listing 6 shows a Computer class that has three fields in it. The toString() method is what is special. The call to the reflectionToString() method is able to figure out which are the fields in the class, and then prints their name and value. In the main() method, we simply create an instance of the class and then print it. The output in this case is dev2dev.Computer@f6a746[processor=Pentium,color=black,cost=1000].

So if you don't wish to put too much effort into it and yet require toString() implementations for your classes, there is no easier way than to copy and paste these two lines of code into all your classes. If you wish to have more control over what is generated, look at the commented options. You can apply various styles to the output or even build the entire output by creating a new instance of ToStringBuilder. The output, if you were to execute each of the four return statements in the listed order, is shown in Listing 7.

Listing 7. The four possible outputs based on the ToStringBuilder method used

1) dev2dev.Computer@f6a746[processor=Pentium,color=black,cost=1000]

2) Computer[processor=Pentium,color=black,cost=1000]

3) dev2dev.Computer@f6a746[

   processor=Pentium

   color=black

   cost=1000

   ]

4) dev2dev.Computer@192d342[processor=Pentium]

Comparing and Sorting Objects

Having to compare data objects and sort them accordingly is a pretty common requirement. So how do you think we can compare and sort objects of the Computer class we saw in Listing 6?

You guessed it! Let's use Lang to sort Computer objects based on the cost of the computer. To compare objects, you need to implement the java.lang.Comparable interface. This interface has a single method compareTo(Object). The method implementation is expected to compare the current object with the object that is passed to the method. The method returns a negative integer, zero, or a positive integer if this object is less than, equal to, or greater than the specified object.

So to compare Computer objects, implement the compareTo() method in the class Computer, as shown in Listing 8.

Listing 8. A compareTo() method

public int compareTo(Object obj) {

    Computer anotherComputer = (Computer)obj;

    //return new CompareToBuilder().reflectionCompare(this, anotherComputer);

    return new CompareToBuilder().

                        append(this.cost, anotherComputer.cost).toComparison();

}

Then, to actually try out the comparison, we write a simple class named ComputerSort, as shown in Listing 9. We just add three objects to an ArrayList and then sort it.

Listing 9. ComputerSort class

public class ComputerSort  {



    public static void main(String[] args) {

        ArrayList computerList = new ArrayList();

        computerList.add(new Computer("Pentium","black", 1000));

        computerList.add(new Computer("Pentium","chocolate", 334));

        computerList.add(new Computer("Pentium","darkgray", 2234));



        Collections.sort(computerList);

        System.out.println(computerList);

    }



}

When we execute ComputerSort, we will see that the objects get sorted by the value of the field cost. The CompareToBuilder like the ToStringBuilder also has a reflection-based usage option. We have commented that bit in the compareTo() method in Listing 8 because in this case the reflection option will compare all fields and get us an incorrect result. CompareToBuilder also has methods that can be used if you not only wish to compare fields in the current class but also its super class. The output on execution of the ComputerSort class is as shown in Listing 10.

Listing 10. Sorted Computer objects

[dev2dev.Computer@cf2c80[processor=Pentium,color=chocolate,cost=334]

, dev2dev.Computer@12dacd1[processor=Pentium,color=black,cost=1000]

, dev2dev.Computer@1ad086a[processor=Pentium,color=darkgray,cost=2234]]

Conclusion

In this article we took a look at some of the key capabilities of the Jakarta Commons Lang component. The Commons project as a whole is a very useful yet underutilized project. While open source projects do use many Commons components, their adoption outside the open source world isn't nearly as widespread. Now that you have an understanding of what Lang has to offer, you should look at adopting it right away. You can also look at the Commons project for useful components that can simplify XML parsing, enable your application to talk HTTP, systematize validations, and perform many other functions.

References

Harshad Oak is the creator of the Java J2EE portal IndicThreads.com. He wrote Pro Jakarta Commons and Oracle JDeveloper 10g: Empowering J2EE Development as well as coauthored Java 2 Enterprise Edition 1.4 Bible. He is also the founder of Rightrix Solutions