Thứ Bảy, 14 tháng 5, 2011

Groovy 1.8 Transformations: @ToString, @EqualsAndHashCode, and @TupleConstructor

Groovy 1.8 introduces many new features including several new useful transformations. I discussed one of these transformations, @Log, in a previous blog post. In this post, I look at three other transformations: @ToString, @EqualsAndHashCode, and @TupleConstructor.

It is often very important in Java (and in Groovy) to correctly implement toString(), equals(Object), and hashCode() methods on Java (and Groovy) classes. Correct implementation of these methods is so important, in fact, that Josh Bloch covers their implementation in detail in Chapter 3 ("Methods Common to All Objects") of Effective Java Second Edition with Item 7 focused on equals(Object), Item 8 focused on hashCode(), and Item 9 focused on toString(). I have blogged previously about the importance of these methods in posts such as Java toString() Considerations and Basic Java hashCode and equals Demonstrations.

The @ToString annotation can be applied to a Groovy class to ensure that a default toString implementation is provided. This is demonstrated in the next code listing.

PersonToString.groovy
@groovy.transform.ToString
class PersonToString
{
String lastName
String firstName
}

As the above Groovy example demonstrates, the Groovy class remains minimalistic in size. This is thanks to Groovy's automatic support for properties and default named argument constructors. By adding one simple annotation, we also now get a useful toString method.

"Data objects" are often used in comparisons and as keys. This means it is important to properly implement the equals(Object) and hashCode() methods for such classes. Groovy 1.8 adds an annotation (@EqualsAndHashCode) to specify a transformation that makes this simple. It is demonstrated in the next code listing.

PersonEqualsAndHashCode.groovy
@groovy.transform.EqualsAndHashCode
class PersonEqualsHashCode
{
String lastName
String firstName
}

Like Project Lombock, Groovy uses a single annotation to represent construction of two methods. The reasoning for this is that equals and hashCode should be implemented consistently.

The previous two code listings have shown Groovy classes that might be instantiated with a constructor using named arguments. This is supported out of the box by Groovy by using a map of name/value pairs for the class's attributes. Groovy 1.8 adds a new annotation, @TupleConstructor, that supports a constructor accepting ordered parameters.

PersonTupleConstructor.groovy
@groovy.transform.TupleConstructor
class PersonTupleConstructor
{
String lastName
String firstName
}

The three transformations covered in this post (@ToString, @EqualsAndHashCode, and @TupleConstructor) can be used in the same class as shown in the next code listing.

TheWholePerson.groovy
@groovy.transform.TupleConstructor
@groovy.transform.EqualsAndHashCode
@groovy.transform.ToString(includeNames = true, includeFields=true)
class TheWholePerson
{
String lastName
String firstName
}

The next Groovy code listing is for a script that makes use of the four Groovy classes shown above.

demonstrateCommonMethodsAnnotations.groovy
#!/usr/bin/env groovy
// demonstrateCommonMethodsAnnotations.groovy

def person = new Person(lastName: 'Rubble', firstName: 'Barney')
def person2 = new Person(lastName: 'Rubble', firstName: 'Barney')
def personToString = new PersonToString(lastName: 'Rockford', firstName: 'Jim')
def personEqualsHashCode = new PersonEqualsHashCode(lastName: 'White', firstName: 'Barry')
def personEqualsHashCode2 = new PersonEqualsHashCode(lastName: 'White', firstName: 'Barry')

// Demonstrate value of @ToString
printHeader("@ToString Demonstrated")
println "Person with no special transformations: ${person}"
println "Person with @ToString transformation: ${personToString}"

// Demonstrate value of @EqualsAndHashCode
printHeader("@EqualsAndHashCode Demonstrated")
println "${person} ${person == person2 ? 'IS' : 'is NOT'} same as ${person2}."
println "${personEqualsHashCode} ${personEqualsHashCode == personEqualsHashCode2 ? 'IS' : 'is NOT'} same as ${personEqualsHashCode2}."

// Demonstrate value of @TupleConstructor
printHeader("@TupleConstructor Demonstrated")
def personTupleConstructor = new PersonTupleConstructor('Whyte', 'Willard')
println "Tuple Constructor #1: ${personTupleConstructor.firstName} ${personTupleConstructor.lastName}"
def personTupleConstructor2 = new PersonTupleConstructor('Prince') // first name will be null
println "Tuple Constructor #2: ${personTupleConstructor2.firstName} ${personTupleConstructor.lastName}"

// Combine all of it!
printHeader("Bringing It All Together")
def wholePerson1 = new TheWholePerson('Blofeld', 'Ernst')
def wholePerson2 = new TheWholePerson('Blofeld', 'Ernst')
println "${wholePerson1} ${wholePerson1 == wholePerson2 ? 'IS' : 'is NOT'} same as ${wholePerson2}."


/**
* Print a header using provided String as header title.
*
* @param headerText Text to be included in header.
*/
def printHeader(String headerText)
{
println "\n${'='.multiply(75)}"
println "= ${headerText}"
println "=".multiply(75)
}

When the above script is executed the output appears as shown in the next screen snapshot.



Conclusion

There are numerous tools in Javadom (such as Apache Commons and Pojomatic) for implementing common methods such as toString, equals, and hashCode. An alternative approach of using annotations to perform similar functionality has become increasingly popular via products such as Project Lombock. For Groovy developers, Groovy 1.8 now provides this latter capability as a built-in part of the language.

