Chapter 18

Useful Libraries

This chapter examines some utilities from three commonly used Java libraries: Apache Commons, Guava, and Joda Time. All three libraries attempt to provide extra, useful functionality that the developers believed was missing from the Standard Java APIs, but was common enough to be written into a reusable library.

What kinds of problems these libraries and their functions alleviate can often be asked as interview questions, but it is also important for experienced developers to know what is implemented in common libraries so that they do not need to reinvent the wheel every time they come across the same pattern.

For any mature project, it is not uncommon for all three of these libraries to appear in some way, but many more libraries are available than just the three shown here.

Removing Boilerplate Code with Apache Commons


How do you properly escape Strings?


The Commons Lang library complements the functionality found in the java.lang package, including several libraries for manipulating Strings.

Many systems work over many platforms and interfaces. It is common to see a Java server that serves HTML pages and receives input from HTML forms, or you can use a Java application to read and write a comma-separated values file. The StringEscapeUtils class provides the facility to represent a String in that environment. Listing 18-1 shows an example of how you could use the StringEscapeUtils class to provide some HTML formatting.

Listing 18-1: Escaping Strings with Apache Commons

@Test
public void escapeStrings() {
    final String exampleText = "Left & Right";
    final String escapedString = 
            StringEscapeUtils.escapeHtml4(exampleText);
    assertEquals("Left & Right", escapedString);
}

The class can also escape Strings for JavaScript, XML, and Java itself. This can be useful if you ever need to write Java source code programmatically.


How do you write anInputStreamto anOutputStream?


The Apache Commons IO library has several operations for streams that significantly reduce the amount of boilerplate code you need to write.

One common pattern is the need to connect an InputStream to an OutputStream. For example, you may want to write the contents of an incoming HTTP request straight to disk for a file upload form or something similar.

Listing 18-2 shows the copy method on the IOUtils class.

Listing 18-2: Connecting an InputStream to an OutputStream

@Test
public void connectStreams() throws IOException {
    final String exampleText = "Text to be streamed";
    final InputStream inputStream =
            new ByteArrayInputStream(exampleText.getBytes());

    final OutputStream outputStream = new ByteArrayOutputStream();

    IOUtils.copy(inputStream, outputStream);

    final String streamContents = outputStream.toString();
    assertEquals(exampleText, streamContents);
    assertNotSame(exampleText, streamContents);
}

The assertNotSame method confirms that a brand new String was constructed, and that the copy method transferred each byte to the OutputStream.

Listing 18-2 used a ByteArrayInputStream and ByteArrayOutputStream to demonstrate this functionality, but both streams were declared against the interface. In practice, you will more than likely use different streams and rely on reading from or writing to a third party, such as the disk or the network interface.

The copy method on the IOUtils class is heavily overloaded to give you as much flexibility as possible in copying a source of bytes from one location to another. You can use it to connect Readers to Writers, to connect a Reader to an OutputStream, or to connect an InputStream to a Writer.

Be aware, too, that the toString method on an OutputStream will not necessarily give the contents of the stream, because these could have been written to disk, or the network, or some other irretrievable place. Some OutputStream implementations do not override toString at all.

IOUtils provides a way to consume an InputStream to convert it to a String, as shown in Listing 18-3.

Listing 18-3: From an InputStream to a String

@Test
public void outputStreamToString() throws IOException {
    String exampleText = "An example String";
    final InputStream inputStream = 
            new ByteArrayInputStream(exampleText.getBytes());

    final String consumedString = IOUtils.toString(inputStream);

    assertEquals(exampleText, consumedString);
    assertNotSame(exampleText, consumedString);
}

Again, the toString method is heavily overloaded, and can convert Readers, too.


How can you split anOutputStreaminto two streams?


Similar to the tee command in UNIX, Apache Commons provides a TeeOutputStream; its constructor takes two OutputStream instances, and writes to both. This can be useful for auditing output from an application, such as logging responses. Listing 18-4 shows an example.

