Newsletter sign-up
View all newsletters

Sign up for our technology specific newsletters.

Enterprise Java
Email Address:

BeanLint: A JavaBeans troubleshooting tool, Part 2

Put JavaBeans to the test

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone

Page 2 of 12

The class itself isn't declared public

Any class that is supposed to be a bean must be public according to the JavaBeans spec. While some JavaBeans containers will let you get away with not declaring a JavaBean class as public, other containers aren't so tolerant, and portability is paramount for a JavaBean. The test is simple: verify that the class is public.

The class has no zero-argument constructor.
This can be checked easily enough: verify that the class has a constructor that takes no arguments.

The zero-argument constructor isn't public.
If the class has a zero-argument constructor, check that it's defined as public, and complain if it isn't.

The class is abstract.
If a class is abstract, it can't be a JavaBean in a useful sense, because it's not possible (by definition) to create an instance of an abstract class. This isn't to say a JavaBean can't inherit from an abstract class, but rather the JavaBean class itself may not be abstract. Fortunately, we can check every potential bean to see whether it is abstract, and produce an error message if it is.

The class doesn't implement Serializable.

This one's a trick question. You might say to yourself, "Self, this is easy enough: simply check that the class implements the interface java.io.Serializable." That's a worthwhile check to make, but due to certain subtleties in how Java defines serialization, the situation is quite a bit more complex than that. We'll cover the case of serialization in the second half of this article in the section entitled "Serial killers."



Notice that in this list, the way to detect the problem is by programmatically determining whether a class has a particular attribute (is it serializable, does it have such-and-such a constructor, and so forth.) We're in rather strange territory here. Typically, programs use classes to deal with data. But in this case, we're actually treating the classes themselves as data by making decisions and printing error messages or reports based on information about the class.

A moment for reflection

Analyzing class files is what the package java.lang.reflection is all about. The reflection package, introduced as a core package with Java 1.1, allows Java programmers to ask the JVM questions about classes it has loaded and even classes for which no instances exist. Java programs can, using the classes in the reflection package, determine what methods a class has, what those methods' parameters are, what fields a methods has, and so forth. This is made possible by a number of API extensions (in 1.1) to java.lang.Class, a class that Java uses to represent a class.

In order for the Java program and the JVM to have a "conversation" about classes, we'll need classes that represent things like methods, fields, constructors, and so on. That's where the reflection package comes in.

Among the classes in java.lang.reflection are the following:

  • The Field class represents a single, specific field of a class. BeanLint wants to be sure that all nonstatic, nontransient fields of a bean are serializable, so it uses instances of Field in order to check.

  • The Method class is a representation of a single method, with a particular name and list of parameters (taken together, the name and parameter list form the signature of the method). A method also has a return type, a list of modifiers (described in the next paragraph), and a list of exceptions that the method might throw. You can even execute the method a Method object represents, by calling its invoke() method. BeanLint doesn't invoke methods on its beans, but it uses class Method heavily when analyzing serialization (see the section "Serial killers" below.)

  • The Modifier class represents a modifier of a method or a field in a class. Modifiers indicate some property of a method, field, or class. For example, they can indicate whether a field is static, transient, or both; whether a class is abstract or is actually an interface; or whether a method is synchronized. Remember that a bean class must be public (as must its zero-argument constructor) so we'll be using class Modifier to test this bean requirement.

  • The Constructor class is similar to class Method, but it represents a constructor instead of a method. As such, it has no method for getting the return value class, since constructors don't have return values. Since one of the requirements of a JavaBean is that it have a zero-argument constructor, we'll be using class Constructor to analyze our beans' constructors.



So, now we have classes that represent the parts of a class (its methods, fields, modifiers, and so on), but how do we get at them? The addition of reflection to Java 1.1 included expanding the information about classes available from java.lang.Class. To get the class object for a class, you can either use the static method java.lang.Class.forName(String classname), passing in the name of the class for which you want a Class object; or, you can ask any Java object instance for its class by simply calling its getClass() method.

java.lang.Class has several methods for returning

information about a class. The return values of many of these methods are instances of classes in the package java.lang.reflect that we've just seen. Among the methods of java.lang.Class used by BeanLint are the following:

  • Constructor getConstructor(Class[]) returns a constructor with a particular parameter list. The array of Class objects passed into this method corresponds to the types of the parameters, in order, of the specific constructor we're requesting. For example, in BeanLint, we're interested in a constructor with no arguments, so we pass in an empty array of Class objects.

  • int getModifiers() returns an int that can be decoded by the methods of class Modifier. BeanLint is interested in whether a class or method is public, and uses this method to get that information.

  • Class[] getInterfaces() returns an array of Class objects, each of which corresponds to an interface that the class in question implements. There is no Interface class; instead, interfaces are represented by instances of class Class with the "interface bit" set in their modifiers. The Serializable interface must be implemented by all JavaBeans, so BeanLint uses getInterfaces() to determine whether the class being analyzed adheres to this rule.

  • Method getDeclaredMethod(String methodName, Class[] parameterTypes) returns a specific method of a class declared in that class; that is, not inherited from a superclass. We'll use this method in the section on serialization, as well.



BeanLint uses several other methods in these classes. If you run across methods in the BeanLint source that don't look familiar, check them out in the Java API documentation (see the link in Resources below).

Detecting the madness in your methods

One of the major goals of BeanLint is to detect problems in class methods. Now that we know what reflection is, let's see how BeanLint uses it to detect the problems described in the first list above. We're going to dissect the method analyzeBeans(), along with supporting methods. You can find the source for analyzeBeans() in the scrolling text box in Figure 3. If you want to print the BeanLint program, or you just want to see it in one piece, you can find the entire source code at BeanLint.html.



390 //

391 // Go through all classes in hashtable, analyzing them

392 // for beaniness.

393 //

394 protected void analyzeBeans() {

395

396 Enumeration e = htCache_.keys();

397

398 // Iterate through all hash table contents (classes)

399 while (e.hasMoreElements()) {

400 String className = (String)(e.nextElement());

401 Class beanClass = null;

402

403 System.out.println("\n=== Analyzing class " + className + " ===");

404

405 //

406 // If it's not defined by now, it never will be

407 //

408 try {

409 beanClass = findLoadedClass(className);

410 } catch (Exception exc) {

411 notABean(className, "trying to define it as a class caused " +

412 "an exception: " + exc.toString());

413 continue;

414 }

415

416 // This should never be true, but just in case...

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Comment
Login
Forgot your account info?
Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources
  • Get down to the nitty-gritty of serialization in my previous JavaBeans column "Serialization and the JavaBeans Specification" (February 1998), at http://www.javaworld.com/javaworld/jw-02-1998/jw-02-beans.html
  • Also see "Serialization grab bag" (April 1998), at http://www.javaworld.com/javaworld/jw-04-1998/jw-04-beans.html
  • The documentation for interface java.io.Serializable can be found at http://java.sun.com/products/jdk/1.1/docs/api/java.io.Serializable.html.
  • Download the source for this month's column at http://www.javaworld.com/jw-01-1999/beans/BeanLint.html