Статьи

Обработка недостающих ресурсов с событиями CDI

В первой части мы создали простое приложение, которое использовало комплекты строковых ресурсов в JSF, а во второй части мы расширили его, используя CDI для внедрения поставщика ресурсов в бины, чтобы мы могли повторно использовать наш код для доступа к строковым ресурсам, зависящим от локали. ,

Одним из преимуществ использования CDI для внедрения ваших bean-компонентов вместо простого их создания вручную является возможность использовать механизм событий внутри этих bean-компонентов. В этом примере мы запустим событие, когда обнаружим, что ресурс отсутствует.

  1. Начните с создания класса MissingResourceInfo, в котором хранится информация о пропущенном ресурсе (ключ и локаль). Это будет передано вместе с событием, поэтому у наблюдателя будет вся информация, когда он получит событие.
    public class MissingResourceInfo {

    private final String key;
    private final String locale;

    public MissingResourceInfo(String key, Locale locale) {
    this(key, locale.getDisplayName());
    }

    public MissingResourceInfo(String key, String locale) {
    super();
    this.key = key;
    this.locale = locale;
    }

    public String getKey() {
    return key;
    }

    public String getLocale() {
    return locale;
    }
    }
  2. Мы запускаем событие, когда обнаруживаем, что в используемом файле свойств локали отсутствует ключ ресурса. В классе MessageProvider из части 2 мы внедряем экземпляр Event в поле и модифицируем метод getValue, чтобы он вызывал событие, когда ключ отсутствует.
    @RequestScoped
    public class MessageProvider {

    @Inject
    private Event<MissingResourceInfo> event;
    ....
    ....
    public String getValue(String key) {
    String result = null;
    try {
    result = getBundle().getString(key);
    } catch (MissingResourceException e) {
    result = "???" + key + "??? not found";
    event.fire(new MissingResourceInfo(key, getBundle().getLocale()));
    }
    return result;
    }
    }
  3. Наконец, мы хотим добавить наблюдателя для этого события, которое обрабатывает его при его запуске. Для этого мы добавим новый класс для этой цели.
    public class MissingResourceObserver {

    public void observeMissingResources(@Observes MissingResourceInfo info) {
    String template = "Oh noes! %s key is missing in locale %s";
    String msg = String.format(template, info.getKey(), info.getLocale());
    System.out.println(msg);
    }

    }
  4. Наконец, чтобы увидеть его в действии, нам нужно добавить средство получения bean-компонента, чтобы извлечь несуществующее значение из пакета.
    @Named
    @RequestScoped
    public class AnotherBean {

    public String getMissingResource() {
    return provider.getValue("ImNotHere");
    }
    }
  5. Мы можем увидеть это в действии, добавив к нашей странице JSF вызов этого свойства.
    #{anotherBean.missingResource}

    В результате в консоли отображается следующее:

    Oh noes! 'ImNotHere' key is missing from locale English (United States)

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

Что насчет EL?

Это прекрасно работает, когда мы обращаемся к пакетам ресурсов из Java, но как насчет того, когда мы используем выражения EL для доступа к значениям. JSF направляет эти выражения прямо в пакет ресурсов без возможности их перехвата. Один из способов исправить это — написать собственный распознаватель выражений EL и посмотреть, отображается ли корень выражения на пакет ресурсов, и если это так, попытаться получить значение из пакета и запустить событие, если элемент не найден. Это бы сработало, но это кажется излишним, поскольку каждое выражение EL в конечном итоге проходит через распознаватель.
Помните, что для доступа к строкам ресурсов в EL используется синтаксис # {bundlename.key}, и классы Java Map также могут быть доступны с использованием того же синтаксиса. Что мы можем сделать, это создать именованный компонент, который (едва) реализует интерфейс карты, а в методе get он получает значение из введенного пакета ресурсов. Это означает, что выражения EL будут использовать наш компонент Map, и мы можем делегировать вызов поставщику сообщений, который будет обрабатывать пропущенные выражения.

  1. Начните с создания нового компонента, который реализует интерфейс Map. Большинство методов будут генерировать исключения, которые не реализованы, нам важен только метод получения значений.
    @Named("messages")
    @RequestScoped
    public class ResourceMap implements Map<String, String> {

    @Inject
    private MessageProvider provider;

    @Override
    public int size() {
    return 0;
    }

    @Override
    public boolean isEmpty() {
    return false;
    }

    @Override
    public boolean containsKey(Object key) {
    return provider.getBundle().containsKey((String) key);
    }

    @Override
    public boolean containsValue(Object value) {
    throw new RuntimeException("Not implemented");
    }

    @Override
    public String get(Object key) {
    return provider.getValue((String) key);
    }

    @Override
    public String put(String key, String value) {
    throw new RuntimeException("Not implemented");
    }

    @Override
    public String remove(Object key) {
    throw new RuntimeException("Not implemented");
    }

    ....
    ....
    ....
    }

    Если метод интерфейса отсутствует в списке, предположим, что он генерирует исключение «не реализовано». Мы внедряем экземпляр MessageProvider, чтобы мы могли повторно использовать наш компонент поставщика сообщений для получения сообщений и запуска событий.

  2. Мы дали бину имена сообщений, поэтому, если мы перейдем на нашу веб-страницу и добавим на нашу страницу следующее:
    First name from map = #{messages.firstName}<br/>
    Missing name from map = #{messages.missingValue}<br/>

    Когда мы запускаем наше приложение и обновляем страницу, мы получаем значение, отображаемое для messages.firstName, и пропущенное значение для messages.missingValue. В нашем журнале сервера мы получаем

    Oh noes! missingValue key is missing in locale English (United States)

    Поскольку мы повторно использовали bean-компонент MessageProvider, он запускает событие, когда не может найти значение ключа, поэтому, даже несмотря на то, что мы вызвали его со страницы JSF, он в конечном итоге был перенаправлен нашему поставщику сообщений и вызвало событие.

То, как вы используете это, зависит от вас, но поскольку синтаксис одинаков, независимо от того, используете ли вы переменную Map или JSF, вы можете переключаться между ними в зависимости от того, является ли это средой разработки или производственной средой. Вы также можете использовать разные обработчики событий в зависимости от среды развертывания. В качестве альтернативы вы можете беспокоиться о производительности на производстве или о том, что вы потеряете автодополнение, если будете использовать карту в разработке.
Для переключения реализаций просто задайте либо переменную ресурса JSF в файле Face-Config, либо компонент EJB MessageProvider, используемый для компонента строки ресурса. Вы можете переключать компоненты без необходимости изменения кода или страниц JSF.

Загрузите исходный код Maven для этого проекта и запустите его локально, напечатав его разархивировав и запустив mvn clean jetty: run в командной строке и перейдя по адресу http: // localhost: 8080 / resourcedemo / .

С http://www.andygibson.net/blog/article/handling-missing-resources-with-cdi-events/