Java Tip 57: Applet parameterization via class reflection

Making applets configurable from HTML docs can be a chore -- unless you know how to do it the easy way

Developers who have written applets of at least moderate size know that making them configurable from HTML documents can be a tedious chore. This usually involves writing repetitive lines of code in the applet's init() method that will fetch fields from strings returned by the getParameter() method.

This article presents an elegant method for applet parameterization. Paramterization means passing parameters via PARAM tags embedded in the HTML file. This method does all the parameter fetching for you. It may be quite a while before you ever need to type getParameter again! The article assumes you have a reasonably good knowledge of the class reflection mechanism, although you could probably figure it out by just looking at the code.

The class reflection mechanism feature is located in the java.lang.reflect package. It lets you examine the structure of a class and provides runtime information on fields, methods, access modifiers, and so on.

So many lines

Most Java programmers know that, ideally, applets should be configurable from their corresponding HTML document -- but how many really are? For my own part, when I neglect to define parameters that would allow Web designers to feed my applets with data of their own, it's usually because I just don't feel like writing an endless sequence of lines that look like this:

  
        // Get delay (in frames) between billboard messages. 
        param = parent.getParameter("BILLBOARD_DELAY"); 
        if (param != null) { 
           try { 
              BILLBOARD_DELAY = Integer.parseInt(param); 
           } 
           catch (Exception e) {} 
        } 
  
        // Get checker cell dimension. 
        param = parent.getParameter("CHECKER_CELL_WIDTH"); 
        if (param != null) { 
           try { 
              CHECKER_CELL_WIDTH = Float.valueOf(param).floatValue(); 
           } 
           catch (Exception e) {} 
        } 
        // Gravitational constant. 
        param = parent.getParameter("G"); 
        if (param != null) { 
           try { 
              G = Float.valueOf(param).floatValue(); 
           } 
           catch (Exception e) {} 
        } 
        ... 

Even if you discipline yourself in parameterizing right from the beginning stages of your development, there will always be a number of fields that will either appear, disappear, or simply change type during the course of development. This means going back to the init() method each time such alterations occur. Having to do this is really annoying because it's very useful to test various settings by updating the parameters directly from the HTML page during the development stage. You don't want to leave parameterization until the final stages of your development -- if you do, you'll have to hardcode values and compile each time you want to test new settings.

This article describes a method that does all the parameter fetching for you and may save you from having to type getParameter ever again!

What the parameter fetching method does

Our parameter fetching method initializes every public non-final field that has a corresponding PARAM tag in the HTML document. If you only want a subset of those fields to be fetched, you have to adopt a field-naming convention where each field to be included must have a name that begins with a prefix of your liking. You usually won't have to use this scheme. I included this feature in case you have public fields you want to hide from the HTML coder.

Note that it is harmless to have public fields that don't have corresponding HTML settings -- our method will simply ignore them.

Requiring public access to the parameter fields may seem to go against the object-oriented programming principle of encapsulation, but applets are very rarely accessed by anything other than a browser, and they are not often subclassed. So there isn't much danger that these fields will be misused. But if you absolutely want to encapsulate your applet, there is a slightly less elegant version of our technique that you can use. We'll see this in the section "Setting private fields".

Using the parameter fetching method

Here's all you have to write to get your applet initialized:

    public void init(){ 
        Util.initializeApplet(this, null); 
    } 

I declared initializeApplet() as a static method in class Util. Of course, it's up to you to declare it in your favorite utility class.

Implementation of the parameter fetching method

Now that you know how to use the parameter fetching method, here's the code for implementing it:

import java.applet.*;
import java.lang.reflect.*;
public abstract class Util {
    /**
     * Initializes an Applet's public non-final fields whose names start
     * with a given filter prefix.  The initial values will be read from
     * the HTML PARAM tags. 
     *
     * @param applet the applet to initialize.
     * @param filterPrefix only fields whose name starts with this prefix
     *        will be initialized.
     *        If the prefix is null, all public non-final fields 
     *        will be initialized
     */
    public static void initializeApplet(Applet applet, String filterPrefix) {
        Class metaclass = applet.getClass();
        Field[] fields = metaclass.getFields();
        String param = null;
        for (int i = 0; i < fields.length; i++) {
            try {
                param = applet.getParameter(fields[i].getName());
                if (param == null ||
                    Modifier.isFinal(fields[i].getModifiers()) ||                
                    ((filterPrefix != null) && 
                     !fields[i].getName().startsWith(filterPrefix))
                   )                
                   continue;            
                Class fieldType = fields[i].getType();
                if (fieldType.equals(boolean.class)) {
                    fields[i].setBoolean(applet, Boolean.valueOf(param).booleanValue());
                }
                else if (fieldType.equals(byte.class)) {
                    fields[i].setByte(applet, Byte.valueOf(param).byteValue());
                }
                else if (fieldType.equals(char.class)) {
                    fields[i].setChar(applet, param.charAt(0));
                }
                else if (fieldType.equals(double.class)) {
                    fields[i].setDouble(applet, Double.valueOf(param).doubleValue());
                }
                else if (fieldType.equals(float.class)) {
                    fields[i].setFloat(applet, Float.valueOf(param).floatValue());
                }
                else if (fieldType.equals(int.class)) {
                    fields[i].setInt(applet, Integer.valueOf(param).intValue());
                }
                else if (fieldType.equals(long.class)) {
                    fields[i].setLong(applet, Long.valueOf(param).longValue());
                }
                else if (fieldType.equals(short.class)) {
                    fields[i].setShort(applet, Short.valueOf(param).shortValue());
                }
 
                else if (fieldType.equals(String.class)) {
                    fields[i].set(applet, param);
                }
            }
            catch (Exception e) {
                System.err.println(e + " while initializing " + fields[i]);
            }
        }
    }
}

We consider every public field in the applet as a potential parameter, settable with some PARAM tag. So, we try to match each public field with a corresponding PARAM tag. If there is a match, we fetch the corresponding value.

Therefore we need a list of all public fields that are declared in the applet. The class reflection mechanism provides this kind of information. This mechanism is located in the java.lang.reflect package, that's why we import it.

We obtain a reference to the applet's metaclass by calling applet.getClass(). Then we obtain the array of metafields by calling metaclass.getFields(). Then, for each metafield in the array, we check that it does have a corresponding PARAM tag in the HTML document. If it does, we check whether it represents a final field by calling:

   Modifier.isFinal(fields[i].getModifiers()) 

Method getModifiers() returns a mask of "modifiers" (like static, final, volatile, and so on) that are part of the field's declaration.

Once we are sure that the metafield represents a public non-final field, we convert the PARAM string to the appropriate field type.

Setting private fields

If you wish to automatically initialize your private fields, you must write a similar initializeApplet method inside the applet itself, because the Field.setXXX methods called in class Util can't have access to the applet's private fields. You must also replace getFields (returns public fields only) by getDeclaredFields (returns all fields).

Conclusion

This tip has shown you how the class reflection mechanism can be used to ease the programmer's task of developing configurable applets. Using a method that does all the parameter fetching for you, it may be awhile before you have to type getParameter again.

Yvon received a degree in mathematics and computer science from McGill University in Montreal. He has seven years of programming experience, ranging from business server applications to GUI programming. Two years ago he became a Java addict (as he puts it!) He received his Sun Certified Java Programmer title last January, and more recently, he became a Sun Certified Java Developer. He is working as a consultant for MTLI-NSK Technologies in Paris. His current project is a human resource management system written entirely in Java and based on an ObjectStore OODBMS. Life is interesting in Europe, but as a Canadian he misses hockey very much!

Learn more about this topic

  • For information on class reflection, see Chuck McManis's JavaWorld article, "Take an in-depth look at the Java Reflection API" http://www.javaworld.com/javaworld/jw-09-1997/jw-09-indepth.html