Настройка архитектуры для постоянно меняющегося текста, файлов свойств?В среде Java EE - PullRequest
3 голосов
/ 25 апреля 2011

В среде Java EE мы обычно используем для хранения текста в файле свойств / ресурсов. И этот файл свойств связан с некоторым файлом HTML разметки представления. Например. если ваша метка «Имя» меняется на «Полное имя» на странице HTML, вы можете использовать это свойство для этого обновления.

firstName=First Name
someOtherData=This is the data to display on screen, from property file

Если вы находитесь в среде, где сложно регулярно обновлять эти файлы свойств, какую архитектуру используют разработчики для изменения содержимого текста / метки, которое обычно находится в файле свойств? Или, скажем, вам нужно изменить это содержимое перед повторным использованием изменения файла свойств. Плохое решение - хранить это в базе данных? Разработчики используют memcache? Это обычно используется для кэширования решений?

Edit-1 База данных действительно не предназначена для задач такого типа (вытягивание текста для отображения на экране), но есть варианты использования для базы данных. Я могу добавить языковой столбец или поле состояния, а также добавить фильтр столбцов по группам. Если я не использую базу данных или файл свойств, какое решение с распределенным ключом / значением позволит мне добавить пользовательские фильтры?

Edit-2 Вы бы использовали решение вне Java-фреймворка? Как хранилище данных ключ / значение? memcachedb

Ответы [ 5 ]

11 голосов
/ 02 мая 2011

Я хочу заверить вас, что если вам нужны постоянные изменения в локализованных текстах, например, они, как правило, отличаются от развертывания к развертыванию, база данных - это путь.Ну, не только база данных, вам нужно как-то кэшировать ваши строки.И, конечно, вы бы не хотели полностью перестраивать свой уровень доступа к ресурсам, я полагаю.

Для этого я могу предложить расширить класс ResourceBundle для автоматической загрузки строк из базы данных и сохранения их в WeakHashMap,Я выбираю WeakHashMap из-за его особенностей - он удаляет ключ с карты, когда он больше не нужен, уменьшая объем памяти.В любом случае вам нужно создать класс доступа.Поскольку вы упомянули J2EE, довольно древнюю технологию, я приведу вам пример, совместимый с Java SE 1.4 (он может быть легко переработан для более новой Java, просто поместите @Override при необходимости и добавьте некоторое обобщение строк в Enumeration):

public class WeakResourceBundle extends ResourceBundle {
    private Map cache = new WeakHashMap();
    protected Locale locale = Locale.US; // default fall-back locale

    // required - Base is abstract
    // @Override
    protected Object handleGetObject(String key) {
        if (cache.containsKey(key))
            return cache.get(key);

        String value = loadFromDatabase(key, locale);
        cache.put(key, value);

        return value;
    }

    // required - Base is abstract
    // @Override
    public Enumeration getKeys() {
        return loadKeysFromDatabase();
    }

    // optional but I believe needed
    // @Override
    public Locale getLocale() {
        return locale;
    }

    // dummy testing method, you need to provide your own
    // should throw MissingResourceException if key does not exist
    private String loadFromDatabase(String key, Locale aLocale) {
        System.out.println("Loading key: " + key
                + " from database for locale:"
                + aLocale );

        return "dummy_" + aLocale.getDisplayLanguage(aLocale);
    }

    // dummy testing method, you need to provide your own
    private Enumeration loadKeysFromDatabase() {
        return Collections.enumeration(new ArrayList());
    }
}

Из-за каких-то странных правил загрузки ResourceBundle вам действительно нужно расширить класс WeakResourceBundle, чтобы создать один класс для поддерживаемых языков:

// Empty Base class for Invariant Language (usually English-US) resources
// Do not need to modify anything here since I already set fall-back language
package com.example.i18n;

public class MyBundle extends WeakResourceBundle {

}

Каждый поддерживаемый язык (я знаю это)отстой):

// Example class for Polish ResourceBundles
package com.example.i18n;

import java.util.Locale;

public class MyBundle_pl extends WeakResourceBundle {

    public MyBundle_pl() {
        super();
        locale = new Locale("pl");
    }
}

Теперь, если вам нужно создать экземпляр вашего ResourceBundle, вам нужно всего лишь позвонить:

// You probably need to get Locale from web browser
Locale polishLocale = new Locale("pl", "PL");
ResourceBundle myBundle = ResourceBundle.getBundle(
                "com.example.i18n.MyBundle", polishLocale);

И получить доступ к ключу:

String someValue = myBundle.getString("some.key");

Возможные ошибки:

  1. ResourceBundle требует Полное имя класса (следовательно, имя пакета).
  2. Если вы опустите параметр Locale, по умолчанию (что означает сервер)Locale будет использоваться.Обязательно всегда передайте Locale, тогда как экземпляр ResourceBundle.
  3. myBundle.getString() может выдать MissingResourceException, если вы последуете моему предложению.Вам нужно будет использовать блок try-catch, чтобы избежать проблем.Вместо этого вы можете принять решение о возврате некоторой фиктивной строки из слоя доступа к базе данных в случае отсутствия ключа (например, return "!" + key + "!"), но в любом случае это, вероятно, должно быть зарегистрировано как ошибка.
  4. Вы всегда должны пытаться создать Localeобъекты, передающие как язык, так и код страны.Это потому, что такие языки, как китайский упрощенный (zh_CN) и китайский традиционный (zh_TW), например, являются совершенно разными языками (по крайней мере, с точки зрения письменности), и вам необходимо будет поддерживать их два вида.Для других стран ResourceBundle фактически автоматически загрузит правильный языковой ресурс (обратите внимание, что я создал MyBundle_pl.java, а не MyBundle_pl_PL.java, и он все еще работает. Кроме того, ResourceBundle автоматически переключится на Enlish-US (MyBundle.java), если там есть).не является классом ресурсов для данного языка (именно поэтому я использовал такую ​​странную иерархию классов).