Thứ Sáu, 13 tháng 5, 2011

Personal Lessons Learned from The Blogger Outage

For nearly 24 hours, Blogger was in read-only mode. This period, combined with the advertised lock out for the maintenance that led to the corrupted data problem, has been frustrating for me as well as for numerous other Blogger users. In this post, I look at some lessons learned or observations made as a result of this experience.


It's Easy To Take Things for Granted

I've been blogging via Blogger since late 2007. Although this lock-out from Blogger for nearly a full day was frustrating, it reminded me of how well Blogger typically runs. I had become accustomed to few, very short outages before this and really took Blogger availability for granted. We all would like the products we deliver to customers to have zero downtime and 100% availability, but this was a reminder to me that many customers (including myself) can tolerate a one-time glitch if there is a proven track record of high availability. Of course, it's still best to avoid these outages as much as possible and to have them be as short as possible when they do occur. However, we have more opportunity to keep customers loyal during longer-than-anticipated outages if they have had longer periods of high availability most of the time.


It's Difficult to Complain about Free Service

I don't make a dime off of this blog and I don't have any advertisements. I enjoy writing the blog and gain non-monetary benefits from writing it. One of the luxuries of this is that it is easy to tell any unsatisfied reader that he or she at least got their money's worth. The Blogger staff has done nothing like this to my knowledge, but the hard truth is that many of us get typical high availability from Blogger without paying a dime for that service. Given Blogger's Service Issue today - Would you Pay for Service? is a Blogger forum thread that includes discussion about whether users would be willing to pay for Blogger use in an effort to get better support (including telephone or direct support beyond the general forum support).

I liked whoffkne's forum comment: "Yeah, I mean, seriously, what kind of service do we pay for...oh...yeah, right. =)" That summarizes it pretty well.


It's My Readers I'm Worried About!

In the Blogger "Something is Broken" forum thread Blogger Service Issues, ronniesaunders states, "I have a lot readers who depend on my blog as their primary source of news and information" and implied that this came at a particularly frustrating time: "A military compound in Pakistan was just bombed and 80 people have been injured and my blogger service is down."

My thoughts were along a similar line as ronniesaunders's, but to a lesser degree and about less important news. I wasn't so much worried about letting down my two readers, but I did want to publish mention of the just-announced JavaOne 2011 session submission tips while the news was still hot. At least with the read-only mode, I slept well knowing that my loyal readers could still read about interesting posts as of May 10. I had not realized how much I enjoyed being able to write a quick post about a new event as I learned about it and how frustrating having to wait another day to do so could be.


Maybe It's Time to Start Being Superstitious?

On the previously mentioned Blogger forum post Blogger Service Issues, themusesguild brings up something I had not previously considered: "Hey Guys,Three Simple Words...Friday The 13th."


Any News is Good News

One of the most difficult things about this issue was not being aware of when service might be restored, how complex the underlying problem was, and not knowing the fate of posts and comments published in the vulnerable period between the problems and the application of the read-only setting. This uncomfortable feeling of not knowing when service would be restored or what, if anything, bloggers should start doing is expressed in the Blogger forum thread Recent Blogger service issues. JMan360's comment likely articulates what others were thinking: "They really need to tell us more. I don't know whether to start redoing everything or not. I'll be irritated either way. Why don't they tell us more details?"

In another Blogger: Something is Broken thread, poingly asked, "Why do you refuse to give an ETA for restoration of Blogger service?" The discussion that followed included covering the challenges associated with coming up with such estimates and the distractions that coming up with such estimates can cause. On the one hand, users want an estimate. On the other hand, users don't necessarily want the ultimate solution put off even longer in order to provide the estimate and users especially don't want to be given repeatedly changing estimates. The lesson that might be learned here is that a best guess estimate might be provided with a whole bunch of associated caveats.


It Could Happen to Me!

Many of the bloggers who use Blogger are not developers and will never be in a situation where they are responsible for creating, delivering, or maintaining a software product used by numerous users. They don't have to be concerned with issues like availability, redundancy, and scalability. For we developers, however, this is a reminder that we do need to care about such issues. With heavyweights such as Amazon and Google experiencing these down times recently, it's a reminder that it can happen to any of us. We have to do our best to avoid it and need to be prepared to deal with the unexpected when even our best preventative efforts fail.


Backup!

No matter who is storing your data "on the other end," it is important to store one's own data in a backup as well. This event was a reminder to me that I had not exported my blog via Blogger's export mechanism for some time. I'll be doing that this coming weekend.


Conclusion

Personally, I'm not ready to abandon Blogger over this single issue. It was frustrating at times, but I also was reminded of how good the service has normally been and appreciate it more now. I definitely hope not to see something similar for some time and believe that nothing has been lost.

Thứ Ba, 10 tháng 5, 2011

Software Development Posts of Interest - 10 May 2011

In this post, I reference and briefly summarize some of the posts I have found worth a closer look in recent days and weeks. Topics covered by these posts include professional software development, Groovy, Java, and Scala.


Software Development is Not a Stressful Job