Listing 18-4: Splitting an OutputStream

@Test
public void outputStreamSplit() throws IOException {
    final String exampleText = "A string to be streamed";
    final InputStream inputStream = IOUtils.toInputStream(exampleText);

    final File tempFile = File.createTempFile("example", "txt");
    tempFile.deleteOnExit();
    final OutputStream stream1 = new FileOutputStream(tempFile);
    final OutputStream stream2 = new ByteArrayOutputStream();

    final OutputStream tee = new TeeOutputStream(stream1, stream2);

    IOUtils.copy(inputStream, tee);

    final FileInputStream fis = new FileInputStream(tempFile);
    final String stream1Contents = IOUtils.toString(fis);
final String stream2Contents = stream2.toString();

    assertEquals(exampleText, stream1Contents);
    assertEquals(exampleText, stream2Contents);
}

Listing 18-4 writes to only one location, but the TeeOutputStream takes care of writing the data to both streams.

This code uses the Apache Commons IO extensively. As well as the TeeOutputStream class under test, the test text is turned into an InputStream using IOUtils.toInputStream; the data is copied between streams using the previously seen copy method; and the FileInputStream is consumed with IOUtils.toString. A useful exercise would be to rewrite this test using only the standard Java libraries, and see just how much extra code you need to write.

Developing with Guava Collections

Guava is a set of libraries originally developed by Google for use in its own Java projects. It concentrates on collections, I/O, and math, as well as some crossover with the Apache Commons libraries. It can be seen as an extension to the data structures included as part of the Java Collections API, which was covered in Chapter 5.


Are any types of collections missing from Java’s standard library?


Guava adds a collection called a Multiset. It enables you to have a set that allows multiple, equal elements. Additional methods on the Multiset class enable you to count the number of a particular element, and to turn it into a regular set. Similar to the Collections library, several Multiset implementations exist, such as a HashMultiset, a TreeMultiset, and a ConcurrentHashMultiset.

Listing 18-5 shows Multiset in use.

Listing 18-5: Using a Multiset

@Test
public void multiset() {
    final Multiset<String> strings = HashMultiset.create();

    strings.add("one");
    strings.add("two");
    strings.add("two");
    strings.add("three");
    strings.add("three");
    strings.add("three");

    assertEquals(6, strings.size());
    assertEquals(2, strings.count("two"));

    final Set<String> stringSet = strings.elementSet();

    assertEquals(3, stringSet.size());
}

This keeps a running total of the count of each element. It can be helpful to think of a Multiset as a Map between that element and an integer to keep the count. Though it is definitely possible to implement the notion of a MultiSet this way, the Guava implementation is more powerful because the size method returns the element count, whereas the Map implementation returns the number of unique elements. Additionally, an Iterator over the Map returns each element only once, regardless of how many are in the collection.

A Multimap is similar to a Multiset—it is possible to have a key appear more than once in the Map.

A common pattern when using a Map to store multiple values against a key is to store a mapping from a key to a List, or Collection, of values. You then often see the following pseudocode when attempting to add value against a particular key:

method put(key, value):
  if (map contains key):
    let coll = map(key)
    coll.add(value)
  else:
    let coll = new list
    coll.add(value)
    map.put(key, coll)

This is brittle, prone to errors, and you also need to make sure the code is thread-safe. Guava’s Multimap takes care of this for you.

Listing 18-6 shows an example of its use.

Listing 18-6: A Multimap address book

@Test
public void multimap() {
    final Multimap<String, String> mapping = HashMultimap.create();

    mapping.put("17 High Street", "Alice Smith");
    mapping.put("17 High Street", "Bob Smith");
    mapping.put("3 Hill Lane", "Simon Anderson");

    final Collection<String> smiths = mapping.get("17 High Street");
    assertEquals(2, smiths.size());
    assertTrue(smiths.contains("Alice Smith"));
    assertTrue(smiths.contains("Bob Smith"));

    assertEquals(1, mapping.get("3 Hill Lane").size());
}

