Настройка загрузки набора ресурсов при помощи класса ResourceBundle.Control

Добавлено : 26 Mar 2009, 15:48

В советах от 17 июня 2005 года Beyond J2SE 5.0 и Collaborating With Sun on Mustang были приведены сведения по использованию Java SE 6. Данная платформа все еще находится на раннем этапе разработки, но существует возможность отсеживать новые тенденции и тестировать новые возможности. Одной из новых возможностей является расширенный контроль использования наборов ресурсов (окончательное включение данной возможности находится на рассмотрении в JCP).

Наборы ресурсов применяются для локализации приложений. Их использоание обсуждалось в совете от 14 декабря 2004 года Resource Bundle Loading. Основной функцией наборов ресурсов является сопоставление различных строк локализованным ресурсам, таким образом, что изображения и строки отображаются на различных языках.

Управление набором ресурсов производится классом ResourceBundle, находящимся в пакете java.util package. Основой процесса работы с набором ресурсов является получение набора при помощи метода getBundle() класса ResourceBundle. Ниже приводится пример использования данного метода:

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);
 
}
}

Программа Test1 получает набор под названием MyResources. Затем она получает из набора ресурс HelpKey и выводит значение данного ресурса.

Файл MyResources может быть представлен либо в виде класса:

import java.util.*;

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

либо в виде файла, именуемого MyResources.properties, содержащего пары ключ-значение:

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

Что если вы хотите сохранять набор ресурсов в XML файле? Существует два способа загрузки файлов при помощи класса java.util.Properties. Первый способ позволяет читать файлы вида key=value, используя метод load(). Второй способ дает возможность чтения свойств из XML файла при помощи метода loadFromXML(). Благодаря наличию нового встроенного класса Control в наборе классов ResourceBundle, вы можете получать доступ к набору ресурсов, хранящемуся в виде XML файла.

Для дальнейшего тестирования вам необходимо загоусить последнюю версию Mustag с адреса Mustang project home page.

В классе ResourceBundle.Control существует набор внешних методов, вызываемых методом ResourceBundle.getBundle() во время поиска и загрузки наборов. Создав свой класс Control, вы можете изменить поведение по-умолчанию для загрузки и кеширования.

В данном случае вам необходимо создать реализацию двух методов класса Control: getFormats() и newBundle(). Метод getFormats() отвечает за поддержку формата XML, а newBundle() работает с набором ресурсов. В базовом классе Control существуют вспомогательные методы, предназначенные для преобразования основных имен наборов в действительные имена ресурсов.

В данную реализацию класса ResourceBundle.Control включен подкласс XMLResourceBundle. Данный подкласс используется для загрузки данных из XML файла и использовании их в методе ResourceBundle.

Ниже приводится описание класса Control и реализация метода 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);
 
}
}

В данную реализацию включена тестовая программа из трех строчек:

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

Наибольший интерес здесь представляет первая строка. Вам необходимо передать ваш элемент Control методу getBundle(). После этого вы можете использовать набор, как и в любом другом случае.

Ниже преводится пример XML файла Test2.xml:

<?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>

Результатом выполнения программы XMLResourceBundleControl будет:

> java XMLResourceBundleControl
HelpKey: Help

В приведенной реализации не используются методы getTimeToLive() и needsReload():

public long getTimeToLive(String baseName, Locale locale)

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

Метод getTimeToLive() возвращает время жизни для наборов ресурсов, созданных при помощи ResourceBundle.Control. Наборы ресурсов сохраняются в кеше для убыстрения процесса повторной загрузки. Таким образом, при повторной загрузке набора, он будет находиться в кеше. Положительное значение времени жизни затает в милисекундах продолжительность сохранения набора в кеше без повторной проверки. По-умолчанию значением, возвращаемым методом getTimeToLive() является TTL_NO_EXPIRATION_CONTROL, отключающее проверку истечения времени хранения в кеше. Если вы не хотите кешировать набор, то верните значение TTL_DONT_CACHE. Если возвращается значение 0, то набор кешируется, но при каждом вызове метода getBundle() происходит его проверка. Для очистки кеша вызовите статичный метод clearCache() класса ResourceBundle. В нем есть не обязательный аргумент ClassLoader, позволяющий очищать кеши, созданные определенным загрузчиком.

Метод needsReload() определяет необходимость перезагрузки кешированного набора. Значение true означает, что набор необходимо перезагрузить, а false, что не его перезагружать не надо. Вы можете контролировать необходимость перезагрузки набора ресурсов при помощи перегрузки метода needsReload(). Например, если вы хотите, чтобы набор ресурсов всегда перезагружался, метод needsReload() должен всегда возвращать значние true. В этом случае метод getTimeToLive() должен возвращать всегда значение 0. Иначе набор будет сохраняться дольше, чем положено.

Для получения дополнительной информации об улучшениях, связанных с процессами интернационализации в Mustang, вы можете обратиться к блогу Джона Оконера, разработчика програмнного обеспечения фирмы Sun, по адресу Overview of Mustang's internationalization features.

Теги: java resource bundle