In the post Overworked? Make a Leap to America's 10 Least Stressful Jobs, Erica Ho reports on a CareerCast survey that found Software Engineer to be the third least stressful job and Computer Programmer to be the fourth least stressful job in the United States. The comments are particularly interesting both on the original CareerCast survey coverage on these two occupations and on the Erica Ho article. At the time of this writing, there are nearly 1000 comments on reddit on this post. Many folks have chimed into to explain why the believe these positions are highly stressful.


Why Programming is a Unique Profession

Nalaka Gamage's presentation Why Programming is a Unique Profession provides an interesting perspective on why the author believes that software programming is a unique profession. Some of the statements in the presentation reiterate my own experience and some I've less certain about, but it's interesting to see the list of differences.


Groovy Posts

There are multiple recent posts that demonstrate the breadth of Groovy's usefulness for different developers with different development needs.

Java.next(): The Groovy Programming Language

In the post Java.next(): The Groovy Programming Language (not to be confused with a similarly titled post Java.next Overview from September 2008), Bozhidar Batsov provides fairly comprehensive coverage of Groovy, its history, and some of its more desirable features in a single post. Although the post itself provides interesting information, especially for someone new to Groovy, the feedback comments are perhaps what interested me most.

A comment from shaberman, a self-described static typing bigot, states, "Groovy would have won the 'Java.next' title by now if they weren't so stubborn about being a dynamic language." Stephen Haberman mentions the current state of Groovy++ in this comment.

Guy Fawkes's comment to the post compares Groovy and Scala in terms of jobs and hiring trends. He provides links to comparisons for Groovy, Grails, and Scala from sites SimplyHired and two from Indeed.com (second one).

As is typically the case when the inevitable comparisons start being made between Groovy and Scala, Pere Villega points out the now well-known post Scala as the Replacement to the Java Compiler in which Groovy creator James Strachan stated, "I can honestly say if someone had shown me the Programming Scala book by by Martin Odersky, Lex Spoon & Bill Venners back in 2003 I'd probably have never created Groovy." I'm glad he wasn't shown that book!


Quickly Creating Test Data with Groovy

I typically use Java for most of the applications I implement, but I use Groovy for an increasing number of the tasks involved with Java development. One of the tasks that I like Groovy for is generation of test data. In his post Quickly creating test data with Groovy, Simon Fortelny writes in detail about how he uses Groovy to do this very thing. His post is concise but covers the most significant points with a simple example.


Groovy Simplifies the Java Developer's Life

Another post that articulates my feelings about how Groovy makes my Java development and maintenance easier and better is the post Making Your Life Simpler—How Groovy Can Leverage Your Existing Java Skill. Adapted from the first chapter of Kenneth Kousen's Making Java Groovy, this post focuses on how use of Groovy makes a Java developer's life easier. The author states, "We’re much more interested in helping Java than replacing it."


Scala and a Dozen Concurrency Pitfalls

Cay Horstmann captured some "Java concurrency pitfalls" and presents them in Scala in his post A Dozen Concurrency Pitfalls.


Another Final Word on Final

I'm a big fan of using Java's final keyword liberally. Jarrod Roberson has written a post called The Final Word on Final that extols the uses and virtues of the final keyword. The title of this post reminds me of Renaud Waldura's The Final Word On the final Keyword.


Conclusion

In this post, I have summarized several software development posts I have found to be of interest in recent days and weeks. The posts on Groovy, Java, Scala, and software development in general were worth the look past the headline.

Thứ Sáu, 6 tháng 5, 2011

Documenting Groovy with Groovydoc

Groovydoc was introduced in 2007 to provide for Groovy what Javadoc provides for Java. Groovydoc is used to generate the API documentation for the Groovy and Java classes that compose the Groovy language. In this post, I look at invoking Groovydoc via the command-line and via Groovy-provided custom Ant task.


Groovy and Java Source Code with Groovydoc/Javadoc Comments

I will use adapted versions of the Groovy script and classes first introduced in my blog post Easy Groovy Logger Injection and Log Guarding to demonstrate Groovydoc. The main Groovy script and the Groovy classes from that post have been modified to include more Javadoc-style comments to better demonstrate Groovydoc in action. The revised script and associated classes are shown in the next code listings.

demoGroovyLogTransformation.groovy
#!/usr/bin/env groovy
/**
* demoGroovyLogTransformation.groovy
*
* Grab SLF4J, Log4j, and Apache Commons Logging dependencies using @Grab and
* demonstrate Groovy 1.8's injected logging handles.
*
* http://marxsoftware.blogspot.com/2011/05/easy-groovy-logger-injection-and-log.html
*/

// No need to "grab" java.util.logging: it's part of the JDK!

/*
* Specifying 'slf4j-simple' rather than 'slf4j-api' to avoid the error
* "Failed to load class "org.slf4j.impl.StaticLoggerBinder" that is caused by
* specifying no or more than one of the actual logging binding libraries to
* be used (see http://www.slf4j.org/codes.html#StaticLoggerBinder). One should
* be selected from 'slf4j-nop', 'slf4j-simple', 'slf4j-log4j12.jar',
* 'slf4j-jdk14', or 'logback-classic'. An example of specifying the SLF4J
* dependency via @Grab is available at
* http://mvnrepository.com/artifact/org.slf4j/slf4j-api/1.6.1.
*/
@Grab(group='org.slf4j', module='slf4j-simple', version='1.6.1')

/*
* An example of specifying the Log4j dependency via @Grab is at
* http://mvnrepository.com/artifact/log4j/log4j/1.2.16.
*/
@Grab(group='log4j', module='log4j', version='1.2.16')