The get method does not return an instance of the value type of the map; it returns a Collection of that type.

The Multimap differs from Java’s Map interface in several ways. The get method never returns null—when getting a nonexistent key, an empty collection will be returned. Another difference is that the size method returns the number of entries, not the number of keys.

Similar to the Multiset, you can convert your Multimap into a Java Map using the asMap method. This returns Map<Key, Collection<Value>>.

Another type of map provided by Guava is the BiMap interface. This provides a two-way lookup: Keys can look up values, and values can look up keys. Implementing this using Java’s Map is particularly tricky; you need to keep two maps, one for each direction, and ensure that a particular value is never duplicated, regardless of which key it is associated with.

Listing 18-7 shows an example of mapping stocks to their company name.

Listing 18-7: Using a BiMap

@Test
public void bimap() {
    final BiMap<String, String> stockToCompany = HashBiMap.create();
    final BiMap<String, String> companyToStock = 
                                           stockToCompany.inverse();

    stockToCompany.put("GOOG", "Google");
    stockToCompany.put("AAPL", "Apple");
    companyToStock.put("Facebook", "FB");

    assertEquals("Google", stockToCompany.get("GOOG"));
    assertEquals("AAPL", companyToStock.get("Apple"));
    assertEquals("Facebook", stockToCompany.get("FB"));
}

The inverse method is a reference to the original BiMap, so any additions in the originally created map are reflected in the inverted map, and vice versa.


How do you create an immutable collection?


The Collections utility class in the Java Collections API provides some utility methods for creating unmodifiable collections, as shown in Listing 18-8.

Listing 18-8: Creating an unmodifiable collection

@Test
public void unmodifiableCollection() {
    final List<Integer> numbers = new ArrayList<>();
    numbers.add(1);
    numbers.add(2);
    numbers.add(3);

    final List<Integer> unmodifiableNumbers = 
            Collections.unmodifiableList(numbers);

    try {
        unmodifiableNumbers.remove(0);
    } catch (UnsupportedOperationException e) {
        return; // test passed
    }

    fail();
}

This can be useful because any reference to unmodifiableNumbers simply cannot mutate the underlying list. If references to the original list still exist, the collection referenced in the unmodifiable collection can change, as shown in Listing 18-9.

Listing 18-9: Changing the underlying values of an unmodifiable collection

@Test
public void breakUnmodifiableCollection() {
    final List<Integer> numbers = new ArrayList<>();
    numbers.add(1);
    numbers.add(2);
    numbers.add(3);

    final List<Integer> unmodifiableNumbers = 
            Collections.unmodifiableList(numbers);

    assertEquals(Integer.valueOf(1), unmodifiableNumbers.get(0));

    numbers.remove(0);

    assertEquals(Integer.valueOf(2), unmodifiableNumbers.get(0));
}

Guava focuses on providing utilities to create unmodifable collections that are also immutable. Listing 18-10 shows how this works.

Listing 18-10: Creating immutable collections

@Test
public void immutableCollection() {
    final Set<Integer> numberSet = new HashSet<>();
    numberSet.add(10);
    numberSet.add(20);
    numberSet.add(30);

    final Set<Integer> immutableSet = ImmutableSet.copyOf(numberSet);

    numberSet.remove(10);
    assertTrue(immutableSet.contains(10));

    try {
        immutableSet.remove(10);
    } catch (Exception e) {
        return; // test passed
    }

    fail();
}

Of course, this is very different from the unmodifiable approach, because this will make a full copy of the collection, so if you intend to use this approach, you should be aware of any memory constraints that could become a concern.

The unmodifiable approach from the Java Collections API simply delegates to the underlying collection, but intercepts calls to any mutating methods and throws an exception instead. This means that any optimizations that are made for thread safety on the underlying collection are still part of the unmodifiable approach, even though an unmodifiable collection cannot be mutated, and is therefore thread-safe.

