Simplifying Java with Jakarta Commons Lang
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.
** First Published on dev2dev
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:
- Commons Proper: Projects in the
Commons Proper can be termed as ready for production use. - 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:
- Check for null and empty string.
- Ignore white spaces.
- The license key is case-sensitive.
- 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”.
- Both parts should have four characters.
- 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 > 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 >"+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.
Download
Download the code used in this article: examples.zip (3KB)
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