java.lang.ProcessBuilder

Posted in blog city on May 28th 2004 

In JDK1.5, a new class ProcessBuilder has been added to java.lang package. This class should be used to execute OS specific commands. Prior to JDK1.5, developers use Runtime.exec to execute any OS specific commands.

I looked at the source code of Runtime.exec in JDK1.5, and it internally uses ProcessBuilder. The javadoc also clearly states that ProcessBuilder is the preferred way of meeting OS specific needs. However, the exec method in Runtime class is not deprecated. In my view, ideally exec should have been deprecated.

Also, the naming convention of methods in ProcessBuilder is strange. For example, consider the following two methods –

public File directory()
and
public ProcessBuilder directory(File directory)

While the first method  returns the current working directory, the latter actually sets the current working directory! Same is the case with command, redirectErrorStream methods.

Not really sure why one cannot name these as getWorkingDirectory() and setWorkingDirectory() that is more conventional in the java world.

May be I am missing something here!

Introducing Annotation Processing Tool (apt) in JDK1.5 Beta2

Posted in blog city on June 4th 2004 

I wrote an article on introducing “Apt”. You can read the same here.

Update: This article introduces “apt”, the new tool provided along with JDK1.5 beta2 to process annotations, the new addition to Java5. It walks you through a simple example on declaring an annotation, using it and processing the same. This will help you get started on using annotations in your applications. 

Introduction

Sun has recently released Annotation Processing Tool (apt) along with beta2 of JDK1.5. This tool greatly helps in using the annotations feature that is introduced in JDK1.5. In this article, I will walk you through the steps required to create, use and process annotations, with the help of an example. This is not an article on annotations feature in JDK1.5, but an article on how to process annotations using apt. One can refer to the article “Declarative Programming in Java” at OnJava to get a very good overview of annotations and what they have to offer.  “Apt” is a command line utility that helps in processing annotations on the source code. Instead of delving into the details on what “apt” offers, I will straight away dive into an example, which can help one appreciate the “power” of apt. 

Setting the Ground

During software development, it is very common for developers to use markers such as “TODO”, “FIXME” to mark the work that is not yet complete in all aspects. Usually these will be present inside the comments. Usually the developer searches the source code regularly to figure out if there are still any more incomplete tasks. Let us use annotations in this scenario, for the purpose of understanding the annotations feature and understanding how to the processing them using the apt. The objective is to process the annotated source code and get list of places where the code has been marked as “ToDo”. Before proceeding further, ensure that you have JDK1.5 beta 2 with you. You can download it from here. Also, as you are aware, JDK1.5 comes with host of other additions to the language, such as Generics, enhanced for loop, static imports. I will refrain from using the new features in the listings below to the best possible extent, so that the focus of the reader is not diverted to these new features.

Step 1: Declare the ToDo Annotation

Declaring an annotation is very simple. Following is the listing of the ToDo annotation. This annotation does not have any members. 

Listing 1: ToDo.javapackage com.rajs.jdk1_5;

public @interface ToDo
{
}

 Notice the special “@” tag before the interface declaration. This indicates that this is not a regular interface, but an annotation. 

Step 2: Write Code that uses the “ToDo” Annotation.

Annotations can be at package level, class level, field level, method level and even at local variable level. Let us create two classes one which uses the annotation at a class level and another at method level for the sake of the example. 

Listing 2: IncompleteClass.java

package com.rajs.jdk1_5; 

@ToDopublic class IncompleteClass
{
}

Listing 3: PartiallyCompleteClass.java

package com.rajs.jdk1_5;

public class PartiallyCompleteClass
{
    private int age = 20;
    @ToDo
   
private String name;    
   
   
public PartiallyCompleteClass()
   
   
{
   
   
}

   
public int getAge()
    {
        return age;
    }

    @ ToDo
    public void incompleteMethod()
    {
    }
}

 Notice the way the class, method and attribute are annotated by simply putting the “@<AnnotationName> before declaring them. Now we have code which can compile in JDK1.5. However there is no use of this code as we are not doing anything with the annotations. Let us now process these annotations so that we get a report of the source code that is incomplete (annotated as ToDo). 

Step 3: Create an Annotation Processor Factory

The first thing that we need to do to process the annotations is to create an annotation processor factory. An annotation processor factory is responsible for creating an annotation processor for one or more annotation types. JDK1.5 beta2 comes with new classes that are part of the tools.jar. These new classes must be used by developers intending to use apt. There is an interface com.sun.mirror.apt.AnnotationProcessorFactory as a part of these new classes that must be implemented by us. Here is the listing for our factory. 

Listing 4: MyAPF.java

package com.rajs.jdk1_5;
import java.util.Collection;
import java.util.Set;
import java.util.Vector;
import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.apt.AnnotationProcessorFactory;

