Saturday, February 28, 2009

Interoperability Between Java and Groovy

This week I continued to learn a bit more about Groovy and the value it can add to an enterprise Java application. In the past, one of the first questions I often pondered when thinking about the use of dynamic languages within the JVM was how feasible it would be to make calls from Java to the dynamic language and back again. Since as a general rule everything is an object in a dynamic language, I wondered in how many cases would you be forced to invoke methods from Java where the return type would simply be a generic ‘Object’ type. This week I focused my attention on how this seems to work in Groovy. I’m happy to report that thus far the answer is quite well!

In fact it works so well that aside from a different compiler (groovyc instead of javac), a consumer of a groovy class wouldn’t know the class is a groovy class unless they open up the source code. I’m thrilled to see how seamless this appears to be (at least in the simple cases I’ve implemented thus far). To illustrate what I found when I dug into this just a bit I created three simple domain classes. The first two, ‘Customer’ and ‘PhoneNumber’ I coded in Java. The third, ‘Address’ I coded in Groovy. In this very simple example, a customer can have zero or one address (customer has a simple getter and setter for the address field) and an address can have zero or one phone number associated with it. To illustrate what it looks like to work with java and groovy together, I created a simple test harness class which instantiates a new Customer and sets all of it’s properties (both java and groovy) by using standard java beans setter syntax.

First the Customer Java class (note the Address reference which is a groovy class):

   1: public class Customer {
   2:     private String firstName;
   3:     private String lastName;
   4:     private Address address;
   5:  
   6:     public Address getAddress() {
   7:         return address;
   8:     }
   9:  
  10:     public void setAddress(Address address) {
  11:         this.address = address;
  12:     }
  13:  
  14:     public String getFirstName() {
  15:         return firstName;
  16:     }
  17:  
  18:     public void setFirstName(String firstName) {
  19:         this.firstName = firstName;
  20:     }
  21:  
  22:     public String getLastName() {
  23:         return lastName;
  24:     }
  25:  
  26:     public void setLastName(String lastName) {
  27:         this.lastName = lastName;
  28:     }
  29:  
  30:     /**
  31:      * This method returns a fully formatted name for this customer.
  32:      * @return - a string of the format 'firstName lastName'
  33:      */
  34:     public String returnFullName(){
  35:         if ("".equals(this.getFirstName())  this.getFirstName() == null)
  36:             return this.getLastName();
  37:  
  38:         if ("".equals(this.getLastName())  this.getLastName() == null)
  39:             return this.getFirstName();
  40:         
  41:         return this.getFirstName() + " " + this.getLastName();
  42:     }
  43:  
  44: }

Here is a simple Address Groovy class (note the PhoneNumber reference which is a Java class):

   1: class Address {
   2:     String addressLine1
   3:     String addressLine2
   4:     String city
   5:     String State
   6:     String postalCode
   7:     PhoneNumber phone
   8:  
   9:     /**
  10:     * Returns the address formated as a single line string.  If line1, city, state, or postal code are null,
  11:     * the entire string will return null
  12:     **/
  13:     def String formattedAddress() {
  14:         if (addressLine1 == null  addressLine1 == "" 
  15:             city == null  city == ""  state == null  state == "" 
  16:             postalCode == null  postalCode == "")
  17:             return null;
  18:  
  19:         if (addressLine2 != null && addressLine2 != "")
  20:             return addressLine1 + " " + addressLine2 + " " + city + ", " + state + " " + postalCode
  21:         else
  22:             return addressLine1 + " " + city + ", " + state + " " + postalCode
  23:     }
  24: }

Finally, Here is a test harness that shows how you would consume both classes:

   1: //customer is a java class
   2: Customer customer = new Customer();
   3: Address address = new Address();
   4: customer.setFirstName("Travis");
   5: customer.setLastName("Walters");
   6:  
   7: //phone number is also a java class
   8: PhoneNumber phone = new PhoneNumber();
   9: phone.setAreaCode("111");
  10: phone.setLocalLeft("222");
  11: phone.setLocalRight("3333");
  12:  
  13: //address is a groovy class
  14: address.setAddressLine1("101 Test Address Way");
  15: address.setAddressLine2("Suite 2001");
  16: address.setCity("Richmond");
  17: address.setState("Virginia");
  18: address.setPostalCode("12345");
  19:  
  20: //consuming a java class from a groovy class
  21: address.setPhone(phone);
  22:  
  23: //consuming a groovy class from a java class
  24: customer.setAddress(address);

I left out the unit tests and supporting files from this post in the interest of keeping the posting brief (you all write unit tests right?). Actually, I still run into engineering teams who don’t write automated tests of any fashion more often than I would like. I’m planning to write up an intro to basic unit testing for my next post and I’ll use the simple unit tests I wrote as part of this exercise as examples for that post. If you’re new to automated unit testing check it out.

1 comment: