Making Java library classes better: the Groovy JDK

Một phần của tài liệu Making java groovy (Trang 98 - 101)

Every Groovy class contains a metaclass. In addition to providing information about a class, the metaclass contains methods that come into play if a method or property that doesn’t exist is accessed through an instance. By intercepting those method or prop- erty “missing” failures, developers can provide whatever they want.

One application of this is for Groovy to add methods to existing classes. This is especially useful when you want to add methods to classes where you cannot change the source code. As mentioned earlier, Groovy makes extensive use of the existing Java standard libraries. It does not, however, simply use them as it finds them. In many cases, a range of new methods has been added to the Java libraries to make them eas- ier and more powerful.

Collectively the set of enhanced Java libraries is known as the Groovy JDK. Groovy has two sets of Javadoc documentation. One is the Groovy API, which contains infor- mation about the included Groovy libraries. The other is the Groovy JDK, which shows only those methods and properties that have been added to the standard Java librar- ies, in order to, as the saying goes, make them groovier (see figure 4.4).

For example, Groovy adds many methods to the java.util.Collection interface, including collect, count, find, findAll, leftShift, max, min, sort, and sum. These methods are then available in any Groovy collection, whether they include objects from Java or Groovy.

I’ve already spent a fair amount of time on collections, though, and I’ll revisit them frequently in the book. So to choose an example from a different Java class, let’s illustrate why it’s a bad idea to use basic authentication over HTTP.

In basic authentication a username and password are transmitted in encoded form to a server. Basic authentication concatenates the username and the password

Figure 4.4 Groovy adds convenience methods to classes in the Java standard library.

together, separated by a colon, performs a Base 64 encoding on the resulting string, and sends the result as part of the authenticated HTTP request header.

There’s a big difference, however, between encoding and encrypting. Encoded strings can just as easily be decoded. Groovy makes it easy to demonstrate this, because the Groovy JDK adds a method called encodeBase64 to, of all things, byte arrays. It also adds a decodeBase64 method to String. The following listing demonstrates both.

def u = 'username' def p = 'password'

def encoded = "$u:$p".getBytes().encodeBase64().toString() println "$u:$p -> $encoded"

assert encoded == 'dXNlcm5hbWU6cGFzc3dvcmQ='

def (user,pass) = new String(encoded.decodeBase64()).split(':') println "(user,pass) = ($user,$pass)"

assert user == u assert pass == p

There’s a lot going on in this short script. First, a username and password are assem- bled into a Groovy string. Then the getBytes method is invoked on the combined string, which encodes the string into a sequence of bytes using the default character encoding. That method is from Java. The result is a byte array. Check the Groovy JDK and you’ll find that Groovy has added the method encodeBase64 to byte[], which returns an instance of groovy.lang.Writable. Here I just use its toString method (from Java, of course, though it’s overridden in the Groovy class) to see the resulting values. In effect I went from Java to Groovy to Java in one chained method call.

To go the other direction, first I use the decodeBase64 method that Groovy adds to java.lang.String, which returns a byte[] again. Then String has a constructor that takes a byte array, and I use the split method from Java to separate the username from the password again and verify that they haven’t been modified by the transformations.

Other than showing how the Groovy JDK adds new methods to standard Java data types, this example also demonstrates that encoded text isn’t encrypted. Anyone who intercepts the request and accesses the encoded header can extract the username and password. Using basic authentication therefore is not at all secure if the requests are transmitted over an unencrypted connection, like HTTP. At a minimum the request should be sent over HTTPS instead.4

There are lots and lots of useful methods in the Groovy JDK. As another example, date manipulation is always painful in Java.5 Groovy doesn’t necessarily fix the many problems, but the Groovy JDK adds several methods to make date-related classes more

Listing 4.4 Base 64 encoding and decoding username/password information

4 For several years Twitter supported basic authentication as part of its RESTful API. Hopefully all the many Twitter clients who used it transmitted their authentication over secure sockets. If not you might want to con- sider changing your password. These days Twitter has switched to OAuth, which may be overly complicated but is much better than basic authentication.

5 Java 8 is supposed to fix this, at long last. In the meantime, the open source date/time library of choice in the Java world is Joda time: http://joda-time.sourceforge.net/.

Mixing Java and Groovy methods

Reversing the encoding

73 Making Java library classes better: the Groovy JDK

powerful. Here’s an example, which hopefully will be both interesting and at least mildly amusing to some readers.

In the United States and Canada, February 2 is known as Groundhog Day. On Groundhog Day, the groundhog is supposed to emerge from his hole and look for his shadow. If he doesn’t see it he’ll stay out of the burrow, and winter is nearly over. If he sees his shadow, he goes back to sleep in his burrow, and we’ll sadly have to suffer through six more weeks of winter.

Let’s check the math on that, though, as shown in the next listing.

println 'Groundhog sees shadow --> 6 more weeks of Winter' def c = Calendar.instance

c.set 2013, Calendar.FEBRUARY, 2 def groundHogDay = c.time c.set 2013, Calendar.MARCH, 20 def firstDayOfSpring = c.time

def days = firstDayOfSpring – groundHogDay

assert days == (firstDayOfSpring..groundHogDay).size() – 1 println """

There are ${(int)(days/7)} weeks and ${days % 7} days between GroundHog Day and the first day of Spring (March 20), so Spring

comes early if the groundhog sees his shadow.

"""

I get an instance of the Calendar class by accessing its instance property. Of course, there’s no instance property in Calendar, but the syntax actually means that I invoke the static getInstance method with no arguments. Then I call set with the appropri- ate arguments for Groundhog Day and the first day of spring. Extracting a Date instance from the Calendar is done through the getTime method (sigh6), which again is invoked by accessing the time property. So far this is straight Java, except that I’m invoking methods via properties and omitting optional parentheses.

I can subtract dates, though, because the Groovy JDK shows that the minus method in Date returns the number of days between them. The Date class has a next method and a previous method and implements compareTo. Those are the requirements nec- essary for a class to be used as part of a range, so I can check the math by invoking the size method on a range. The size of a range counts both ends, so I have to correct for the potential off-by-one error by subtracting one.

The bottom line is that there are six weeks and four days between Groundhog Day and the first day of spring (March 20). In other words, if the groundhog sees his shadow the resulting six more weeks of winter is actually a (slightly) early spring anyway.7

One last convenience should be noted here. In Java, arrays have a length prop- erty, strings have a length method, collections have a size method, NodeLists have

Listing 4.5 GroundHog Day—an example of Date and Calendar in the Groovy JDK

6 Seriously, couldn’t the method getDate have been used to extract a Date from a Calendar?

7 Yes, that’s a long way to go for a gag, but it does clearly show a mix of Java and Groovy that takes advantage of both Groovy JDK methods and operator overloading. The joke is just a side benefit.

Invokes getTime to return a Date

Set method

from Java Subtracting

dates

Dates as a range

a getLength method, and so on. In Groovy you can invoke size on all of them to get the proper behavior. In this case the Groovy JDK has been used to correct a historical inconsistency in Java.

The Groovy JDK is full of helpful methods. Even if your application is planning to use only Java library classes I encourage you to check the Groovy JDK for possible sim- plifications and enhancements.

I mentioned runtime metaprogramming, which is done through the metaclass. One of the more interesting features of Groovy, though, is compile-time metaprogramming done through AST transformations, which is the subject of the next section.

Một phần của tài liệu Making java groovy (Trang 98 - 101)

Tải bản đầy đủ (PDF)

(369 trang)