You are receiving this e-mail because you elected to receive e-mail from Sun Microsystems, Inc. To update your communications preferences, please see the link at the bottom of this message. We respect your privacy and post our privacy policy prominently on our Web site http://sun.com/privacy/

Please do not reply to the mailed version of the newsletter, this alias is not monitored. Feedback options are listed in the footer for both content and delivery issues.
  Welcome to the Core Java Technologies Tech Tips.
Core Java Technologies
TECHNICAL TIPS
October 18, 2005
View this issue as simple text
In This Issue
 
Here you'll get tips on using core Java technologies and APIs, such as those in Java 2 Platform, Standard Edition (J2SE).

This issue covers:

» Variable Arity Methods
» Customizing Resource Bundle Loading with ResourceBundle.Control
» Update to Last Month's Tip on Cookie Management With CookieHandler

The Variable Arity Methods tip was developed using Java 2 Platform, Standard Edition Development Kit 5.0 (JDK 5.0). You can download JDK 5.0 at http://java.sun.com/j2se/1.5.0/download.jsp.

The Customizing Resource Bundle Loading tip was developed using a snapshot release of Java Platform, Standard Edition 6 (Java SE 6), code name Java SE 6. You can download a snapshot release of Java SE 6 at https://jdk6.dev.java.net/.

This issue of the Core Java Technologies Tech Tips is written by John Zukowski, president of JZ Ventures, Inc.

See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms.

For more Java technology content, visit these sites:

java.sun.com - The latest Java platform releases, tutorials, and newsletters.

java.net - A web forum where enthusiasts of Java technology can collaborate and build solutions together.

java.com - Hot games, cool apps -- Experience the power of Java technology.

VARIABLE ARITY METHODS
 

Variable arity methods, sometimes referred to as varargs methods, accept an arbitrary set of arguments. Prior to JDK 5.0, if you wanted to pass an arbitrary set of arguments to a method, you needed to pass an array of objects. Furthermore, you couldn't use variable argument lists with methods such as the format method of the MessageFormat class or the new to JDK 5.0 printf method of PrintStream to add additional arguments for each formatting string present. The old style of using variable argument lists is described in the February 4, 2003 tip Using Variable Argument Lists.

JDK 5.0 adds a varargs facility that's a lot more flexible. To explore the varargs facility, let's look at a JDK 5.0 version of one of the printf methods in the PrintStream class:

   public PrintStream printf(String format,
                             Object... args)

This method was described in the October 2004 Tech Tip, Formatting Output With the New Formatter.

Essentially, the first argument is a string, with place holders filled in by the arguments that follow. The three dots in the second argument indicate that the final argument may be passed as an array or as a sequence of arguments. Note that the three dots are similar to how variable argument lists are specified in C and C++ programming.

The number of place holders in the formatting string determines how many arguments there need to be. For example, with the formatting string "Hello, %1s\nToday is %2$tB %2$te, %2$tY" you would provide two arguments: the first argument of type string, and the second of type date. Here's an example:

   import java.util.*;

   public class Today {
     public static void main(String args[]) {
       System.out.printf(
         "Hello, %1s%nToday is %2$tB %2$te, %2$tY%n",
         args[0], new Date());
     }
   }

Provided that you pass in an argument for the name, the results would be something like the following:

   > java Today Ed
   Hello, Ed
   Today is October 18, 2005

Autoboxing and unboxing allow you to provide primitive type arguments (as in the following printf method):

   System.out.printf("Pi is approximately  %f", Math.PI, %n);

Here, Math.PI is of type double. Autoboxing converts it to an object of type Double. (For more information on autoboxing, see the April 5, 2005 tip Introduction to Autoboxing).

If you create your own methods to accept variable argument lists, only the last argument to the method can be declared as accepting a variable list. You can't put the variable-length argument first. For example, the following method declaration is valid -- it defines a method that has one parameter which accepts a variable number of String arguments:

  private static void method1(String... args)

