Put JavaBeans to the test In last month’s JavaBeans column, “BeanLint: A JavaBeans troubleshooting tool,” we discussed various problems that may cause JavaBeans to fail — either not loading into a development environment or, even worse, failing at runtime. We also introduced a troubleshooting tool called BeanLint, which analyzes class files and reports these potential problems. (To get the most out of what’s being discussed here, you should read last month’s column first; it describes some potential problems JavaBeans might have, and begins to describe how BeanLint works.)Why BeanLint is important to beans developersBeanLint is interesting to JavaBeans developers for two reasons. First, it’s obviously preferable to find out about bugs in your software during development or testing, rather than from customer service. The second reason this topic is interesting is that it uses several new and/or advanced features of the Java platform, and, as such, provides good working examples of how to use those features.Last month’s topics included examples and a discussion of how to use package java.util.zip to read the contents of a jar file, and it covered the creation of a custom ClassLoader class to convert the jar file contents into usable Java classes. This month, we’ll see how BeanLint uses the package java.lang.reflection (a relatively new package introduced in Java 1.1) to analyze class files and look for potential problems. We’ll have an in-depth look at a potential pitfall in Java’s object serialization, and go over the solution BeanLint offers. Finally, we’ll show how to use the package java.beans.Introspector (part of the standard Java package java.beans) to produce a report of all of the properties, event sets, and other JavaBeans-related attributes of any class file that passes all the tests. But before we start, I’d like to respond to some reader mail.Several people have said they like the way I’ve been using scrollable text areas to show code examples in a compact way. Others say they don’t like it because the articles, when printed, don’t show all the code. Some have made both observations. One of my mottos is, Why choose when you can have both? So, I’ve continued to put the code in the scrolling text boxes, but any classes or programs I’ve written will also be accompanied by a link to a printable form of the source. Please write and let me know if this solution doesn’t address your concerns.Checking up on our beansBeanLint‘s basic approach to finding potential problems in a class file is to load the file as a class, and then perform a series of checks on the class that look for these problems. Last month, we looked at how to get at the bytes that represent a class file, and then how to convert that byte stream into a class. Now, we’ll get into the nitty-gritty of detecting potential problems in our beans. Several of these potential bean flaws have to do with properties of the class methods. The list below covers potential bean problems that can be detected by analyzing a class’s methods, and describes how to detect that problem. The class itself isn’t declared publicAny 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 reflectionAnalyzing 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 returninginformation 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 methodsOne 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 them392 // for beaniness.393 //394 protected void analyzeBeans() {395396 Enumeration e = htCache_.keys();397398 // Iterate through all hash table contents (classes)399 while (e.hasMoreElements()) {400 String className = (String)(e.nextElement());401 Class beanClass = null;402403 System.out.println(“n=== Analyzing class ” + className + ” ===”);404405 //406 // If it’s not defined by now, it never will be407 //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 }415416 // This should never be true, but just in case…417 if (beanClass == null) {418 notABean(className, “it couldn’t be loaded as a class”);419 continue;420 }421422 // A bean can’t be abstract; at least, we’re423 // not going to allow it to be. But, we still want424 // to define them as classes, because beans may425 // defined in terms of them.426 if (Modifier.isAbstract(beanClass.getModifiers())) {427 notABean(className, “the class is abstract”);428 continue;429 }430431 // A bean also must be a public class432 if (!Modifier.isPublic(beanClass.getModifiers())) {433 notABean(className, “the class is not public”);434 continue;435 }436437 // So it’s a concrete class, but is it a bean? Check for438 // a public, zero-arg constructor and Serializable implementation.439 // Also, ensure that all of its fields are Serializable!440 if (!isSerializable(beanClass)) {441 notABean(className, “the class is not Serializable”);442 continue;443 }444445 // See if it has a zero-argument constructor446 // Here we use reflection to get all constructors, and447 // then see if any of them have an empty argument list.448 Constructor c = null;449450 try {451 c = beanClass.getDeclaredConstructor(noArgs);452 } catch (NoSuchMethodException exc) {453 notABean(className, “it has no zero-argument constructor”);454 continue;455 }456457 // We have a zero-arg constructor, but what if it’s not public?458 if (!Modifier.isPublic(c.getModifiers())) {459 notABean(className, “its zero-argument constructor is not ” +460 “public”);461 continue;462 }463464 // Now, let’s make sure all of its fields are Serializable465 Field[] nsf = getNonSerializableFields(beanClass);466467 // If there are nonserializable fields, there’s a potential problem468 if (nsf.length != 0) {469470 String nsfFieldNames = “”;471 notABean(className, “the following fields of the class are ” +472 “not Serializable:”);473 for (int i = 0; i 474 System.out.println(“tt” +475 nsf[i].getType() + ” ” +476 nsf[i].getName() + ” (defined in ” +477 nsf[i].getDeclaringClass().getName() +478 “)”);479 }480 continue;481 }482483 // Only beans make it this far. Record the bean484 // in the hash table.485 System.out.println(className + ” passes all JavaBean tests”);486 htCache_.put(className, beanClass);487488 // Now create the introspection report489 printIntrospectionReport(beanClass);490 }491 }… (supporting methods follow)076077 // Returns true if class claims to implement Serializable, false otherwise078 protected boolean isSerializable(Class beanClass) {079 Class[] interfaces = beanClass.getInterfaces();080 int i;081082 for (i = 0; i 083 ;084085 return (i 086 }087088 //089 // Returns true only if the class passed in defines090 // private void writeObject(java.io.ObjectOutputStream out)091 // throws IOException092 // private void readObject(java.io.ObjectInputStream in)093 // throws IOException, ClassNotFoundException;094 // exactly. In this case, we can be generous and assume that095 // nonserializable, nontransient, nonstatic fields are being096 // handled by these methods.097 //098 static final Class[] writeArgs = { java.io.ObjectOutputStream.class };099 static final Class[] readArgs = { java.io.ObjectInputStream.class };100101 protected boolean definesCustomSerialization(Class beanClass) {102 Method writeMethod = null;103 Method readMethod = null;104 String cname = beanClass.getName();105 boolean result = true;106 Class[] exceptions;107108 try {109 writeMethod = beanClass.getDeclaredMethod(“writeObject”, writeArgs);110 } catch (SecurityException exc) {111 System.out.println(“Can’t search methods for ” +112 cname + “: ” + exc.toString());113 result = false;114 } catch (NoSuchMethodException e) {115 // Just didn’t find it116 result = false;117 }118119 try {120 readMethod = beanClass.getDeclaredMethod(“readObject”, readArgs);121 } catch (SecurityException exc) {122 System.out.println(“Can’t search methods for ” +123 cname + “: ” + exc.toString());124 result = false;125 } catch (NoSuchMethodException e) {126 // Just didn’t find it127 result = false;128 }129130 // Make sure the methods are defined precisely, and131 // complain if they’re not.132 if (writeMethod != null) {133134 if (!Modifier.isPrivate(writeMethod.getModifiers())) {135 System.out.println(“Warning: ” + cname + ” defines a ” +136 “non-private writeObject() method”);137 result = false;138 }139140 exceptions = writeMethod.getExceptionTypes();141 if (exceptions.length != 1 ||142 exceptions[0] != java.io.IOException.class) {143 System.out.println(“Warning: ” + cname + ” defines a ” +144 “writeObject() method with an invalid ” +145 “exception list”);146 result = false;147 }148149 if (readMethod == null) {150 System.out.println(“Warning: ” + cname + ” defines a ” +151 “writeObject() method, but no ” +152 “readObject() method”);153 result = false;154 }155 } else {156 result = false;157 }158159 if (readMethod != null) {160161 if (!Modifier.isPrivate(readMethod.getModifiers())) {162 System.out.println(“Warning: ” + cname + ” defines a ” +163 “non-private readObject() method”);164 result = false;165 }166167 exceptions = readMethod.getExceptionTypes();168 if (exceptions.length != 2) {169 System.out.println(“Warning: ” + cname + ” defines a ” +170 “readObject() method with an invalid ” +171 “exception list”);172 result = false;173 } else {174 boolean ok = ((exceptions[0] == java.io.IOException.class &&175 exceptions[1] == java.lang.ClassNotFoundException.class) ||176 (exceptions[1] == java.io.IOException.class &&177 exceptions[0] == java.lang.ClassNotFoundException.class));178179 if (!ok) {180 System.out.println(“Warning: ” + cname + ” defines a ” +181 “readObject() method with an invalid ” +182 “exception list”);183 result = false;184 }185 }186 if (writeMethod == null) {187 System.out.println(“Warning: ” + cname + ” defines a ” +188 “readObject() method, but no ” +189 “writeObject() method”);190 result = false;191 }192 } else {193 result = false;194 }195196 return result;197 }198199 //200 // Return a list of fields of the given class that are not serializable.201 // Returns a 0-length list if they all are.202 // This, of course, refers only to nonstatic, nontransient,203 // nonserializable fields.204 //205 protected Field[] getNonSerializableFields(Class beanClass) {206 Vector v = new Vector();207 Field[] myFields = null;208209 Class thisClass = beanClass;210211 // Walk to top of inheritance tree212 while (thisClass != null) {213214 myFields = null;215216 try {217 myFields = thisClass.getDeclaredFields();218 } catch (SecurityException exc) {219 System.out.println(“Couldn’t check fields for class ” +220 thisClass.getName() + “: ” +221 exc.toString());222 }223224 // If we have fields, see if they’re serializable225 if (myFields != null) {226227 // If custom serialization is defined for this class,228 // assume that it’s to handle the nonserializable229 // fields for this class. In that case, set “myFields”230 // to an empty array, thereby skipping the check for231 // this class.232 if (definesCustomSerialization(thisClass)) {233 System.out.println(“Note: ” + thisClass.getName() +234 ” defines custom serialization”);235 myFields = new Field[0];236 }237238 // Add each nonserializable field to the vector239 for (int i = 0; i 240241 // If a field is static or transient, we don’t242 // care about it.243 int mods = myFields[i].getModifiers();244 if (Modifier.isTransient(mods) || Modifier.isStatic(mods))245 continue;246247 // Get the field’s class, and then see if it’s248 // Serializable249 Class fieldClass = myFields[i].getType();250 if (fieldClass == null) {251 // Hmmm… this would be weird…252 }253254 // If the field type is an array, check its component255 // type for serializability; that is, arrays are256 // serializable if they’re arrays of serializable things257 if (fieldClass.isArray()) {258 fieldClass = fieldClass.getComponentType();259 }260261 // If the field class is primitive, it’s serializable by262 // definition, so we let it pass263 if (fieldClass.isPrimitive()) {264 continue;265 }266267 // Check to see if it’s serializable, and add it268 // to the list if not269 if (!isSerializable(fieldClass)) {270 v.addElement(myFields[i]);271 }272 }273 }274275 // Up a class276 thisClass = thisClass.getSuperclass();277 }278279 // Now return any nonserializable fields we collected280 Field[] result = new Field[v.size()];281 for (int i = 0; i 282 result[i] = (Field)v.elementAt(i);283 }284285 return result;286 }287288 //289 // Reports why a class is not a bean290 //291 protected void notABean(String cname, String reason) {292 System.out.println(“class ” + cname + ” is not a JavaBean because:”);293 System.out.println(“t” + reason);294 }295Figure 3. analyzeBeans() loading classes from ajar file The method analyzeBeans() is actually fairly simple. First, it sucks all of the beans from the jar file, saving their Class objects in a hashtable, using the class loader we defined last month (lines 394-404). It then iterates through all of the class names in the hashtable, and for each name, it gets the class object for that name and verifies certain facts about each bean. Here’s what it checks:The class can be loaded. (Lines 405-420): If findLoadedClass() fails, the class won’t load and therefore it can’t be a bean.The class isn’t abstract. (Lines 426-429): We determine whether the class is abstract by using the getModifiers() method of the bean’s Class object and the static method Modifier.isAbstract(). This is the standard way to check modifiers.The class is public. (Lines 431-435): We use the class’s modifiers with Modifier.isPublic to determine whether the class we’re analyzing is public, as it must be in order to be a bean.The class is Serializable. (Lines 440-443): Since beans must be serializable, we check that this class is. The method isSerializable() (lines 078-086) simply checks to see if the class or any of its superclasses implement java.io.Serializable. Note that this function has changed slightly since last month. See the section entitled “Oops” below for an explanation as to why. As noted before, claiming to implement Serializable and actually doing so are different things. We’ll discuss serializability in depth in the section “Serial killers” below.The class has a zero-argument constructor. (Lines 445-455): We use getDeclaredConstructors(), with an empty parameter list, to get the constructor that has no parameters — that is, the zero-argument constructor. We use the getDeclaredConstructors method because we want to ensure that this constructor is defined in this class, not in a superclass.The zero-argument constructor isn’t public. (Lines 457-462): We use getModifiers() and the Modifier class yet again, this time on the Constructor object we just retrieved.The class has a nonserializable field. (Lines 465-481): If a class has a field that can’t be serialized, then neither can an instance of the class. BeanLint reports any such nonserializable fields as errors. The section “Serial killers” below discusses just what is meant by a nonserializable field.A class that passes all of these tests is a bean, at least as far as BeanLint is concerned. Once the class’s “beaniness” has been established, BeanLint saves the bean in its hashtable and prints an “introspection report” on the bean, which describes the bean in minute detail. (Remember that the hashtable is the class cache from which the class loader gets its already-loaded classes.)Let’s continue now and see what introspection really is. JavaBean introspectionprintIntrospectionReport(), shown in Figure 4 (lines 493-521), uses the class java.beans.Introspector to analyze class files as beans. Classes have methods, fields, and modifiers. Beans also have these, since beans are classes, but they also have additional structures such as properties, event sets, and customizers.java.beans.Introspector uses the reflection package we’ve just learned about to discover information about a class file, and then present that class file to the caller as a JavaBean. The Introspector takes care of finding the BeanInfo object for the bean (if it exists), or of creating a dynamic BeanInfo object (if it doesn’t). It can find a Customizer class for a bean. The Introspector can also determine a bean’s properties and event sets, either by querying the BeanInfo object for the bean, or by using reflection and its knowledge of the JavaBeans naming conventions (called “design patterns” in the JavaBeans spec).542 // Create an introspection report for all beans registered543 // in the cache.544 public void printIntrospectionReport(Class beanClass) {545 BeanInfo bi = null;546547 try {548 bi = Introspector.getBeanInfo(beanClass);549 } catch (IntrospectionException e) {550 System.out.println(“Couldn’t introspect ” + beanClass.getName() +551 “: ” + e.toString());552 }553554 if (bi == null)555 return;556557 BeanDescriptor bd = bi.getBeanDescriptor();558 Class customizer = bd.getCustomizerClass();559 String custname = (customizer == null) ? “none” : customizer.getName();560561 // Print all properties, event sets, etc. We’re going to562 // ignore getAdditionalBeanInfo() for the moment.563 System.out.println(“nIntrospection Report”);564 System.out.println(“——————–“);565 System.out.println(“Class: ” + beanClass.getName());566 System.out.println(“Customizer class: ” + custname);567 System.out.println(“”);568569 PropertyDescriptor[] pd = bi.getPropertyDescriptors();570571 System.out.println(“Properties:”);572 int dpi = bi.getDefaultPropertyIndex();573 for (int i = 0; i 574 printDescriptor(pd[i], i == dpi);575 }576577578 EventSetDescriptor[] esd = bi.getEventSetDescriptors();579 int dei = bi.getDefaultEventIndex();580 System.out.println(“nEvent sets:”);581 for (int i = 0; i 582 printDescriptor(esd[i], i == dei);583 }584585 MethodDescriptor[] md = bi.getMethodDescriptors();586 System.out.println(“nMethods:”);587 for (int i = 0; i 588 printDescriptor(md[i]);589 }590591 System.out.println(“=== End of class ” + beanClass.getName() + ” ===n”);592 }… (supporting methods)493 // This could be expanded to be much more verbose494 protected void printDescriptor(PropertyDescriptor pd, boolean isDefault) {495 Method readMethod = pd.getReadMethod();496 Method writeMethod = pd.getWriteMethod();497498 String rmn = (readMethod == null) ? “none” : readMethod.getName();499 String wmn = (writeMethod == null) ? “none” : writeMethod.getName();500 Class pPropType = pd.getPropertyType();501502 if (pPropType == null)503 return;504505 String pClassname;506507 // Handle arrays508 if (pPropType.isArray()) {509 pPropType = pPropType.getComponentType();510 pClassname = pPropType.getName() + “[]”;511 } else {512 pClassname = pPropType.getName();513 }514515 if (isDefault) {516 System.out.print(” Default”);517 }518519 System.out.println(” ” + pClassname + ” ” + pd.getName() +520 ” {” + rmn + “, ” + wmn + “}”);521 }Figure 4. printIntrospectionReport() prints everything you ever wanted to know about a JavaBean printIntrospectionReport() gets a complete description of the JavaBean from the Introspector and simply prints the results. The PropertyDescriptors are structures used to describe individual properties of the bean, just as a Field object for a class describes a field of the class. Likewise, EventSetDescriptor instances describe individual event sets of the bean.Let’s finish up this month’s column by going over some subtleties of serialization.Serial killersSerialization in Java is easy, but it can be tricky for the uninitiated. (For background on serialization, see my previous JavaBeans columns linked to in the Resources below.) The first odd point about serialization is that the interface java.io.Serializable has no methods! This makes it extremely easy to implement. All a developer has to do is add implements java.io.Serializable to a class definition, and the interface is implemented. Unfortunately, that doesn’t mean you’ll have no serialization problems.In fact, a class may claim to implement Serializable, and then fail to perform correctly in actual use. This is because of how Java does its default serialization: it simply tells all of the nonstatic, nontransient fields of the object to serialize themselves. If any one of those fields of the object fail to serialize, the whole object fails. One excellent example of this problem, with which I started last month’s column, was trying to serialize an object that contains an instance of java.awt.Image. Since the Image object isn’t serializable, default serialization for the whole object fails.Notice that I’ve been saying default serialization. Java serializes objects using the default serialization mechanism unless the object defines custom serialization for that class, by defining the readObject() and writeObject() methods (see the documentation for java.io.Serializable linked in Resources below). If custom serialization for a class is specified, the serialization methods readObject() and writeObject() take care of serializing and deserializing. For example, in our object that contains a java.awt.Image, a developer might choose to define readObject() and writeObject() methods that send or receive the URL of the Image as a string, instead of transmitting the Image itself. Since readObject() and writeObject() exist, the serialization machinery calls them instead of the default, and the problem of a nonserializable field is resolved.205 protected Field[] getNonSerializableFields(Class beanClass) {206 Vector v = new Vector();207 Field[] myFields = null;208209 Class thisClass = beanClass;210211 // Walk to top of inheritance tree212 while (thisClass != null) {213214 myFields = null;215216 try {217 myFields = thisClass.getDeclaredFields();218 } catch (SecurityException exc) {219 System.out.println(“Couldn’t check fields for class ” +220 thisClass.getName() + “: ” +221 exc.toString());222 }223224 // If we have fields, see if they’re serializable225 if (myFields != null) {226227 // If custom serialization is defined for this class,228 // assume that it’s to handle the nonserializable229 // fields for this class. In that case, set “myFields”230 // to an empty array, thereby skipping the check for231 // this class.232 if (definesCustomSerialization(thisClass)) {233 System.out.println(“Note: ” + thisClass.getName() +234 ” defines custom serialization”);235 myFields = new Field[0];236 }237238 // Add each nonserializable field to the vector239 for (int i = 0; i 240241 // If a field is static or transient, we don’t242 // care about it.243 int mods = myFields[i].getModifiers();244 if (Modifier.isTransient(mods) || Modifier.isStatic(mods))245 continue;246247 // Get the field’s class, and then see if it’s248 // Serializable249 Class fieldClass = myFields[i].getType();250 if (fieldClass == null) {251 // Hmmm… this would be weird…252 }253254 // If the field type is an array, check its component255 // type for serializability; that is, arrays are256 // serializable if they’re arrays of serializable things257 if (fieldClass.isArray()) {258 fieldClass = fieldClass.getComponentType();259 }260261 // If the field class is primitive, it’s serializable by262 // definition, so we let it pass263 if (fieldClass.isPrimitive()) {264 continue;265 }266267 // Check to see if it’s serializable, and add it268 // to the list if not269 if (!isSerializable(fieldClass)) {270 v.addElement(myFields[i]);271 }272 }273 }274275 // Up a class276 thisClass = thisClass.getSuperclass();277 }278279 // Now return any nonserializable fields we collected280 Field[] result = new Field[v.size()];281 for (int i = 0; i 282 result[i] = (Field)v.elementAt(i);283 }284285 return result;286 }Figure 5. getNonSerializableFields() finds fields that are likely to cause default serialization to fail What does this have to do with BeanLint? Well, we’re trying to catch as many errors as we can up front, and nonserializable fields definitely will interfere with serialization unless custom serialization is defined. For the purposes of BeanLint, we’re going to say that a field cannot be serialized if all of the following conditions are met (the line numbers refer to the source for getNonSerializableFields() in Figure 5):The field is neither transient nor static. (Lines 243-245): The reason for this condition is that default serialization ignores static and transient fields, so they can’t possibly interfere with default serialization.The field isn’t primitive. (Lines 263-265): Primitive types like int and double are serializable by definition.The class to which the field belongs doesn’t define custom serialization. (Lines 232-246): If a class does define custom serialization, we can assume the developer is aware that nonstatic, nontransient fields need to be attended to manually. (For example, sending a URL instead of an Image.) We’ll assume the developer knows best, and consider nonserializable fields in classes with custom serialization to be taken care of.Fields that are arrays or objects have additional constraints:If the field is an array, the base type of the array isn’t serializable. (Lines 257-259): Arrays are serializable, as long as their contents are.If the field is an object, the object isn’t serializable. (Lines 269-271): Since default serialization tries to serialize all of a class’s fields, any one field that doesn’t serialize causes the entire serialization process to fail.To make matters just a bit more complicated, default serialization works for the entire object, including fields that are defined in superclasses. So even though your class may not contain a nonserializable object, your class’s superclass may. Therefore, getNonSerializbleFields collects all nonserializable fields, even those in superclasses of the class being analyzed (line 276).Any field that meets all of these conditions is a nonserializable field, and any class that contains a nonserializable field (based on this definition) almost certainly will not serialize properly. Using BeanLint against your bean before releasing it to testing may help catch these more subtle errors.Oops!As noted above, the function isSerializable() has been modified slightly since last month. Alert reader Alexander Rusak of Minsk, Belarus (Vit´j, Alexander!) pointed out a problem with how BeanLint detects whether a class implements Serializable. (See Alexander’s e-mail at bug.html.)The problem is simply that isSerializable() was checking if the class implemented java.io.Serializable, but it wasn’t checking whether any the class’s superclasses implemented it. So, BeanLint was checking whether a class implemented Serializable directly, where implementing it indirectly (via inheritance) is sufficient. The solution is to check if any superclass implements Serializable.In the new version of the code, I added one line, and modified another. isSerializable() now checks to see if the Class object it has received is null; if it is, it now returns false (since null obviously can’t implement anything), like this:079 if (beanClass == null) return false; The return statement also now calls isSerializable() recursively to check superclasses. If any superclass implements Serializable, it will return true. If the recursion reaches java.lang.Object, the call to getSuperclass() will return null, and the result will be false (because of line 079). The code for the new return statement appears below:084 return (i < interfaces.length) || 085 isSerializable(beanClass.getSuperclass()); Thanks (or, rather, dzi´kuju), Alexander, for finding and reporting this problem.ConclusionThe BeanLint tool can catch a lot of potential errors, but it’s no substitute for proper regression testing.This month, we’ve taken an in-depth look at BeanLint and the various things that can go wrong in a JavaBean. We’ve leveraged reflection and introspection to detect some of those potential problems, and in the process learned quite a bit about reflection, introspection, and some of the vagaries of default serialization. Please write and let me know if you’ve found BeanLint useful, or what new features you believe it ought to include. You can find my e-mail address in the bio section below.Mark Johnson has a BS in computer and electrical engineering from Purdue University (1986). He is a fanatical devotee of the design pattern approach in object-oriented architecture, of software components in theory, and of JavaBeans in practice. Over the past several years, he worked for Kodak, Booz-Allen and Hamilton, and EDS in Mexico City, developing Oracle and Informix database applications for the Mexican Federal Electoral Institute and for Mexican Customs. He currently works as a designer and developer for Object Products Inc. in Fort Collins, CO.