users@jersey.java.net

Re: [Jersey] Date format based on request parameter

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Mon, 12 Apr 2010 10:00:15 +0200

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
>