РЕДАКТИРОВАТЬ

Некоторые случайные мысли о том, какчтобы сделать его более потрясающим .

Статическими фабриками (избегайте прямого использования ResourceBundle)

Вместо того, чтобы напрямую создавать экземпляры пакетов с помощью ResourceBundle, вы можете добавитьстатические фабричные методы:

public static ResourceBundle getInstance(Locale aLocale) {
    return ResourceBundle.getBundle("com.example.i18n.MyBundle", aLocale);
}

Если вы решите изменить имя класса WeakResourceBundle на что-то более подходящее (я решил использовать LocalizationProvider), теперь вы можете легко создавать экземпляры своих пакетов изкод потребления:

ResourceBundle myBundle = LocalizationProvider.getInstance(polishLocale);

автоматически сгенерированные классы ресурсов

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

Автоопределение локали

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

Реализация дополнительных методов, связанных с локализацией

В локализованном приложении есть типичные задачи, которые необходимо выполнить - форматирование и разбор дат, чисел, валют и т. Д. Являются обычными примерами. Имея локаль конечного пользователя, вы можете просто обернуть такие методы в LocalizationProvider.

Ну и дела, я действительно люблю моя работа:)

4 голосов
/ 28 апреля 2011

Вы говорите о файлах свойств, но во время выполнения у вас, скорее всего, будет комплект ресурсов или что-то, что будет хотеть список пар ключ / значение (возможно, даже в зависимости от локали)

Вы можете хранить данные в любом формате, а затем использовать их для создания с ним нужного пакета ресурсов. Даже если это исходит из памяти. Таким образом, база данных может прекрасно это делать, потому что все свойства будут загружаться при запуске, кэшироваться в памяти JVM и все. (ВЫБРАТЬ * ИЗ LOCALIZATION_DATA)

Не используйте распределенный кеш для этого, у вас, скорее всего, небольшой набор данных ... что? Может быть, несколько МБ в худшем случае. И доступ к этим данным должен быть мгновенным после загрузки, потому что все представления будут вызывать доступ к ним десятки или даже сотни раз на страницу.

Если вы хотите обновить данные без перезапуска приложения, просто добавьте где-нибудь экран администрирования с «перезагрузкой данных локализации» или даже экран, который позволяет обновить данные этого типа (но сохранить в файл / БД / что угодно) )

С точки зрения рабочего процесса, это зависит от того, чего вы пытаетесь достичь.

Классический файл свойств является предпочтительным способом сделать это. Вы помещаете его в версию, вместе с исходным кодом, чтобы у вас всегда был актуальный перевод с кодом. Вы хотите отладить V1.3.0.1? просто получите эту версию, и вы будете использовать файл свойств, который использовался в это время. Вы добавили новый код, который требует новых ключей? Или просто поменяли им ключевое имя по какой-то причине? Вы знаете, что код и информация о вашем местонахождении связаны в единое состояние. И это автоматически.

Если ваши данные не находятся под контролем версий, вы теряете автоматическое управление версиями и историю ваших данных. При развертывании / повторном развертывании на новом компьютере может возникнуть несоответствие и даже помешать правильной работе приложения (если новый ключ требуется, но не добавлен. Это не очень хорошо, подвержено ошибкам и дополнительным ручным вмешательствам.

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

Если изменение на сервере является просто настройкой одним выстрелом, тогда вы просто переходите на нужную веб-страницу администратора, используете экран настройки данных локализации и все.

Если изменение необходимо сохранить для любой новой установки, добавьте его 2 раза. Один раз на сервере, один раз в управлении версиями.

1 голос
/ 06 мая 2011

По просьбе Берлина Брауна я добавляю еще один ответ, более сфокусированный на его конкретных потребностях:

Из объема данных, которые вам нужны (например, тысячазаписей), вам просто нужно загрузить файл свойств при запуске, указав удаленный URL.

Данные кэшируются в памяти JVM для максимальной производительности.

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

Нет необходимости в базе данных.Нет необходимости в memcached, нет необходимости в NoSQL.простой URL-адрес, доступный с рабочего сервера.С точки зрения безопасности и разработки это проще, быстрее и более гибко.

Детали реализации: если вы используете стандартный формат, у вас будет файл на язык / страну.Не забудьте обновить для всех языков или связать их вместе (используя zip для примера).

1 голос
/ 04 мая 2011

Не уверен, что база данных не может справиться с этим, я думаю, что вы действительно ищете кеш, который можно сделать недействительным при изменении этих свойств. Задумывались ли вы об использовании чего-то вроде JBoss Infinispan (http://www.jboss.org/infinispan)?). Он чрезвычайно прост в использовании и может быть распределен по нескольким серверам приложений.

Infinispan, после настройки, может использоваться как карта; имейте в виду, что вы можете настроить его распределение по кластеру!

Если вы не возражаете против использования решения NoSQL, я бы порекомендовал что-то вроде Redis или Memcache. Конечно, я бы посоветовал вам хранить локальный кеш (зачем платить за сетевой вызов, особенно если эти свойства вряд ли будут часто меняться?).

1 голос
/ 25 апреля 2011

Вы всегда можете использовать JNDI или даже рассмотреть хранилище документов, такое как JCR, для такого рода вещей.

...