The variable argument parameter doesn't have to be the only argument. For instance, the following method takes one integer argument followed by an arbitrary number of arguments.

  private static void method2(int arg, Object... args) {

Putting methods that have the two formats shown above into a sample class allows you to call the methods with a different number of arguments:

      method1("Hello", "World");
      method1(args);
      method2(12, 'a', "Hello", Math.PI, 1/3.0);
      method2(18, 94.0);      

The second call shown above is slightly different than the others. It takes the String[] argument that comes into the main() method. Because the varargs facility is just implicit syntax for creating and passing arrays, an array can be passed directly. In this case, the compiler will simply pass the array unmodified.

Here's a sample class, MyArgs, that includes the two types of methods:

    import java.util.*;
    public class MyArgs {
      public static void main(String args[]) {
        method1("Hello", "World");
        method1(args);
        method2(12, 'a', "Hello", Math.PI, 1/3.0);
        method2(18, 94.0);
      }
      private static void method1(String... args) {
        System.out.println(Arrays.asList(args) +
          " // " + args.length);
      }
      private static void method2(int arg, Object... args) {
        System.out.println(Arrays.asList(args) + " / " + arg);
      }
    }

Here's a sample run of MyArgs:

    > java MyArgs x y z
   [Hello, World] // 2
   [x, y, z] // 3
   [a, Hello, 3.141592653589793, 0.3333333333333333] / 12
   [94.0] / 18

MyArgs accesses the variable argument list by passing it to the Arrays.asList() method. In other words, the argument is accessed just like an array. Normally, you would do something more like the following in an enhanced for loop:

  for (String arg: args) {
    ...
  }

This would allow you to process one element at a time.

As you can see, without much programming effort, the varargs feature allows you to define methods that accept a variable number of arguments. For more information about varargs, see Varargs.

Back to Top

CUSTOMIZING RESOURCE BUNDLE LOADING WITH RESOURCEBUNDLE.CONTROL
 

In the June 17, 2005 Tech Tips Beyond J2SE 5.0 and Collaborating With Sun on Java SE 6, you learned how to get started with Java SE 6. The platform is still pre-beta, but it is far enough along to see where things are headed and to try out some new features. One of the new features gives you more control in the use of resource bundles (final inclusion of the feature is subject to JCP approval).

Resource bundles are used for localization of applications. Their use was discussed in the December 14, 2004 tip Resource Bundle Loading. Basically, resource bundles give you the ability to map different strings to localized resources so that icons or text strings can show images and translations that are understood in different languages.

The use of resource bundles is managed by the ResourceBundle class in the java.util package. The basic process of working with a resource bundle is to get the bundle through the getBundle() method of ResourceBundle, as shown in the following program:

   import java.util.*;

   public class Test1 {
     public static void main(String args[]) {
       Locale locale = Locale.ENGLISH;
       ResourceBundle myResources =
         ResourceBundle.getBundle("MyResources", locale);
       String string = myResources.getString("HelpKey");
       System.out.println("HelpKey: " + string);
     }
   }

The Test1 program gets a bundle called MyResources. It then gets the resource named HelpKey from the bundle, and prints the value of the resource.

MyResources can be either a class file:

   import java.util.*;
   
      public class MyResources extends ListResourceBundle {
        public Object[][] getContents() {
          return new Object[][] {
            {"OkKey", "OK"},
            {"CancelKey", "Cancel"},
            {"HelpKey", "Help"},
            {"YesKey", "Yes"},
            {"NoKey", "No"},
          };
        }
   } 

or a file named MyResources.properties, with key value pairs:

   OkKey=OK
   CancelKey=Cancel
   HelpKey=Help
   YesKey=Yes
   NoKey=No

But, what if you want your resource bundle to be an XML file? The java.util.Properties class offers two ways to load files. One way involves reading the key=value type files through the load() method. The second way involves loading properties from XML files through the loadFromXML() method. Thanks to a new inner class of ResourceBundle in Java SE 6, named Control, you can have resource bundles stored as XML files.

At this point you might want to install the latest snapshot release from the Java SE 6 project home page.

The ResourceBundle.Control class offers a series of callback methods to be called when ResourceBundle.getBundle() searches for and loads bundles. By providing your own Control class, you can override the default loading and caching behavior.

For one such implementation of a custom Control class, you only need to override its getFormats() and newBundle() methods. The getFormats() method would say you support XML as a format, and newBundle() would then look for that bundle. There are helper methods in the base Control class to convert the base bundle names to the actual resource name.

Included with the custom ResourceBundle.Control implementation is a subclass of ResourceBundle named XMLResourceBundle. This subclass is used to load the XML file and treat it as a ResourceBundle.

Here's the full class description for the Control class and the ResourceBundle:

   import java.io.*;
   import java.net.*;
   import java.util.*;

   public class XMLResourceBundleControl extends 
      ResourceBundle.Control {
     private static String XML = "xml";

     public List getFormats(String baseName) {
         return Collections.singletonList(XML);
     }

     public ResourceBundle newBundle(String baseName, 
         Locale locale, String format, ClassLoader loader, 
         boolean reload)
             throws IllegalAccessException, 
                 InstantiationException, IOException {
       if ((baseName == null) || (locale == null) ||
           (format == null) || (loader == null)) {
         throw new NullPointerException();
       }
       ResourceBundle bundle = null;
       if (format.equals(XML)) {
         String bundleName = toBundleName(baseName, locale);
         String resourceName = 
            toResourceName(bundleName, format);
         URL url = loader.getResource(resourceName);
         if (url != null) {
           URLConnection connection = url.openConnection();
           if (connection != null) {
             if (reload) {
               connection.setUseCaches(false);
             }
             InputStream stream = connection.getInputStream();
             if (stream != null) {
               BufferedInputStream bis = 
                  new BufferedInputStream(stream);
               bundle = new XMLResourceBundle(bis);
               bis.close();
             }
           }
         }
       }
       return bundle;
     }
     private static class XMLResourceBundle extends 
        ResourceBundle {
       private Properties props;

        XMLResourceBundle(InputStream stream) throws IOException {
         props = new Properties();
         props.loadFromXML(stream);
       }
       protected Object handleGetObject(String key) {
         return props.getProperty(key);
       }
       public Enumeration getKeys() {
         Set handleKeys = props.stringPropertyNames();
         return Collections.enumeration(handleKeys);
       }
     }

     public static void main(String args[]) {
       ResourceBundle bundle = ResourceBundle.getBundle("Test2",
         new XMLResourceBundleControl());
        String string = bundle.getString("HelpKey");
       System.out.println("HelpKey: " + string);
    }
  }

Included in the custom Control class is a test program of three lines:

      ResourceBundle bundle = ResourceBundle.getBundle("Test2",
        new XMLResourceBundleControl());
       String string = bundle.getString("HelpKey");
       System.out.println("HelpKey: " + string);

The important line here is the first one. You must pass your custom control to the getBundle() method. After that, you use the bundle in the same way as any other bundle.

Here's what the XML resource bundle file, named Test2.xml, looks like:

<?xml version="1.0" encoding="UTF-8"?>
   <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
   <properties>
     <entry key="OkKey">OK</entry>
     <entry key="CancelKey">Cancel</entry>
     <entry key="HelpKey">Help</entry>
     <entry key="YesKey">Yes</entry>
     <entry key="NoKey">No</entry>
   </properties>
   

Running the XMLResourceBundleControl program produces the following output:

   > java XMLResourceBundleControl

     HelpKey: Help

Not shown in the implementation of ResourceBundle.Control above is the getTimeToLive() and needsReload() methods:

    public long getTimeToLive(String baseName,
                              Locale locale)

    public boolean needsReload(String baseName,
                               Locale locale,
                               String format,
                               ClassLoader loader,
                               ResourceBundle bundle,
                               long loadTime)

The getTimeToLive() method returns the "time-to-live" value for resource bundles that are loaded under ResourceBundle.Control. Resource bundles are maintained in a cache to speed performance the next time you use them. This means that the second call to load a bundle from the same class loader will find the bundle already loaded and cached. A positive time-to-live value specifies the number of milliseconds that a bundle can remain in the cache without needing to be being revalidated. The default return value of getTimeToLive() is TTL_NO_EXPIRATION_CONTROL, which disables the expiration control. If you don't want a bundle cached, have getTimeToLive() return ResourceBundle.Control.TTL_DONT_CACHE. If 0 is returned, a bundle is cached, but every time getBundle() is called the bundle is revalidated from the last call. To clear the cache, call the static clearCache() method of ResourceBundle. There is an optional ClassLoader argument to clear bundles loaded through a specific class loader. If not specified, the caller's class loader is used.

The needsReload() method determines if an expired bundle in the cache needs to be reloaded. A return value of true means the bundle needs to be reloaded, false means it doesn't. You can control if a resource bundle needs to be reloaded by having the Control class override the needsReload() method. For instance, if you always want a bundle to be reloaded, have the needsReload method always return true. If needsReload always returns true, you should also ensure that the getTimeToLive() method returns 0. Otherwise the data will be cached longer than necessary.

For additional information on the resource bundle enhancements and other internationalization features in Java SE 6, see the blog Overview of internationalization features from Sun software architect John O'Conner.



Back to Top

UPDATE TO LAST MONTH'S TIP ON COOKIE MANAGEMENT WITH COOKIEHANDLER
 

In last month's tip Cookie Management with CookieHandler, there were two lines of source code in the Cookie class that defined the expired date formats:

     private static DateFormat expiresFormat1
         = new SimpleDateFormat("E, dd MMM yyyy k:m:s 'GMT'");

     private static DateFormat expiresFormat2
        = new SimpleDateFormat("E, dd-MMM-yyyy k:m:s 'GMT'");

If you run the Fetch program as described in the tip in a locale other than US English, the program won't work. For the program to work properly in a locale other than US English, the locale needs to be explicitly set in the Cookie class as follows:

     private static DateFormat expiresFormat1
         = new SimpleDateFormat
            ("E, dd MMM yyyy k:m:s 'GMT'", Locale.US);

     private static DateFormat expiresFormat2
        = new SimpleDateFormat
           ("E, dd-MMM-yyyy k:m:s 'GMT'", Local.US);

The archived version of the tip has been updated to reflect this change.

Back to Top

Rate and Review
Tell us what you think of the content of this page.
Excellent   Good   Fair   Poor  
Comments:
If you would like a reply to your comment, please submit your email address:
Note: We may not respond to all submitted comments.
Comments? Send your feedback on the Tech Tips: http://developers.sun.com/contact/feedback.jsp?category=newslet

Subscribe to the following newsletters for the latest information about technologies and products in other Java platforms:
  • Enterprise Java Technologies Tech Tips. Get tips on using enterprise Java technologies and APIs, such as those in the Java 2 Platform, Enterprise Edition (J2EE).
  • Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME).
You can subscribe to these and other Java technology developer newsletters or manage your current newsletter subscriptions on the Sun Developer Network Subscriptions page

ARCHIVES: You'll find the Core Java Technologies Tech Tips archives at:
http://java.sun.com/developer/JDCTechTips/index.html

IMPORTANT: Please read our Terms of Use, Privacy, and Licensing policies:
http://www.sun.com/share/text/termsofuse.html
http://www.sun.com/privacy/
http://developer.java.sun.com/berkeley_license.html

© 2005 Sun Microsystems, Inc. All Rights Reserved. For information on Sun's trademarks see: http://sun.com/suntrademarks
Java, J2EE, J2SE, J2ME, and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.

Sun Microsystems, Inc. 10 Network Circle, MPK10-209 Menlo Park, CA 94025 US