Because using Guava’s copyOf returns a copied collection, it is optimized for immutability; it does not need to ensure thread safety when accessing the elements. They simply cannot change.


Is it possible to create anIteratorover severalIterators?


Making your own “iterator of iterators” can be especially difficult to do by hand. You need to keep a reference to your most recent iterator, and when calling hasNext or next you may need to move to the next iterator, or even beyond that, if the next iterator has no elements at all.

Being asked to make this implementation can be a common interview question, but it is also provided out of the box by Guava, as shown in Listing 18-11.

Listing 18-11: Iterating over iterators

@Test
public void iterators() {
    final List<Integer> list1 = new ArrayList<>();
    list1.add(0);
    list1.add(1);
    list1.add(2);
    final List<Integer> list2 = Arrays.asList(3, 4);
    final List<Integer> list3 = new ArrayList<>();
    final List<Integer> list4 = Arrays.asList(5, 6, 7, 8, 9);

    final Iterable<Integer> iterable = Iterables.concat(
                                          list1, list2, list3, list4);
    final Iterator<Integer> iterator = iterable.iterator();

    for (int i = 0; i <= 9; i++) {
        assertEquals(Integer.valueOf(i), iterator.next());
    }

    assertFalse(iterator.hasNext());
}

The Iterables.concat method takes implementers of the Iterable interface.


Can you find the intersection of two Sets?


Surprisingly, many operations you would expect to see on the Set interface are not there: There is no clear way when dealing with Sets to perform a union, subtraction, or difference without modifying the provided Set. The Collection interface provides the addAll method to perform a union, removeAll for difference, and retainAll for intersection. All of these methods modify the collection on which you called the method.

An alternative is to use the Sets class from Guava. This is a class with several static methods for manipulating sets. Listing 18-12 shows how it is used.

Listing 18-12: Set operations with Guava

@Test
public void setOperations() {
    final Set<Integer> set1 = new HashSet<>();
    set1.add(1);
    set1.add(2);
    set1.add(3);

    final Set<Integer> set2 = new HashSet<>();
    set2.add(3);
    set2.add(4);
    set2.add(5);

    final SetView<Integer> unionView = Sets.union(set1, set2);
    assertEquals(5, unionView.immutableCopy().size());

    final SetView<Integer> differenceView = Sets.difference(set1, set2);
    assertEquals(2, differenceView.immutableCopy().size());

    final SetView<Integer> intersectionView = 
                                          Sets.intersection(set1, set2);
    set2.add(2);
    final Set<Integer> intersection = intersectionView.immutableCopy();
    assertTrue(intersection.contains(2));
    assertTrue(intersection.contains(3));
}

The union, difference, and intersection methods all return a SetView object, which is reliant on the underlying sets. SetView has a method that can return an immutable set of the elements that meet that SetView’s requirements. SetView is a view over the sets, so if the underlying sets change, the properties of SetView may change, too. This is what happened with the intersection call in Listing 18-12: after that intersection call, another element was added to the set2 object, and that value was in set1, so it was to be returned in the intersection of the two sets.

Utility classes for the core Java Collections interfaces include Maps, Sets, and Lists, and utility classes for the Guava interfaces include Multisets and Multimaps.

Using Joda Time

Joda Time is intended as a replacement for Java’s Date and Calendar classes. It provides much more functionality, and is generally considered easier to use. Java 8 will provide a new and improved date and time API based on Joda Time.


How do you use theDateTimeclass?


The Calendar class has traditionally been used for working with dates in Java. It has a strange access pattern, and can be slightly uncomfortable for newcomers to use. Listing 18-13 shows a simple operation that subtracts a month from the current date using the Calendar class.

Listing 18-13: Using Java’s Calendar class

@Test
public void javaCalendar() {
    final Calendar now = Calendar.getInstance();
    final Calendar lastMonth = Calendar.getInstance();
    lastMonth.add(Calendar.MONTH, -1);

    assertTrue(lastMonth.before(now));
}

