В первой части мы создали простое приложение, которое использовало комплекты строковых ресурсов в JSF, а во второй части мы расширили его, используя CDI для внедрения поставщика ресурсов в бины, чтобы мы могли повторно использовать наш код для доступа к строковым ресурсам, зависящим от локали. ,
Одним из преимуществ использования CDI для внедрения ваших bean-компонентов вместо простого их создания вручную является возможность использовать механизм событий внутри этих bean-компонентов. В этом примере мы запустим событие, когда обнаружим, что ресурс отсутствует.
- Начните с создания класса 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;
}
} - Мы запускаем событие, когда обнаруживаем, что в используемом файле свойств локали отсутствует ключ ресурса. В классе 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;
}
} - Наконец, мы хотим добавить наблюдателя для этого события, которое обрабатывает его при его запуске. Для этого мы добавим новый класс для этой цели.
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);
}
} - Наконец, чтобы увидеть его в действии, нам нужно добавить средство получения bean-компонента, чтобы извлечь несуществующее значение из пакета.
@Named
@RequestScoped
public class AnotherBean {
public String getMissingResource() {
return provider.getValue("ImNotHere");
}
} - Мы можем увидеть это в действии, добавив к нашей странице 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, и мы можем делегировать вызов поставщику сообщений, который будет обрабатывать пропущенные выражения.
- Начните с создания нового компонента, который реализует интерфейс 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, чтобы мы могли повторно использовать наш компонент поставщика сообщений для получения сообщений и запуска событий.
- Мы дали бину имена сообщений, поэтому, если мы перейдем на нашу веб-страницу и добавим на нашу страницу следующее:
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/