public class MyAPF implements AnnotationProcessorFactory
{
      Vector anntTypes = new Vector();
     
     
public MyAPF()
      {
          //all annotations are processed by this factory.
          anntTypes.addElement(“*”);
      }

    public Collection supportedOptions()
    {
        //no options are supported.
        return new Vector();
    }

    public Collection supportedAnnotationTypes()
    {
        return anntTypes;
    }

     public AnnotationProcessor getProcessorFor(
        Set <com.sun.mirror.declaration.AnnotationTypeDeclaration> set,
        AnnotationProcessorEnvironment env)
    {
        //return an instance of ToDoAnnotation Processor.
        return new ToDoAP(env);
    }
}

  In the above listing, we have declared a class that implements the com.sun.mirror.apt.AnnotationProcessorFactory interface. There are three methods in the factory.  The first method supportedOptions() returns if there any options that are supported by this annotation processing factory. For beginners like us, at this moment, we can ignore this and return an empty collection. The second method supportedAnnotationTypes() returns the collection of annotation types that can be processed by this factory. Since we have just one annotation type here, we can either give that name here or simply return a “*”, indicating all annotations are processed by this class. The third and the most important method getProcessorFor() returns the AnnotationProcessor that actually processes the annotations. Here we are returning an instance of ToDoAP (discussed in detail below). This method will be invoked by the apt. The tool shall pass the context of the processing using the AnnotationProcessorEnvironment parameter. The AnnotationProcessingEnvironment has methods that provide the state of processing the source code which can be used by the AnnotationProcessor. 

Step 4: Create the Annotation Processor

The annotation processor is the class that actually processes the annotations. This is the class that has the ability to create new source files, configuration files (deployment descriptors, for example), class files, or data files. Here is the listing for ToDoAP.java 

Listing 5: ToDoAP.java 

package com.rajs.jdk1_5;

import java.io.File;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Iterator;
import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.declaration.AnnotationTypeDeclaration;
import com.sun.mirror.declaration.Declaration; 

public class ToDoAP implements AnnotationProcessor
{
    AnnotationProcessorEnvironment ape;
    PrintWriter pw;

    public ToDoAP(AnnotationProcessorEnvironment ape)
    {
            this.ape = ape;
    }

    /**
     * @see com.sun.mirror.apt.AnnotationProcessor#process()
     */
    public void process()
    {
         AnnotationTypeDeclaration todoAtd = (AnnotationTypeDeclaration)ape.getTypeDeclaration(“com.rajs.jdk1_5.ToDo”);
        Collection c = ape.getDeclarationsAnnotatedWith(todoAtd);
        for(Iterator I = c.iterator(); i.hasNext();)
        {
            Declaration typeDecl = (Declaration)i.next();
            System.out.println(typeDecl.getPosition());
        }
    }
}

  In the above code, the method of significance is “process()”. In this method, the first line returns the declaration that represents the “com.rajs.jdk1_5.ToDo” annotation. The next line is the most important line – this provides the collection of Declarations that are annotated with “com.rajs.jdk1_5.ToDo”. This is achieved by calling the method “getDeclarationsAnnotatedWith()” method on the AnnotationProcessorEnvionment. The resulting collection of Declaration instances is iterated and the position of the annotation is printed by calling the getPosition() method on each instance of Declaration. 

Step 5: Compiling the Code

As mentioned earlier, to compile this code, one needs to have JDK1.5 Beta 2. You can download the same here. Ensure that the classpath contains “%JAVA_HOME%\lib\tools.jar”. Ensure the path contains “%JAVA_HOME%\bin”.Use the “–source 1.5” option along with javac, as we are compiling code containing JDK1.5 specific features. 