/*
* An example of specifying the Apache Commons Logging dependency via @Grab is at
* http://mvnrepository.com/artifact/commons-logging/commons-logging-api/1.1.
*/
@Grab(group='commons-logging', module='commons-logging-api', version='1.1')


/*
* Run the tests...
*/
int headerSize = 79
printHeader("java.util.logger", headerSize)
def javaUtilLogger = new JavaUtilLoggerClass()
printHeader("Log4j", headerSize)
def log4jLogger = new Log4jLoggerClass()
printHeader("SLF4j", headerSize)
def slf4jLogger = new Slf4jLoggerClass()
printHeader("Apache Commons", headerSize)
def commonsLogger = new ApacheCommonsLoggerClass()


/**
* Print header with provided text.
*
* @param textForHeader Text to be included in the header.
* @param sizeOfHeader Number of characters in each row of header.
*/
def printHeader(final String textForHeader, final int sizeOfHeader)
{
println "=".multiply(sizeOfHeader)
println "= ${textForHeader}${' '.multiply(sizeOfHeader-textForHeader.size()-4)} ="
println "=".multiply(sizeOfHeader)
}

JavaUtilLoggerClass.groovy
import groovy.util.logging.Log

/**
* Sample Groovy class using {@code @Log} to inject java.util.logging logger
* into the class.
*/
@Log
class JavaUtilLoggerClass
{
/**
* Constructor.
*/
public JavaUtilLoggerClass()
{
println "\njava.util.logging (${log.name}: ${log.class}):"
log.info "${this.printAndReturnValue(1)}"
log.finer "${this.printAndReturnValue(2)}"
}

/**
* Print provided value and then return it as part of String indicating part
* of JDK's java.util.logging.
*
* @param newValue Value to be printed and included in return String.
* @return String indicating newValue and JDK for java.util.logging.
*/
public String printAndReturnValue(int newValue)
{
println "JDK: Print method invoked for ${newValue}"
return "JDK: ${newValue}"
}
}

Log4jLoggerClass.groovy
import groovy.util.logging.Log4j

import org.apache.log4j.Level

/**
* Sample Groovy class using {@code @Log4j} to inject Log4j logger
* into the class.
*/
@Log4j
class Log4jLoggerClass
{
/**
* Constructor.
*/
Log4jLoggerClass()
{
// It is necessary to set logging level here because default is FATAL and
// we are not using a Log4j external configuration file in this example
log.setLevel(Level.INFO)
println "\nLog4j Logging (${log.name}: ${log.class}):"
log.info "${this.printAndReturnValue(1)}"
log.debug "${this.printAndReturnValue(2)}"
}

/**
* Print provided value and then return it as part of String indicating part
* of Log4j.
*
* @param newValue Value to be printed and included in return String.
* @return String indicating newValue and Log4j.
*/
public String printAndReturnValue(int newValue)
{
println "Log4j: Print method invoked for ${newValue}"
return "Log4j: ${newValue}"
}
}

Slf4jLoggerClass.groovy
import groovy.util.logging.Slf4j

/**
* Sample Groovy class using {@code @Slf4j} to inject Simple Logging Facade for
* Java (SLF4J) logger into the class.
*/
@Slf4j
class Slf4jLoggerClass
{
/**
* Constructor.
*/
public Slf4jLoggerClass()
{
println "\nSLF4J Logging (${log.name}: ${log.class}):"
log.info "${this.printAndReturnValue(1)}"
log.debug "${this.printAndReturnValue(2)}"
}

/**
* Print provided value and then return it as part of String indicating part
* of SLF4J logging.
*
* @param newValue Value to be printed and included in return String.
* @return String indicating newValue and SLF4J.
*/
public String printAndReturnValue(int newValue)
{
println "SLF4J: Print method invoked for ${newValue}"
return "SLF4J: ${newValue}"
}
}

ApacheCommonsLoggerClass.groovy
import groovy.util.logging.Commons

/**
* Sample Groovy class using {@code @Commons} to inject Apache Commons logger
* into the class.
*/
@Commons
class ApacheCommonsLoggerClass
{
/**
* Constructor.
*/
public ApacheCommonsLoggerClass()
{
println "\nApache Commons Logging (${log.name}: ${log.class}):"
log.info "${this.printAndReturnValue(1)}"
log.debug "${this.printAndReturnValue(2)}"
}

/**
* Print provided value and then return it as part of String indicating part
* of Apache Commons Logging.
*
* @param newValue Value to be printed and included in return String.
* @return String indicating newValue and Apache Commons Logging.
*/
public String printAndReturnValue(int newValue)
{
println "Commons: Print method invoked for ${newValue}"
return "Commons: ${newValue}"
}
}

In addition to the above Groovy script and classes, I also use a new Java class here to illustrate that Groovydoc works on Java classes as well as Groovy classes. The Java class doesn't do much besides providing the Javadoc comments to be processed by Groovydoc.

