users@jersey.java.net

[Jersey] Re: Date format based on request parameter

From: Bryon Jacob <bryon_at_jacobtx.net>
Date: Fri, 11 Mar 2011 11:23:49 -0600

Aah, never mind - I think a few more minutes of Googling and I have answered
my own question.

This link (
http://www.tikalk.com/java/forums/injecting-request-scope-beans-singletons-with-resteasy-and-spring)
led me to realize that I didn't fully understand Spring's
RequestContextListener (
http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/web/context/request/RequestContextListener.html
)
and that it proxies request-scoped attributes, protecting me from exactly
the sort of shenanigans I was worried about below.

The link is about RestEasy/Spring - but I assume that since the relevant
code is part of Spring's stack it applies equally well to a Jersey/Spring
solution.


On Fri, Mar 11, 2011 at 11:11 AM, Bryon Jacob <bryon_at_jacobtx.net> wrote:

> I am trying to do something very similar, and I have a question about the
> validity of this approach -
>
> As nearly as I can tell, the scope of any Providers whose lifecycle is
> owned by Spring is "singleton", but when you inject the UriInfo below with
> an @Context annotation, that is at the request scope. Doesn't that make
> this provider itself not thread-safe? (i.e. any two requests can have a
> race condition on injecting the UriInfo and calling the getContext method)
>
> I hope that I'm wrong, and you can explain why this approach does NOT
> create a race condition between requests, as this strategy will do exactly
> what I needed, if it's safe!
>
> On Mon, Apr 12, 2010 at 3:00 AM, Paul Sandoz <Paul.Sandoz_at_sun.com> wrote:
>
>> Hi,
>>
>> Nice! good use of Spring DI.
>>
>> Be careful about caching Marshaller instances as they are not thread safe.
>> You will need to store such instances in thread local variables. Unless you
>> are really concerned about performance and have empirical data indicating
>> this is a performance issue for your system i would not bother with the
>> additional complexity.
>>
>> Paul.
>>
>> On Apr 2, 2010, at 8:46 PM, Chris Carrier wrote:
>>
>> So in case anyone's curious I got a solution working. Since I'm using
>>> Spring integrated with Jersey I was able to get this working fairly
>>> simply. I already have a JAXBContext ContextResolver implemented so I
>>> just implemented a new ContextResolver like this:
>>>
>>> @Component
>>> @Provider
>>> public class JaxbMarshallerConfig implements ContextResolver<Marshaller>
>>> {
>>>
>>> private static String HUMAN_READABLE_DATE_STRING =
>>> "humanReadableDates";
>>>
>>> @Autowired
>>> private ContextResolver<JAXBContext> jaxbContextResolver;
>>>
>>> @Context
>>> UriInfo uriInfo;
>>>
>>> public JaxbMarshallerConfig() throws JAXBException {
>>>
>>> }
>>>
>>> @Override
>>> public Marshaller getContext(Class<?> type) {
>>> Marshaller marshaller = null;
>>> try {
>>> marshaller =
>>> jaxbContextResolver.getContext(type).createMarshaller();
>>> DateAdapter dateAdapter = new DateAdapter();
>>>
>>> MultivaluedMap<String, String> params =
>>> uriInfo.getQueryParameters();
>>> if (params.containsKey(HUMAN_READABLE_DATE_STRING)){
>>> Boolean humanReadableDates =
>>> Boolean.parseBoolean(params.getFirst(HUMAN_READABLE_DATE_STRING));
>>> dateAdapter.setHumanReadable(humanReadableDates);
>>> }
>>>
>>> marshaller.setAdapter(DateAdapter.class, dateAdapter);
>>> } catch (JAXBException e) {
>>> //i can't throw a checked exception because of the interface
>>> //but if this exception is thrown something is probably
>>> seriously wrong
>>> throw new RuntimeException(e);
>>> }
>>>
>>> return marshaller;
>>> }
>>> }
>>>
>>> I might clean bits of it up but this alone seems to work pretty well.
>>> Also I could easily cache my two different Marshaller implementations
>>> in a Map to avoid the overhead of instantiating all the stuff over and
>>> over again. Now any of my domain classes that I annotate to use my
>>> DateAdapter automagically inherit the ability to send a
>>> 'humanReadableDates' query parameter to control the format of the
>>> returned dates.
>>>
>>> Thanks for the help!
>>> Chris
>>>
>>> On Fri, Apr 2, 2010 at 9:21 AM, Chris Carrier <ctcarrier_at_gmail.com>
>>> wrote:
>>>
>>>> Wow Paul thanks for the extensive example. I'm gonna have to absorb
>>>> that and give it a shot but I'll let you know how it works.
>>>>
>>>> Chris
>>>>
>>>> On Fri, Apr 2, 2010 at 2:00 AM, Paul Sandoz <Paul.Sandoz_at_sun.com>
>>>> wrote:
>>>>
>>>>> Hi Chris,
>>>>>
>>>>> If you want to share the JSONJAXBContext instance between multiple
>>>>> ContextResolver classes then you could do the following:
>>>>>
>>>>> public class MyApp extends PackagesResourceConfig {
>>>>> public MyApp() {
>>>>> super("my.package", "my.other.package");
>>>>>
>>>>> JSONJAXBContext c = ...
>>>>> getSingletons().add(new MarshallingCR(c));
>>>>> getSingletons().add(new JAXBContextCR(c));
>>>>> }
>>>>> }
>>>>>
>>>>> public class Base {
>>>>> protected final JAXBContext c;
>>>>> protected final et<Class<?>> types;
>>>>>
>>>>> protected Base(JAXBContext c, Set<Class<?>> types) {
>>>>> this.c = c;
>>>>> this.types = types;
>>>>> }
>>>>>
>>>>> protected boolean supports(Class<?> type) {
>>>>> return types.contains(type);
>>>>> }
>>>>> }
>>>>>
>>>>> public class MarshallingCR implements ContextResolver<Marshaller>
>>>>> extends
>>>>> Base {
>>>>>
>>>>> @Override
>>>>> public Marshaller getContext(Class<?> type) {
>>>>> if (!supports(type)) return null;
>>>>>
>>>>> ...
>>>>> }
>>>>> }
>>>>>
>>>>> public class JAXBContextCR implements ContextResolver<JAXBContext>
>>>>> extends
>>>>> Base {
>>>>> @Override
>>>>> public JAXBContext getContext(Class<?> type) {
>>>>> if (!supports(type)) return null;
>>>>>
>>>>> ...
>>>>> }
>>>>> }
>>>>>
>>>>> and register your MyApp. That makes things quite clear.
>>>>>
>>>>>
>>>>> I have not tested it, but i think it should be possible to do the
>>>>> following:
>>>>>
>>>>> @Provider
>>>>> public class MarshallingCR extends ContextResolver<Marshaller> {
>>>>> @Context
>>>>> private ContextResolver<JAXBContext> cr;
>>>>>
>>>>> private final DateAdapter da = new DateAdapter();
>>>>>
>>>>> @Override
>>>>> public Marshaller getContext(Class<?> type) {
>>>>> JAXBContext c = cr,getContext(type);
>>>>> if (c == null) return null;
>>>>> Mashaller marshaller = jaxbContext.createMarshaller();
>>>>> marshaller.setAdapter(da);
>>>>> return marshaller;
>>>>> }
>>>>> }
>>>>>
>>>>> but note that the above injected ContextResolver<JAXBContext> will be
>>>>> an
>>>>> aggregation of all such declared providers so the above
>>>>> ContextResolver<Marshaller> will not restrict according to a set of
>>>>> types.
>>>>>
>>>>> Also note that a Marshaler instance is not thread safe so it is not
>>>>> safe to
>>>>> store it. To make things safe you need to store thread local instances.
>>>>>
>>>>> Paul.
>>>>>
>>>>> On Apr 1, 2010, at 7:18 PM, Chris Carrier wrote:
>>>>>
>>>>> Yeah that's what I was thinking. So I have a Marshaller Provider that
>>>>>> looks something like:
>>>>>>
>>>>>> public class JaxbMarshallerConfig implements
>>>>>> ContextResolver<Marshaller> {
>>>>>>
>>>>>> Marshaller marshaller;
>>>>>>
>>>>>> JAXBContext jaxbContext;
>>>>>>
>>>>>> public JaxbMarshallerConfig() throws JAXBException {
>>>>>> this.marshaller = jaxbContext.createMarshaller();
>>>>>> marshaller.setAdapter(new DateAdapter());
>>>>>> }
>>>>>>
>>>>>> @Override
>>>>>> public Marshaller getContext(Class<?> type) {
>>>>>> return null;
>>>>>> }
>>>>>> }
>>>>>>
>>>>>> Now is there a suggestion for how to get my JAXBContext into this
>>>>>> provider? Should I use Jersey's internal injection stuff or is that
>>>>>> not really meant for end users? Like there must be some call that
>>>>>> retrieves a Provider based on type right? Or should I just use Spring
>>>>>> to inject the instance of my JaxbContextResolver into my
>>>>>> MarshallerProvider? I THINK that should work but I'm getting a bit
>>>>>> confused by how all these Providers work.
>>>>>>
>>>>>> Like somewhere in the depths of Jersey there is a call being made to
>>>>>> get an instance of a Marshaller to marshal my objects. What does that
>>>>>> call look like? Is it like (warning pseudocode incoming):
>>>>>>
>>>>>> Marshaller marshaller =
>>>>>> ContextResolver<JAXBContext>.giveMeAJaxbContext.createMarshaller();
>>>>>>
>>>>>> In which case my new Marshaller provider would get ignored anyway. Or
>>>>>> is it like:
>>>>>>
>>>>>> Marshaller marshaller =
>>>>>> ContextResolver<Marshaller>.giveMeAMarshaller();
>>>>>>
>>>>>> In which case I don't see how my JaxbContextResolver was ever used (in
>>>>>> reality it is used obviously). It seems like all i can do is
>>>>>> implement these Providers and throw them against the wall and hope
>>>>>> they stick how I want. But really I have no control over whether they
>>>>>> are used in any particular way or not. Am I just confusing myself at
>>>>>> this point?
>>>>>>
>>>>>> Chris
>>>>>>
>>>>>>
>>>>>>
>>>>>> On Thu, Apr 1, 2010 at 10:03 AM, Paul Sandoz <Paul.Sandoz_at_sun.com>
>>>>>> wrote:
>>>>>>
>>>>>>>
>>>>>>> On Apr 1, 2010, at 6:40 PM, Chris Carrier wrote:
>>>>>>>
>>>>>>> Also I'm a bit confused on how these Providers will interact with
>>>>>>>> each
>>>>>>>> other. So I already have a JAXBContext Provider that configures my
>>>>>>>> JSON format via JSONJAXBContext like:
>>>>>>>>
>>>>>>>> this.context = new
>>>>>>>> JSONJAXBContext(JSONConfiguration.mapped().arrays(...);
>>>>>>>>
>>>>>>>> And returns it in the getContext method. Now I am implementing a
>>>>>>>> Marshaller provider that also has a getContext method but this one
>>>>>>>> returns a Marshaller object. If I define both of these Providers
>>>>>>>> will
>>>>>>>> they play well together? Or am I only able to use one or the other?
>>>>>>>>
>>>>>>>>
>>>>>>> Hmm... i have not tried it but i think it will result in ignoring the
>>>>>>> json
>>>>>>> configuration for marshaling unless you create the Marshaller from
>>>>>>> the
>>>>>>> JSONJAXBContext. As long as you do that you should be OK.
>>>>>>>
>>>>>>> Paul.
>>>>>>>
>>>>>>> Chris
>>>>>>>>
>>>>>>>> On Thu, Apr 1, 2010 at 9:28 AM, Chris Carrier <ctcarrier_at_gmail.com>
>>>>>>>> wrote:
>>>>>>>>
>>>>>>>>>
>>>>>>>>> Great guys thanks for the tips that looks like what I was looking
>>>>>>>>> for.
>>>>>>>>> Is there a master list of available @Providers anywhere? I swear
>>>>>>>>> I
>>>>>>>>> had seen one once in the JAXRS docs but I just spent some time
>>>>>>>>> looking
>>>>>>>>> and now can't find it.
>>>>>>>>>
>>>>>>>>> Thanks!
>>>>>>>>> Chris
>>>>>>>>>
>>>>>>>>> On Thu, Apr 1, 2010 at 3:48 AM, Paul Sandoz <Paul.Sandoz_at_sun.com>
>>>>>>>>> wrote:
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Hi Chris,
>>>>>>>>>>
>>>>>>>>>> You can modify your context resolver to be
>>>>>>>>>> ContextResolver<Marshaller>
>>>>>>>>>>
>>>>>>>>>> And then use the following methods:
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> http://java.sun.com/javase/6/docs/api/javax/xml/bind/Marshaller.html#setAdapter%28java.lang.Class,%20A%29
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> http://java.sun.com/javase/6/docs/api/javax/xml/bind/Marshaller.html#setAdapter%28javax.xml.bind.annotation.adapters.XmlAdapter%29
>>>>>>>>>>
>>>>>>>>>> In your context resolver you can inject UriInfo and thus obtain
>>>>>>>>>> the
>>>>>>>>>> query
>>>>>>>>>> parameter when the request is in scope.
>>>>>>>>>>
>>>>>>>>>> Paul.
>>>>>>>>>>
>>>>>>>>>> On Apr 1, 2010, at 3:03 AM, Chris Carrier wrote:
>>>>>>>>>>
>>>>>>>>>> Hey folks,
>>>>>>>>>>>
>>>>>>>>>>> A little while ago I toiled to find a way to deal with dates
>>>>>>>>>>> being
>>>>>>>>>>> represented as Java timestamps. I finally discovered the ability
>>>>>>>>>>> to
>>>>>>>>>>> use package level annotations to define custom JAXB adapters
>>>>>>>>>>> based on
>>>>>>>>>>> type that I could use to format Dates however I wanted. Well now
>>>>>>>>>>> I
>>>>>>>>>>> have a new requirement to return Date representations based on a
>>>>>>>>>>> parameter in the get request. Like a parameter might be passed
>>>>>>>>>>> into
>>>>>>>>>>> GET calls like
>>>>>>>>>>>
>>>>>>>>>>> ...?humanReadableDates=true
>>>>>>>>>>>
>>>>>>>>>>> And then I return GMT dates or whatever. Otherwise I return
>>>>>>>>>>> timestamps. So I would love to be able to just use my central
>>>>>>>>>>> XmlJavaTypeAdapter so that I can apply this easily to any pojo's
>>>>>>>>>>> I
>>>>>>>>>>> want but the annotation syntax:
>>>>>>>>>>>
>>>>>>>>>>> @XmlJavaTypeAdapter(value = DateAdapter.class, type = Date.class)
>>>>>>>>>>>
>>>>>>>>>>> Suggests that JAXB will instantiate a new DateAdapter whenever it
>>>>>>>>>>> needs one and I have no idea how to get a handle on that instance
>>>>>>>>>>> to
>>>>>>>>>>> do anything with it based on request parameters.
>>>>>>>>>>>
>>>>>>>>>>> So basically I was wondering if anyone has any suggestions for a
>>>>>>>>>>> good
>>>>>>>>>>> way to do this. If there's some trick to get a handle on an
>>>>>>>>>>> XmlJavaTypeAdapter that would work. I could just put some logic
>>>>>>>>>>> in
>>>>>>>>>>> my
>>>>>>>>>>> domain classes so that i can set some Date format instance
>>>>>>>>>>> variable
>>>>>>>>>>> in
>>>>>>>>>>> them to affect the way they represent Dates but that really seems
>>>>>>>>>>> pretty crappy. I would much prefer to keep my marshalling config
>>>>>>>>>>> stuff centralized and out of my pojo's. I've got a Provider that
>>>>>>>>>>> 'implements ContextResolver<JAXBContext>' but I see no way to
>>>>>>>>>>> configure my Date format from here.
>>>>>>>>>>>
>>>>>>>>>>> If I was using plain Jackson I could do
>>>>>>>>>>> 'mapper.getSerializationConfig().setDateFormat(FORMAT)' but at
>>>>>>>>>>> this
>>>>>>>>>>> point we've gone done the road of using JAXB and it would be a
>>>>>>>>>>> bit of
>>>>>>>>>>> a pain to switch.
>>>>>>>>>>>
>>>>>>>>>>> Suggestions very welcome.
>>>>>>>>>>>
>>>>>>>>>>> Thanks!
>>>>>>>>>>> Chris
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> ---------------------------------------------------------------------
>>>>>>>>>>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>>>>>>>>>>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> ---------------------------------------------------------------------
>>>>>>>>>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>>>>>>>>>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>> ---------------------------------------------------------------------
>>>>>>>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>>>>>>>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>> ---------------------------------------------------------------------
>>>>>>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>>>>>>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>> ---------------------------------------------------------------------
>>>>>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>>>>>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>>>>>
>>>>>>
>>>>>
>>>>> ---------------------------------------------------------------------
>>>>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>>>>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>>>>
>>>>>
>>>>>
>>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>>
>>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>
>>
>