Come mettere il pacchetto di messaggi JSF fuori da WAR?
due modi:
Add its path to the runtime classpath of the server.
Create a custom ResourceBundle
implementation with a Control
.
modificare il testo e aggiornare i messaggi nell'applicazione senza dover riavviare l'applicazione
Modifica il testo sarà banale. Tuttavia, l'aggiornamento non è banale. Mojarra lo memorizza internamente in modo aggressivo. Questo deve essere preso in considerazione nel caso in cui si desideri andare per la via 1. Arjan Tijms ha pubblicato un trucco specifico di Mojarra per cancellare la sua cache interna del bundle di risorse in questa domanda correlata: How to reload resource bundle in web application?
Se la modifica del testo avviene nella webapp da solo, quindi è possibile semplicemente eseguire la pulizia della cache nel metodo di salvataggio. Se la modifica del testo può tuttavia avvenire esternamente, è necessario registrare un file system watch service per ascoltare le modifiche (tutorial here) e quindi per il modo 1 svuotare la cache del bundle o per il modo 2 ricaricare internamente in handleGetObject()
.
hanno un fascio predefinita nel WAR, che viene sovrascritto dal fascio esterno
Quando caricandoli da classpath, il comportamento predefinito è viceversa (risorse in WAR avere precedenza più alta sulla classe), quindi questo sicuramente scalfisce il modo 1 e ci lascia con il modo 2.
Di seguito è riportato un esempio di kickoff del modo 2. Ciò presuppone che si stia utilizzando la risorsa di proprietà bun dles con un nome di base text
(ad es. nessun pacchetto) e che il percorso esterno si trova in /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);
}
}
}
Al fine di farlo funzionare, registrarsi come di seguito in faces-config.xml
.
<application>
<resource-bundle>
<base-name>com.example.YourBundle</base-name>
<var>i18n</var>
</resource-bundle>
</application>
È utile? http://stackoverflow.com/q/4499732 – BalusC
@BalusC Beh, non ho esaminato i dettagli della domanda quando ci siamo imbattuti prima, in quanto si riferisce alla gestione tramite database, che non ho qui - ma credo ti riferisci alla parte di estendere il 'ResourceBundle'? Quindi nel 'getItSomehow'part deve quindi essere caricato tramite l'operazione sui file? In tal caso potrebbe essere un modo per gestirlo. Qui non sono chiari solo i due requisiti opzionali. –
@BalusC Ok, 2) ha senso, 1) è forse frainteso - Non ho bisogno di riflettere le modifiche al file, ma essere in grado di cambiare il file e quindi attivare una ricarica del bundle. - Se ti piace passare il tempo a mettere i commenti in una risposta, sono felice di assegnare la taglia. –