DoNothingClass.java
/**
* Class that does not do anything, but is here to be a Java class run through
* groovydoc.
*/
public class DoNothingClass
{
/**
* Simple method that returns literal "Hello _addressee_!" string where
* _addressee_ is the name provided to this method.
*
* @param addressee Name for returned salutation to be addressed to.
* @return "Hello!"
*/
public String sayHello(final String addressee)
{
return "Hello, " + addressee;
}

/**
* Main executable function.
*/
public static void main(final String[] arguments)
{
final DoNothingClass me = new DoNothingClass();
me.sayHello("Dustin");
}

/**
* Provide String representation of this object.
*
* @return String representation of me.
*/
@Override
public String toString()
{
return "Hello!";
}
}


Running Groovydoc on the Command Line

With the Groovy script, Groovy classes, and Java class shown above ready to go, it is time to turn attention to running Groovydoc against these classes and script. As is the case with Javadoc, Groovydoc can be run from the command line. The command for running Groovydoc against the above classes and scripts (assuming they are all in the same directory in which the command is run) looks something like this:

groovydoc -classpath C:\groovy-1.8.0\lib\ -d output -windowtitle "Groovy 1.8 Logging Example" -header "Groovy 1.8 Logging (Inspired by Actual Events)" -footer "Inspired by Actual Events: Logging in Groovy 1.8" -doctitle "Logging in Groovy 1.8 Demonstrated"  *.groovy *.java

The above command is all run on one line. However, for improved readability, I have added line breaks to break the command down.

groovydoc -classpath C:\groovy-1.8.0\lib\
-d output
-windowtitle "Groovy 1.8 Logging Example"
-header "Groovy 1.8 Logging (Inspired by Actual Events)"
-footer "Inspired by Actual Events: Logging in Groovy 1.8"
-doctitle "Logging in Groovy 1.8 Demonstrated"
*.groovy *.java

The groovydoc command's parameters look familiar to anyone who has used javadoc from the command line. The last part of the command specifies that groovydoc should be run against Groovy and Java code.


Running Groovydoc from Ant

Groovydoc can also be easily accessed via a custom Ant task as described in the Groovy User Guide. It is fairly easy to apply the groovydoc Ant task by first setting up the appropriate taskdef and then by using that defined tag. This is demonstrated in the following XML snippet from a relevant build.xml file.

Portions of an Ant build.xml File Demonstrating groovydoc Task
 <taskdef name="groovydoc"
classname="org.codehaus.groovy.ant.Groovydoc"
classpathref="groovydocpath" />

<target name="generategroovydoc" description="Generate Groovydoc-based documentation">
<mkdir dir="${groovydoc.dir}" />
<groovydoc destdir="${groovydoc.dir}"
sourcepath="."
packagenames="**.*"
use="true"
windowtitle="Groovy 1.8 Logging Example"
doctitle="Logging in Groovy 1.8 Demonstrated"
header="Groovy 1.8 Logging (Inspired by Actual Events)"
footer="Inspired by Actual Events: Logging in Groovy 1.8"
overview="override.html"
private="false">
<link packages="java.,org.xml.,javax.,org.xml." href="http://download.oracle.com/javase/6/docs/api"/>
<link packages="groovy.,org.codehaus.groovy." href="http://groovy.codehaus.org/api/"/>
</groovydoc>
</target>

The portion of the Ant build.xml shown above is roughly equivalent to that used on the command line. Having Groovydoc available via Ant is important because it makes it easier to integrate building of Groovy documentation from Ant-based build systems.


Groovydoc Generated Documentation

Because each approach to generating Groovy documentation via Groovydoc (command line or Ant-based) works about the same as the other, I'll now focus on the HTML output that could come from either approach. The next series of screen snapshots shows the generated documentation starting with the main page, followed by the DefaultPackage page (I lazily left the script, Groovy classes, and Java class in the current directory and without any package declaration), followed respectively by the output for the Groovy script, for an example Groovy class, and for the contrived Java class. The last three images help to differentiate between the output for a Groovy Script versus a Groovy class versus a Java class.

Groovydoc Main Page Example

Groovydoc Output for Example Package (DefaultPackage)

Groovydoc Output for Example Groovy Script

Groovydoc Output for Example Groovy Class

Groovydoc Output for Example Java Class