The add method takes two integer parameters, and unless you are familiar with the API, you may not realize that the first parameter is intended to be a constant, one of a many special constant integers on the Calendar class.

This is partially a hangover from before Java 5, before it was possible to have enumerated types.

The DateTime class replaces Java’s Calendar class, and provides many clearer methods for retrieving and modifying a representation of time and date. Listing 18-14 shows how to use DateTime.

Listing 18-14: Using the DateTime object

@Test
public void dateTime() {
    final DateTime now = new DateTime();
    final DateTime lastWeek = new DateTime().minusDays(7);

    assertEquals(now.getDayOfWeek(), lastWeek.getDayOfWeek());
}

The DateTime class has many methods for getting the appropriate information about a date, such as the day of the month, the week of the year, and even to check for a leap year.

DateTime objects are immutable, so any “mutating” operation, such as minusDays in Listing 18-14, actually returns a new DateTime instance. Similar to String, if you do call any mutating methods remember to assign the result to a variable!


How can you work with Java’s Date object when using Joda Time?


When working with a third-party or legacy API, you may be constricted to using Date objects as method parameters or return types. Joda Time tries to make it as easy as possible to interact with Java’s Date and Calendar objects. The DateTime class can take both Date and Calendar instances as constructor parameters, and it has a toDate method that returns the DateTime representation as a Date. Listing 18-15 shows taking a Calendar instance, converting it to a DateTime, modifying the time, and converting it to a Java Date.

Listing 18-15: Converting to and from a DateTime object

@Test
public void withDateAndCalendar() {
    final Calendar nowCal = Calendar.getInstance();
    final DateTime nowDateTime = new DateTime(nowCal);

    final DateTime tenSecondsFuture = nowDateTime.plusSeconds(10);

    nowCal.add(Calendar.SECOND, 10);
    assertEquals(tenSecondsFuture.toDate(), nowCal.getTime());
}

What is the difference between a duration and a period in Joda Time?


The representation of amounts of time is a feature that is not part of Java’s Date and Calendar classes, and it is the simplicity of working with these in Joda Time that makes the library so popular.

A DateTime instance represents an instant; that is, a measurement of time exact to the millisecond. Joda Time also provides APIs for working with an amount of time between two instants.

A duration is an amount of time in milliseconds. If you add a duration to a DateTime, you will get a new DateTime instance. Listing 18-16 shows how this works.

Listing 18-16: Using a duration

@Test
public void duration() {
    final DateTime dateTime1 = new DateTime(2010, 1, 1, 0, 0, 0, 0);
    final DateTime dateTime2 = new DateTime(2010, 2, 1, 0, 0, 0, 0);

    final Duration duration = new Duration(dateTime1, dateTime2);

    final DateTime dateTime3 = new DateTime(2010, 9, 1, 0, 0, 0, 0);
    final DateTime dateTime4 = dateTime3.withDurationAdded(duration, 1);

    assertEquals(2, dateTime4.getDayOfMonth());
    assertEquals(10, dateTime4.getMonthOfYear());
}

The Duration instance is calculated as the difference between two DateTime instances; in this case midnight January 1, 2010, and midnight February 1, 2010. Internally, this is represented as a number of milliseconds, and in this case it will be equal to the number of milliseconds in 31 days.

This duration is then added, once, to another DateTime, representing midnight on September 1, 2010, and a new DateTime is returned. Because September has 30 days, the DateTime returned represents October 2, 2010.

With the irregularity of calendars—differing lengths of months and even years—you may still want to perform operations such as “the same day of the month, in two months’ time,” or “the same date next year.” You cannot simply add 60 or so days for two months and get the same date next year. You need to check if there is a February 29 between now and then, and add one if so.

The Period class removes all of these headaches for you. A period is a quantity of time; it is relative to the starting date, and can only be resolved once the start date is known. Listing 18-17 shows how this works.

