2

I've been joyfully using omnifaces' Faces.getLocale() to aquire the locale used by the currently logged in user (which in turn gets this from a <f:view> definition). I really like the fallback approach from view to client to system default locale as it fits the requirements for locale selection in my application:

  1. If a user is logged in, use his language preference (obtained from the backend entity)
  2. If no user preference can be found, use the highest ranking language from the Accept-Languages HTTP header
  3. If no locale has been selected by now, use the system default.

Now I've started using JAX-RS (resteasy implementation) and find it quite difficult to write a service that will provide my backend code with the current user's locale.

I can't use Faces.getLocale(), since that requires a FacesContext which isn't present during JAX-RS request processing.

I can't use the @Context SecurityContext annotation in a @Provider (which would give me the user preferred locale) or @Context HttpHeaders (access to the client locale) since JAX-RS only injects those when it uses the provider itself, not when my backend code instantiates the class.

And I don't want to litter my method signatures with Locale parameters, since virtually everything requires a locale to be present.

To have a concrete example: I have a vcard generator that generates little NOTE fields depending on the user's preferred locale. I can both call the vcard generating method via JSF/EL:

<h:commandLink action="#{vcfGenerator.forPerson(person)}" 
    value="Go" target="_blank" />

And via a REST service:

@GET @Path('person/{id:[1-9][0-9]*}/vcard')
@Produces('text/vcard')
String exportVcard(@PathParam('id') Long personId, @Context HttpHeaders headers) {
    VcfGenerator exporter = Component.getInstance(VcfGenerator) as VcfGenerator

    Person person = entityManager.find(Person, personId)
    if (! person)
        return Response.noContent().build()

    def locale = headers.acceptableLanguages[0] ?: Locale.ROOT
    return exporter.generateVCF(person, locale).toString()
}

This works (VcfGenerator has a set of JSF-only methods that use Faces.getLocale()), but is a pain to maintain. So instead of passing the Locale object, I'd like to say:

Vcard generateVCF(Person person) {
    Locale activeLocale = LocaleProvider.instance().getContext(VcfGenerator.class)
    ResourceBundle bundle = ResourceBundle.getBundle("messages", activeLocale, new MyControl())
    // use bundle to construct the vcard
}

Has anyone done similar work and can share insights?

mabi
  • 5,279
  • 2
  • 43
  • 78
  • So, the flow is basically JSF view <--> JSF backing bean <--> JAX-RS service <--> business service? Why exactly the additional JAX-RS layer? Can't the JSF backing bean just invoke that business service directly? Otherwise you've really got to set a request header or add a request parameter on the JAX-RS web service request yourself. It's namely physically a completely different HTTP request. – BalusC Sep 03 '13 at 15:10
  • @BalusC No, the JSF backing bean is talking to the business service and the JAX-RS service is calling backing bean code, both are independent. I'm looking for a way to have the business service say `LocaleProvider.instance().getContext(this.getClass())` and `LocaleProvider` figuring out where it should get the locale from based on available info. – mabi Sep 03 '13 at 15:31

1 Answers1

0

I know this has been posted a while ago, but as it has not been marked as resolved, here is how I got a workaround working for this specific case:

  • First I got a custom ResourceBundle working, as @BalusC described here: http://balusc.blogspot.fr/2010/10/internationalization-in-jsf-with-utf-8.html
  • Then I updated the constructor in order to detect if a FacesContext is currently being in use, from this :

    public Text() {
        setParent(ResourceBundle.getBundle(BUNDLE_NAME, 
        FacesContext.getCurrentInstance().getViewRoot().getLocale(), UTF8_CONTROL));
    }
    
  • To This:

    public Text() {
        FacesContext ctx = FacesContext.getCurrentInstance();
        setParent(ResourceBundle.getBundle(BUNDLE_NAME, 
        ctx != null ? ctx.getViewRoot().getLocale() : Locale.ENGLISH, UTF8_CONTROL));
    }
    

This now works both in JSF and JAX-RS context.

Hope this help,

Psyx
  • 758
  • 1
  • 9
  • 15
  • Jup, this is the intuitive solution but falls short of requirements 1 and 2 under JAX-RS? – mabi Apr 23 '14 at 14:33