Several observations can be made from the Groovydoc output shown above. First, the generated documentation for the Groovy script only documented the methods defined in the script (including the implicit main method). What is not so obvious from the static images above is that, in fact, no Groovydoc output is created for a script at all unless at least one method is explicitly defined in the script. If one method is defined in the script, then Groovydoc output is generated for any defined methods and for the implicit main method. The option -nomainforscripts can be passed to Groovydoc to have no Groovydoc generated for the implicit main method. The output of adding this option is shown next (note that the main's documentation is no longer displayed).


The -nommainforscripts option is nice because we often don't want the main function to be implicitly documented for our scripts. Indeed, the main function is typically "hidden" from us as script writers and maintainers.

A second observation from looking at Groovydoc-generated output is that the generated output distinguishes between Groovy and Java source code. Groovy scripts and classes are labeled with "[Groovy]" and Java classes are labeled with "[Java]." This is also evident in the Groovydoc-generated Groovy API documentation where this features makes it easy to identify that groovy.sql.Sql and AntBuilder are Java classes while JmxBuilder and SwingBuilder are Groovy classes.

A third observation is the numerous methods that Groovy script's automatically inherit from groovy.lang.Script and from groovy.lang.GroovyObjectSupport. These are displayed in the next screen snapshot taken directly from Groovydoc's output for the script shown above.



Conclusion

I don't find myself commenting Groovy scripts with Javadoc/Groovydoc style comments as often as I might comment a Groovy or Java class, but it is nice to be able to do so in some cases. I frequently use the various API documentation sets for Groovy and appreciate their availability. It is nice to be able to easily generate documentation for Groovy in a very similar manner to that used for Java.

Thứ Tư, 4 tháng 5, 2011

Oracle Proposes Hudson Becoming an Eclipse Foundation Project

In the recent highly opinionated post ("Ellison's treatment of open source since the Sun acquisition has been nothing short of reprehensible, and it made me really mad.") Oracle's fight with open source, the good guys won -- this time, Bill Snyder wrote that the Hudson/Jenkins fork is another example of "tension between [Oracle] and the open source community."

Today's major announcement, of course, is that Oracle is proposing transfer of Hudson to the Eclipse Foundation. There are several observations and questions that naturally arise from this announcement.

One of the things I first wondered when reading about this was if Hudson and Jenkins might merge back together. Paul Krill's article Oracle hands Hudson to Eclipse, but Jenkins fork seems permanent seems to answer, "Probably not," and backs that answer up with quotes from pertinent individuals.

If Hudson and Jenkins are not to reunite, then which one is more likely to enjoy long-term success? Hudson has name recognition, potential Eclipse community support, and major vendors behind it. Jenkins enjoys the support of the majority of the committers to Hudson, including its creator. Jenkins appeals to community members who want to support (or just use) freely available open source products, but Hudson will soon have the same advantage if it is part of the Eclipse Foundation. It sounds like Hudson will have an Apache 2 or Eclipse license, both of which are "friendly" to corporate users.

If Hudson and Jenkins continue as separate projects, there are advantages and disadvantages for the rest of us. The obvious disadvantage is the loss of additional testing and community involvement for either project due to the existence of the other. The obvious advantage is that competition is often good for the consumer and should help drive innovation. We have seen this effect between Java and C# and between the Eclipse IDE and NetBeans. Although the communities may be split, they can still borrow from each others' ideas.

One observation/question I have is if Oracle's handling of OpenOffice and Hudson are indicative of a pattern. In both cases, Oracle proposed donation of the product to an independent open source repository/provider after a fork occurred. It was LibreOffice in the case of OpenOffice and Jenkins in the case of Hudson.

The final question that won't be answered for some time to come is the long-term success and viability of these donated products and their forked alternatives. My belief is that both Hudson and Jenkins are likely to enjoy some success (though each product's success will be less than it would be if they were one), but I'm less certain about the future of OpenOffice/LibreOffice.

Thứ Ba, 3 tháng 5, 2011

Easy Groovy Logger Injection and Log Guarding

Groovy 1.8 was recently released with a long list of new features. I focus on one of these new features, logger injection via new AST Transformations, in this post.

Groovy 1.8 provides AST Transformations for annotation-based logged injection for four of the most popular Java logging frameworks: the standard java.util.logging, Log4j, Apache Commons Logging, and Simple Logging Facade for Java (SLF4J). The annotation is different for each of these logging frameworks, but the effect is the same: each injects the respective logger into the annotated class accessible by a "log" variable name handle. Each AST does more than simply inject the logger; each also automatically wraps all calls to logging methods with checks for the logging level being set sufficiently to log at that level.

The @Log annotation is used to inject a java.util.logging logger. Its Groovydoc states the following regarding use of @Log to inject the Log4j logger:
The method name will be used to determine what to call on the logger.

log.name(exp)

