2016-01-29 1 views
5

У нас есть приложение JSF на WildFly 8, которое использует традиционный механизм с интернационализацией текста, имея пакеты сообщений для немецкого и английского языков в папке WEB-INF\classes WAR и конфигурацию в faces-config.xml, сопоставляя ему имя и список локалей. Приложение не имеет подключения к базе данных, но использует службы REST для связи со вторым приложением.Как разместить JSF-сообщение за пределами WAR, чтобы его можно было редактировать без перераспределения?

Теперь нам нужно иметь возможность изменять текст более легко, то есть не нужно создавать новый WAR-файл и выполнять развертывание при смене текста. Поэтому мне нужен механизм, чтобы иметь пакеты сообщений за пределами WAR, имея возможность использовать его, как и раньше, на страницах XHTML.

Два необязательных требования состоят в том, чтобы изменить текст и обновить сообщения в приложении без необходимости перезапуска приложения (приоритет 2) и иметь пул по умолчанию в WAR, который перезаписывается внешним пакетом (приоритет 3).

Моя мысль заключалась в том, чтобы использовать что-то вроде конфигурации сообщества Apache, чтобы прочитать файл свойств в компоненте с областью действия приложения и выставить геттер под именем EL, которое использовалось ранее. Но почему-то это похоже на необходимость повторного внедрения существующего механизма и что это должно быть как-то проще, может быть, даже с ядром Java EE.

Кто-нибудь использовал этот механизм таким образом и может указать мне на какой-то пример/описание деталей или лучше реализовать перечисленные требования?

+0

Полезно? http://stackoverflow.com/q/4499732 – BalusC

+0

@BalusC Ну, не рассматривали детали вопроса, когда спотыкались раньше, поскольку это относится к обработке его через базу данных, чего у меня здесь нет, но я думаю вы имеете в виду часть расширения «ResourceBundle»? Итак, в 'getItSomehow'part он должен быть загружен с помощью операции с файлом? В этом случае это может быть способ справиться с этим. Только два необязательных требования здесь не ясны. –

+0

@BalusC Ok, 2) имеет смысл, 1) может быть неправильно понято - мне не нужно отражать изменения обратно в файл, но иметь возможность изменять файл, а затем запускать перезагрузку пакета. - Если вам нравится тратить время на то, чтобы ответить на комментарии, я рад назначить награду. –

ответ

6

Как разместить комплект сообщений JSF вне WAR?

Два способа:

  1. Add its path to the runtime classpath of the server.

  2. Create a custom ResourceBundle implementation with a Control.


изменить текст и обновления сообщений в приложении без необходимости перезапуска приложения

Изменение текста будет тривиальным. Тем не менее, обновление не является тривиальным. Mojarra внутренне кэширует его агрессивно. Это нужно учитывать, если вы хотите идти в путь 1. Arjan Tijms опубликовал специальный трюк Mojarra, чтобы очистить его внутренний кэш ресурсов в этом связанном вопросе: How to reload resource bundle in web application?

Если изменение текста происходит в webapp сам, тогда вы могли бы просто выполнить очистку кеша в методе сохранения. Если изменение текста, однако, может произойти извне, тогда вам нужно зарегистрировать file system watch service для прослушивания изменений (tutorial here), а затем либо для способа 1 очистить кэш пакетов, либо для способа 2 перезагрузить внутренне в handleGetObject().


имеет сверток по умолчанию в пределах WAR, который будет перезаписан внешним пучком

При загрузке их пути к классам, по умолчанию поведение наоборот (ресурсы в WAR имеют более высокий приоритет при загрузке), так что это определенно царапины 1 и оставляет нас в пути 2.

Ниже приведен пример использования 2. Это предполагает, что вы используете ресурс ресурса ресурса dles с базовым именем text (т.е. нет пакета) и что внешний путь находится в /var/webapp/i18n.

public class YourBundle extends ResourceBundle { 

    protected static final Path EXTERNAL_PATH = Paths.get("/var/webapp/i18n"); 
    protected static final String BASE_NAME = "text"; 
    protected static final Control CONTROL = new YourControl(); 

    private static final WatchKey watcher; 

    static { 
     try { 
      watcher = EXTERNAL_PATH.register(FileSystems.getDefault().newWatchService(), StandardWatchEventKinds.ENTRY_MODIFY); 
     } catch (IOException e) { 
      throw new ExceptionInInitializerError(e); 
     } 
    } 

    private Path externalResource; 
    private Properties properties; 

    public YourBundle() { 
     Locale locale = FacesContext.getCurrentInstance().getViewRoot().getLocale(); 
     setParent(ResourceBundle.getBundle(BASE_NAME, locale, CONTROL)); 
    } 

    private YourBundle(Path externalResource, Properties properties) { 
     this.externalResource = externalResource; 
     this.properties = properties; 
    } 

    @Override 
    protected Object handleGetObject(String key) { 
     if (properties != null) { 
      if (!watcher.pollEvents().isEmpty()) { // TODO: this is naive, you'd better check resource name if you've multiple files in the folder and keep track of others. 
       synchronized(properties) { 
        try (InputStream input = new FileInputStream(externalResource.toFile())) { 
         properties.load(input); 
        } catch (IOException e) { 
         throw new IllegalStateException(e); 
        } 
       } 
      } 

      return properties.get(key); 
     } 

     return parent.getObject(key); 
    } 

    @Override 
    @SuppressWarnings({ "rawtypes", "unchecked" }) 
    public Enumeration<String> getKeys() { 
     if (properties != null) { 
      Set keys = properties.keySet(); 
      return Collections.enumeration(keys); 
     } 

     return parent.getKeys(); 
    } 

    protected static class YourControl extends Control { 

     @Override 
     public ResourceBundle newBundle 
      (String baseName, Locale locale, String format, ClassLoader loader, boolean reload) 
       throws IllegalAccessException, InstantiationException, IOException 
     { 
      String resourceName = toResourceName(toBundleName(baseName, locale), "properties"); 
      Path externalResource = EXTERNAL_PATH.resolve(resourceName); 
      Properties properties = new Properties(); 

      try (InputStream input = loader.getResourceAsStream(resourceName)) { 
       properties.load(input); // Default (internal) bundle. 
      } 

      try (InputStream input = new FileInputStream(externalResource.toFile())) { 
       properties.load(input); // External bundle (will overwrite same keys). 
      } 

      return new YourBundle(externalResource, properties); 
     } 

    } 

} 

Для того, чтобы заставить его работать, зарегистрируйся ниже в faces-config.xml.

<application> 
    <resource-bundle> 
     <base-name>com.example.YourBundle</base-name> 
     <var>i18n</var> 
    </resource-bundle> 
</application> 
+0

Как usal очень всеобъемлющий, спасибо. –

 Смежные вопросы

  • Нет связанных вопросов^_^