2010-08-13 21 views
7

J'ai un fichier de propriétés pour la localisation:JSF 2 localisation (managed bean)

foo=Bar 
title=Widget Application 

Ceci est lié en tant resource-bundle dans les faces-config:

<resource-bundle> 
    <base-name>com.example.messages.messages</base-name> 
    <var>msgs</var> 
</resource-bundle> 

Je peux accéder à ce juste bien dans la facelets vue en utilisant EL:

<title>#{msgs.title}</title> 

Cependant, s'il y a des choses comme SQLExceptions, j'ai besoin être capable d'écrire des messages à partir du bean géré. Tout cela est travaille également:

FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "There was an error saving this widget.", null); 
FacesContext.getCurrentInstance().addMessage(null, message); 

Voici la question: je veux avoir ces messages proviennent des propriétés fichier afin qu'eux aussi, peuvent être modifiés en fonction des paramètres régionaux. Existe-t-il un moyen facile d'accéder au fichier de propriétés en utilisant l'injection?

Répondre

16

j'ai posé une question tout à fait connexe sur le SO: How to inject a non-serializable class (like java.util.ResourceBundle) with Weld

Et à l'intérieur du Forum Seam: http://seamframework.org/Community/HowToCreateAnInjectableResourcebundleWithWeld

Pour résumer: j'ai réalisé un ResourceBundle injectable avec 3 Les producteurs Vous devez d'abord avoir un FacesContextProducer. J'ai pris celui des sources Seam 3 Alpha.

public class FacesContextProducer { 
    @Produces @RequestScoped 
    public FacesContext getFacesContext() { 
     FacesContext ctx = FacesContext.getCurrentInstance(); 
     if (ctx == null) 
     throw new ContextNotActiveException("FacesContext is not active"); 
     return ctx; 
    } 
} 

Ensuite, vous avez besoin d'un LocaleProducer, qui utilise le FacesContextProducer. Je l'ai aussi pris à Seam 3 Alpha.

public class FacesLocaleResolver { 
    @Inject 
    FacesContext facesContext; 

    public boolean isActive() { 
     return (facesContext != null) && (facesContext.getCurrentPhaseId() != null); 
    } 

    @Produces @Faces 
    public Locale getLocale() { 
     if (facesContext.getViewRoot() != null) 
     return facesContext.getViewRoot().getLocale(); 
     else 
     return facesContext.getApplication().getViewHandler().calculateLocale(facesContext); 
    } 
} 

Maintenant, vous avez tout à créer un ResourceBundleProducer, ce qui peut ressembler à ceci:

public class ResourceBundleProducer { 
    @Inject  
    public Locale locale; 

    @Inject  
    public FacesContext facesContext; 

    @Produces 
    public ResourceBundle getResourceBundle() { 
    return ResourceBundle.getBundle("/messages", facesContext.getViewRoot().getLocale()); 
    } 
} 

Maintenant vous pouvez @Inject ResourceBundle dans vos haricots. Faites attention qu'il doit être injecté dans un attribut transitoire, sinon vous obtiendrez une exception se plaignant que ResourceBundle n'est pas sérialisable.

@Named 
public class MyBean { 
    @Inject 
    private transient ResourceBundle bundle; 

    public void testMethod() { 
    bundle.getString("SPECIFIC_BUNDLE_KEY"); 
    } 
} 
0

Voici un exemple sur la façon de le faire: http://www.laliluna.de/articles/javaserver-faces-message-resource-bundle-tutorial.html

Vous voulez jeter un oeil à la partie ResourceBundle.getBundle().

Salutations, Lars

+0

J'ai vu cela quand je l'ai googlé. Cependant, y a-t-il une façon plus élégante d'injecter le conteneur en utilisant @Resource ("# {msgs}") ou quelque chose comme ça? Je suppose, puisque j'utilise CDI, que je pourrais créer un producteur de '@ MessageBundle' ou quelque chose, et ensuite passer juste un objet' Properties' ... –

+0

J'ai utilisé cette approche dans un de nos derniers projets - nous avions le même problème avec les erreurs de DB. Je peux jeter un oeil à l'ancienne source mercredi mercredi si cela n'est toujours pas résolu. – Lars

+0