is mapped to
if (log.isLoggable(Level.NAME) {
log.name(exp)
}
Here name is a place holder for info, fine, finer, finest, config, warning, severe.

As the Groovydoc documentation for @Log indicates, using code like log.info(expression) is mapped to a conditional logging guard call before actually invoking that named method (info in this case) on the logger.

Just as the @Log annotation injects a java.util.logging logger, there are different annotations that do the same thing for the other three previously mentioned logging frameworks. A Log4j logger is injected with the @Log4j annotation, an Apache Commons Logging logger is injected with the @Commons annotation, and an SLF4J logger is injected with the @Slf4j annotation.

These logger annotations are easily demonstrated in the following Groovy code snippets. I first list four simple Groovy classes, one for each of the four logging frameworks for which Groovy 1.8 provides logger transformations.

JavaUtilLoggerClass.groovy - Demonstrates @Log for java.util.logging
import groovy.util.logging.Log

@Log
class JavaUtilLoggerClass
{
public JavaUtilLoggerClass()
{
println "\njava.util.logging (${log.name}: ${log.class}):"
log.info "${this.printAndReturnValue(1)}"
log.finer "${this.printAndReturnValue(2)}"
}

public String printAndReturnValue(int newValue)
{
println "JDK: Print method invoked for ${newValue}"
return "JDK: ${newValue}"
}
}

Log4jLoggerClass.groovy - Demonstrates @Log4j for Log4j
import groovy.util.logging.Log4j

import org.apache.log4j.Level

@Log4j
class Log4jLoggerClass
{
Log4jLoggerClass()
{
// It is necessary to set logging level here because default is FATAL and
// we are not using a Log4j external configuration file in this example
log.setLevel(Level.INFO)
println "\nLog4j Logging (${log.name}: ${log.class}):"
log.info "${this.printAndReturnValue(1)}"
log.debug "${this.printAndReturnValue(2)}"
}

public String printAndReturnValue(int newValue)
{
println "Log4j: Print method invoked for ${newValue}"
return "Log4j: ${newValue}"
}
}

ApacheCommonsLoggerClass.groovy - Demonstrates @Commons for Commons Logging
import groovy.util.logging.Commons

@Commons
class ApacheCommonsLoggerClass
{
public ApacheCommonsLoggerClass()
{
println "\nApache Commons Logging (${log.name}: ${log.class}):"
log.info "${this.printAndReturnValue(1)}"
log.debug "${this.printAndReturnValue(2)}"
}

public String printAndReturnValue(int newValue)
{
println "Commons: Print method invoked for ${newValue}"
return "Commons: ${newValue}"
}
}

Slf4jLoggerClass.groovy - Demonstrates @Slf4j for SLF4J Logging
import groovy.util.logging.Slf4j

@Slf4j
class Slf4jLoggerClass
{
public Slf4jLoggerClass()
{
println "\nSLF4J Logging (${log.name}: ${log.class}):"
log.info "${this.printAndReturnValue(1)}"
log.debug "${this.printAndReturnValue(2)}"
}

public String printAndReturnValue(int newValue)
{
println "SLF4J: Print method invoked for ${newValue}"
return "SLF4J: ${newValue}"
}
}

The above four Groovy classes are very similar, but each one takes advantage of a different Groovy 1.8 logger injection. The next Groovy code listing shows a script that makes use of these classes and demonstrates the workings of the respective injected loggers.

demoGroovyLogTransformation.groovy
#!/usr/bin/env groovy
// demoGroovyLogTransformation.groovy

//
// Grab SLF4J, Log4j, and Apache Commons Logging dependencies using @Grab
//

// No need to "grab" java.util.logging: it's part of the JDK!

/*
* Specifying 'slf4j-simple' rather than 'slf4j-api' to avoid the error
* "Failed to load class "org.slf4j.impl.StaticLoggerBinder" that is caused by
* specifying no or more than one of the actual logging binding libraries to
* be used (see http://www.slf4j.org/codes.html#StaticLoggerBinder). One should
* be selected from 'slf4j-nop', 'slf4j-simple', 'slf4j-log4j12.jar',
* 'slf4j-jdk14', or 'logback-classic'. An example of specifying the SLF4J
* dependency via @Grab is available at
* http://mvnrepository.com/artifact/org.slf4j/slf4j-api/1.6.1.
*/
@Grab(group='org.slf4j', module='slf4j-simple', version='1.6.1')

/*
* An example of specifying the Log4j dependency via @Grab is at
* http://mvnrepository.com/artifact/log4j/log4j/1.2.16.
*/
@Grab(group='log4j', module='log4j', version='1.2.16')

/*
* An example of specifying the Apache Commons Logging dependency via @Grab is at
* http://mvnrepository.com/artifact/commons-logging/commons-logging-api/1.1.
*/
@Grab(group='commons-logging', module='commons-logging-api', version='1.1')


//
// Run the tests...
//

def javaUtilLogger = new JavaUtilLoggerClass()
def log4jLogger = new Log4jLoggerClass()
def slf4jLogger = new Slf4jLoggerClass()
def commonsLogger = new ApacheCommonsLoggerClass()

Most of the above script is comments. The script takes advantage of Groovy's built-in Grape/@Grab support to find its dependencies on the various logging frameworks. There is no need to explicitly "grab" available-by-default java.util.logging, but @Grab is used to specify dependencies on the other three logging frameworks. Groovy 1.8 introduces more concise syntax for @Grab, but I used the pre-1.8 syntax in this example. As the comments in the script indicate, I used Maven repository settings for declaring these dependencies on Log4j, Apache Commons Logging, and SLF4J (Simple Binding).

The output from running the above script that instantiates the four Groovy classes is shown next.

java.util.logging (JavaUtilLoggerClass: class java.util.logging.Logger):
JDK: Print method invoked for 1
May 2, 2011 9:42:23 PM org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSiteNoUnwrap invoke
INFO: JDK: 1

Log4j Logging (Log4jLoggerClass: class org.apache.log4j.Logger):
Log4j: Print method invoked for 1
INFO - Log4j: 1

SLF4J Logging (Slf4jLoggerClass: class org.slf4j.impl.SimpleLogger):
SLF4J: Print method invoked for 1
19 [main] INFO Slf4jLoggerClass - SLF4J: 1

Apache Commons Logging (ApacheCommonsLoggerClass: class org.apache.commons.logging.impl.Jdk14Logger):
Commons: Print method invoked for 1
May 2, 2011 9:42:23 PM org.apache.commons.logging.Log$info call
INFO: Commons: 1

The output above proves that the injected loggers do indeed prevent the classes from even being instantiated when the level of logging does not meet the log level of the application.


Conclusion

This post's simple example demonstrates that the Groovy 1.8 logger AST Transformations can inject loggers from the four major Java logging frameworks and map logging calls to code that checks the log level before trying to log only if appropriate. This greatly reduces the amount of code necessary to set up and use logging for any of these four popular logging frameworks.

Thứ Hai, 2 tháng 5, 2011

Can the Java Applet Be Salvaged?

In the October 1996 article What is Java, Really?, Rudi Cilibrasi wrote the following in his introductory overview of Java:
Most people view Java as the programming language of the Web. Though Java certainly serves well in this regard, it is actually much more general; it is appropriate for stand-alone, non-Web programming as well.

The above quotation seems funny today, but it was a relatively accurate representation of the perception of Java at that time. Many people did see Java as "the programming language of the Web" and many people did not realize that it was even possible to use Java outside of a web browser. In other words, Java Applets were well known and Java applications were not so well known.

In an early 1996 JavaWorld article called Introduction to Java (in plain English) (originally called "Waiter! What is this Java doing in my soup?"), Frank D. Greco wrote "...the way cool Java programming language is muscling its way onto the Internet with powers beyond those of mortal computer languages" and added that Java was, "flying on the vapor trails of the booming World Wide Web (WWW) service across the ubiquitous Internet." Greco's article talks about using Java for applications and for applets, but it is obvious that applets were still a huge part of the Java discussion at this time.

As a largely unrelated but interesting side note, Greco also wrote:
Since the Java runtime system is actually independent from the Java source language that a programmer would create, other languages and other tools could potentially generate the machine-independent Java bytecodes. There's nothing to stop someone from creating a version of BASIC that generates Java bytecodes; and it would be 100 percent compatible with bytecodes generated by the standard Java translator.
Although I don't know of any flavor of Basic running on the JVM, just about every other imaginable language seems to be running on the JVM these days in addition to entirely new languages written for the JVM. Fifteen years ago, applets were everything and alternative languages on the JVM were mostly conceptual.

By the time I published my article An Overview of Web Technologies for Oracle-Driven Web Sites in the Summer 2002 edition of the RMOUG newsletter, things were already looking bad for the once mighty Java applet. I wrote the following in that article:
While Java applets are no longer as popular as they used to be, I still think of them fondly since they introduced me and many others to Java and to the idea of more elegant solutions than CGI for web development.
By 2002, it already looked like applets were on their way to "has been" status.

"Applets are back!" was the beginning line of Next Generation in Applet Java Plug-in Technology, published in June 2008 in conjunction with the release of Java SE 6 Update 10. This "next generation Java Plug-in" was advertised with features addressing many of the common complaints about applets. These advertised features included "improved reliability," "improved user experience," and improved integration with JavaScript (another historical side note is the reversal of roles between Java and JavaScript when it comes to the web).

The driving force behind the renewed applet in Java SE 6 Update 10 was JavaFX. Sun's (and more recently Oracle's) enthusiasm regarding JavaFX has not seemed to be matched by the general Java development community's enthusiasm. Indeed, there seems to be a chasm between Java developers regarding JavaFX with the majority of developers seemingly against it, but a smaller group enthusiastically supporting it as a competitor in the RIA space. I don't know if it is the next generation Java plug-in's association with JavaFX or the fact that it received too little attention too late or both of these factors, but the applet still has not seemed to regain any lost ground.