javac -cp %CLASSPATH% -d bin -source 1.5 src/com/rajs/jdk1_5/*.java

Step 6: Running “apt”

Finally, we are now ready to run the annotation processor tool.  Apt first processes the annotations in the specified source code. Apt recursively processes any new source code that is generated during the earlier processing, till no more source code is there to be processed.. Finally, all the source code will be compiled using javac by apt.  Apt has a default inbuilt mechanism by which it identifies the AnnotationProcessorFactories that needs to be used during its execution. However, in this article, we will use a much simpler way of specifying the apt the factory class to be used by passing the same as a command line parameter to apt. Following is the invocation of apt and the output 

>apt -cp %CLASSPATH% -d bin -s src -source 1.5 -factory com.rajs.jdk1_5.MyAPF src/com/rajs/jdk1_5/*.java>src/com/rajs/jdk1_5/PartiallyCompleteClass.java:4>src/com/rajs/jdk1_5/PartiallyCompleteClass.java:20>src/com/rajs/jdk1_5/IncompleteClass.java:9

 As you see the output contains the list of java files and approximate line number where the ToDo annotations are present. 

Summary

The article explained in detail the steps involved in executing the new “annotation processing tool” from Sun. Needless to say, we have just touched the waters of annotation. There are lots of advanced features such as DeclarationVisitors, Filer, Messagers that are not covered in this introductory article to limit the scope. Also it is worthwhile mentioning that while “apt” is intended to use for processing annotations, it can be used where there is a need to parse source code. Interested readers can always get into the depth from the references available in the Resources section. 

Resources

  1. Declarative Programming in Java — http://www.onjava.com/lpt/a/4768
  2. Download JDK1.5 — http://java.sun.com/j2se/1.5.0/download.jsp
  3. Getting Started with the Annotation Processing Tool — http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html


 


A Declaration is something which represents the declaration of a program element such as package, class, interface, method, field in the source code

© 2004 All rights reserved.

On Doclipse

Posted in blog city on June 16th 2004  

Cedric has released a new plug in for eclipse called Doclipse.
This is small but very nice utility to have in ones plug in list for eclipse.

However, with introduction of annotations in JDK1.5, I am not sure how many would like to write custom javadoc tags. I expect (and hope) developers to use more annotations than java doc tags.

I do not know much about XDoclet, except the fact that it supports various custom javadoc tags that help in generating code. I would hope that in future XDoclet will also start supporting annotations (similar to the custom javadoc tags) to generate the source code.

I expect auto completion to be present while using annotations once eclipse starts supporting new features of JDK1.5. Then the use of this plugin may be very limited.

PMD vs Findbugs vs Hammurapi

This post is moved to http://blog.thoughtsinprogress.co.in/2007/12/28/pmd-vs-findbugs-vs-hammurapi/

Unit Testing – Necessary but not Sufficient

Posted in blog city on July 12th 2004 

Mike Spille wrote his opinions on unit testing and JUnit in his recent blogs. Read some of them here and here. As is the case with any post, there are many points I agree and quite a few that I disagree. As a disclaimer, I am not a die hard follower of XP/TDD as I believe they are just some common sense ideas documented in detail.

JUnit has certainly changed the way developers look at unit testing. At the least, it has made unit testing poplular. Yes, it will not solve all other problems of integration testing, performance testing, MT testing etc. — but still it does help in unit testing.

As I see this is very important. Unit Testing is an necessary but not sufficient testing for any useful peice of software. We cannot think of next level of testing without unit testing. And when you have a complex project where multiple developers across geographies are involved, it becomes all more important to have unit test cases that follow some kind of framework, which is where JUnit fits in, rather neatly.

Usage of new Java5 method – inheritedChannel()

Posted in blog city on September 3rd 2004  

I am struggling to understand how to use the new method inheritedChannel() in java.lang.System.
Any example code on the usage? The method returns an instance of Channel interface which has only two methods close() and isOpen(). So, unless one knows the exact class that is returned by this method (SocketChannel, DatagramChannel, ServerSocketChannel etc), there is nothing much that can be done with inheritedChannel method.

So, are developers expected to somehow “detect” (instanceof?) the exact class that is being returned and use this method? If that is indeed the intention, not sure what is the purpose of returning an interface that is useless.

 Comments

#1
‘ posted this on Sat 4 Sep 2004, 12:37 am

What would be the point of giving it a less specific return type? It gives the programmer a good idea about what is going on. Most types do not specialise Channel.The idea, I believe, is that a program would be written to expect the inherited channel to be something specific. Obviously giving a nice error message in the case of that expectation not being reached is a good idea. So a logind style program may start up a Java program as a particular user inheritting a SocketChannel. Listening to ports less than 1024 is not allowed for user programs under UNIX, therefore a web server may inherit a ServerSocketChannel bound to port 80. It’s tough if you want two channels, for instance to support both HTTP and HTTPS.

Tom Hawtin [tackline@claranet.com]

#2
Rajs‘ posted this on Thu 9 Sep 2004, 4:06 pm

To me looks like this is violating some fundamental principles of OO. The API is kind of forcing it’s users to always check for the return type. With out this one cannot do anything. It is kind of same as returning java.lang.Object.
Ideally it would have better if there is some other interface or base class from which all these different channels are derived OR there should have been more methods that Channel interface must be enforcing. To me, it looks like a design issue.
#4
Rajs‘ posted this on Mon 4 Oct 2004, 2:50 am

Anon,
I am aware of the SelectorProvider api when I wrote this blog. That’s exactly the problem I was talking about. One has to do an “instance of” kind of operation to figure out which among the ServerSocketChannel, SocketChannel and DatagramChannel is returned. There is no other way to figure out.Rajs

Deleting untested code!!?

Posted in blog city on October 7th 2004  

Just learnt about this rather inane tool Guantanmo which *deletes* all code that is untested!!!
While it is amusing to see this kind of tool, I doubt if this has any kind of practical, real-life use.

All I am interested at any point is the %age of my test coverage so that I can improve my tests.
What any one can achieve by deleting the code. The simplest thing that can be done is to ensure the build fails when the coverage is not 100%. Why on the earth would any one want to use this tool!?