Vous avez raison de dire que c'est une façon valable de le faire. Je me demandais juste s'il y avait un moyen de le faire plus élégamment. Je peux juste utiliser CDI pour l'injecter. Cela fonctionnera s'il n'y a pas d'annotation intégrée. –

2

Vous pouvez le faire avec JSF seul.

Commencez par définir une propriété gérée sur votre bean backing. Dans la configuration JSF, vous pouvez définir la valeur de la propriété gérée sur une expression EL qui référence votre regroupement de ressources. J'ai fait quelque chose comme ce qui suit en utilisant Tomcat 6. Le seul inconvénient est que vous ne pouvez pas accéder à cette valeur depuis le constructeur de votre bean backing car JSF ne l'a pas encore initialisé.Utilisez @PostConstruct sur une méthode d'initialisation si la valeur est nécessaire au début du cycle de vie du bean.

<managed-bean> 
    ... 
    <managed-property> 
    <property-name>messages</property-name> 
    <property-class>java.util.ResourceBundle</property-class> 
    <value>#{msgs}</value> 
    </managed-property> 
    ... 
</managed-bean> 

<application> 
    ... 
    <resource-bundle> 
    <base-name>com.example.messages.messages</base-name> 
    <var>msgs</var> 
    </resource-bundle> 
    ... 
</application> 

Cela a l'avantage de rendre moins votre support méthodes de haricots dépendant de la technologie de présentation, il devrait donc être plus facile à tester. Il découpe également votre code à partir de détails tels que le nom donné à l'ensemble.

Certains tests effectués avec Mojarra 2.0.4-b09 montrent une légère incohérence lorsqu'un utilisateur modifie les paramètres régionaux en cours de session. Les expressions EL sur la page utilisent le nouvel environnement local, mais le nouveau bean de sauvegarde ne reçoit pas la nouvelle référence ResourceBundle. Pour le rendre cohérent, vous pouvez utiliser la valeur de la propriété bean dans les expressions EL, par exemple en utilisant #{backingBean.messages.greeting} à la place de #{msgs.greeting}. Ensuite, la page EL et le bean backing utiliseront toujours les paramètres régionaux actifs au début de la session. Si les utilisateurs avaient pour basculer entre les sessions locales et recevoir les nouveaux messages, vous pourriez essayer de créer un bean de portée requête et lui donner des références au bean de session et au regroupement de ressources.

+0

+1 De loin la solution la plus simple – klonq

+2

Dans JSF2, vous pouvez simplement utiliser '@ManagedProperty (" # {msgs} ") private ResourceBundle msgs;' (avec setter). – BalusC

0

Ceci est une vieille question mais j'ajoute une autre manière de faire ceci. Je cherchais quelque chose d'autre et j'ai traversé ça. Les méthodes ici semblaient compliquées pour quelque chose qui ne me semble pas aussi difficile. Jouer avec des paillettes parce que c'est joli si tu me le demandes.

Compte tenu d'un fichier:

/com/full/package/path/to/messages/errormessages.properties 

A l'intérieur du fichier:

SOME_ERROR_STRING=Your App Just Cratered 

Je crée une méthode "getBundle()" depuis que j'aime prendre le temps d'exécution et ajouter un message significatif alors je vais comprendre d'où ça vient. Pas dur et peut aider si vous avez le caprice de jouer avec les fichiers de propriétés pour une raison quelconque et ne pas mettre à jour tout correctement. Je rends parfois cela privé car c'est parfois une méthode d'aide dans une classe (pour moi). Cela permet à l'essai d'arrêter le fouillis du code significatif. L'utilisation du chemin d'accès complet au fichier vous permet de le placer ailleurs que dans le répertoire/emplacement par défaut si vous avez d'autres idées sur l'organisation.

public/private ResourceBundle getMessageResourceBundle(){ 
    String messageBundle = "com.full.package.path.to.messages.errormessages"; 
    ResourceBundle bundle = null; 
    try{ 
     bundle = ResourceBundle.getBundle(messageBundle); 
    }catch(MissingResourceException ex){ 
     Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, 
        "Unable to find message bundle in XYZ Class", ex); 
     throw ex; 
    } 

} 

public void doSomethingWithBundle(){ 

    ResourceBundle bundle = getMessageResourceBundle(); 
    String someString = bundle.getString("SOME_ERROR_STRING"); 
    ... 
}