Firefox 3.6 required Java SE 6 Update 10 to support the next generation applet. Unfortunately, the browser news related to the applet has not continued to help. Indeed, the latest blow to the applet came on a highly commented reddit post that pointed out the Google Chrome web browser now blocks the Java applet plug-in and adds insult to injury by calling it a plug-in that is "not widely used." I'd like to argue with that, but I just can't.

The Future for Applets?

Jeff Friesen wrote a JavaWorld article called "Are applets making a comeback?" in May 2008 (about the same time as the release of the next generation Java Plug-in in conjunction with Java SE 6 Update 10). He stated the following in that article:
These days applets have largely been forgotten by Java developers working in the RIA (rich Internet application) space. ... Talk to Sun engineers working on Sun's consumer and client-side initiatives, though, and ... they'll tell you that news of the demise of applets is premature. ... The question for many of us is whether it's just too late for applets. ... The problem is that most Java developers have already given up on Java on the client side, and bought into the promise of slicker technologies that caught that wave when it was coming in, rather than chasing it, as Sun seems to be doing today.

The above quote was published nearly three years ago. I don't believe that things have improved much since then for applets. In that same article, Friesen "posed a series of questions to some of the most respected thinkers in the Java developer community" in an effort "to investigate the potential of Java on the client side, and of an applet comeback." The responses of these "most respected thinkers" (Danny Coward, Romain Guy, Chet Haase, Cay Horstmann, Ted Neward, Jim Weaver, and John Zukowski) were very interesting at that time, but I'd love to hear how or if their respective responses would be different now about the future of Java applets.

I do think that Oracle made the correct decision (announced at JavaOne 2010) to abandon JavaFX Script and allow JavaFX to be more easily used with the massive amounts of Java products, frameworks, and libraries already out there. My concern, however, is that it all may be too little too late for the Java applet.

Conclusion

Until Oracle states that support for the applet will be discontinued, there's always a glimmer of hope regarding the applet's future. However, if I was coerced into betting on the outcome, I could not in good conscience place my bet on the applet returning to its former glory. At this point, the Java applet has to fight a force potentially more powerful than any other: negative perceptions earned from truly negative experiences and perpetuated in discussions, blog posts, articles, and other modern forms of communication. It was difficult for the Java language to escape the "Java is slow" mantra that was once very true, but quickly improved (it's not as fast relatively as some languages, but is much faster than some of today's most trendy languages, and performance does depend on what's being done). The applet did not enjoy such rapid reaction to its complaints and now the hole that has been dug may be too deep to come out of.