Listing 18-17: Using a period

@Test
public void period() {
    final DateTime dateTime1 = new DateTime(2011, 2, 1, 0, 0, 0, 0);
    final DateTime dateTime2 = new DateTime(2011, 3, 1, 0, 0, 0, 0);

    final Period period = new Period(dateTime1, dateTime2);

    final DateTime dateTime3 = new DateTime(2012, 2, 1, 0, 0, 0, 0);
    final DateTime dateTime4 = dateTime3.withPeriodAdded(period, 1);

    assertEquals(1, dateTime4.getDayOfMonth());
    assertEquals(3, dateTime4.getMonthOfYear());
}

The Period in Listing 18-17 was calculated as the interval between February 1, 2011 and March 1, 2011. The Period was then applied to February 1, 2012, giving a new DateTime for March 1, 2012. A Period inspects the difference in each field between two DateTime instances, that is, the difference in months, days, hours, and so on. Although the number of days between the first two dates was 28 days, it was represented as a period of “one month.” When this period is applied to a leap year, that extra day is still part of that “one month,” so March 1, 2012 was returned. Had this interval been a Duration rather than a Period, the DateTime instance returned would have been the last day of February.


How do you convert a specific date into a human-readable representation?


As part of the Java API, the SimpleDateFormat class is commonly used to construct custom, human-readable dates. You provide a template for the date, and when given a Date instance, it returns a String representation of that date. Listing 18-18 shows this in use.

Listing 18-18: Using SimpleDateFormat

@Test
public void simpleDateFormat() {
    final Calendar cal = Calendar.getInstance();
    cal.set(Calendar.MONTH, Calendar.JULY);
    cal.set(Calendar.DAY_OF_MONTH, 17);

    final SimpleDateFormat formatter = 
              new SimpleDateFormat("'The date is 'dd MMMM");

    assertEquals(
            "The date is 17 July", 
            formatter.format(cal.getTime()));
}

The format string is hard to use, and can often lead to confusion: Lowercase m represents milliseconds and uppercase M represents months, for instance. Plaintext must be surrounded by single quotes. Any nontrivial case for using the SimpleDateFormat class is often accompanied by some time with the Java API documentation and some fiddling to get the format exactly right. Also, the SimpleDateFormat class is not thread-safe as the format String can change after the object has been created. DateTimeFormatter is immutable, which implies thread safety, as once it has been created it can never be changed.

The DateTimeFormatter can parse similar Strings that you would give to the SimpleDateFormat class, but Joda Time also provides a builder class, giving clearer meaning to each element of the string you are building. Listing 18-19 shows how to construct the same String as that in Listing 18-18.

Listing 18-19: Formatting dates with Joda Time

@Test
public void jodaFormat() {
    final DateTime dateTime = new DateTime()
            .withMonthOfYear(7)
            .withDayOfMonth(17);

    final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendLiteral("The date is ")
            .appendDayOfMonth(2)
            .appendLiteral(' ')
            .appendMonthOfYearText()
            .toFormatter();

    assertEquals("The date is 17 July", formatter.print(dateTime));
}

Summary

Java is mature enough that it has a large number of robust, well-supported libraries, from full application frameworks such as Tomcat, Spring, and Hibernate, all the way through to lower-level libraries that make the day-to-day work of a developer a little easier.

If you are writing the same pattern in code repeatedly, you may find that someone has encapsulated it into a library. When a well-known, established library has the same functionality as whatever you have written, it is often better to use the library. The chances are it will have been well tested, both with unit tests and also out in production of many other applications.

Even better: If you find you’re writing the same code over and over again, and there is no library for it, you could write one yourself. Make it open source, publish it on GitHub, and maintain it. Potential employers love to see interview candidates who have written and maintained open source code, and it also gives them a chance to examine how you write code outside of an interview environment.

The next chapter steps away from writing Java code and looks at how to build nontrivial Java projects using build tools such as Maven and Ant.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.187.165