users@jersey.java.net

Re: [Jersey] Date format based on request parameter

From: Chris Carrier <ctcarrier_at_gmail.com>
Date: Fri, 2 Apr 2010 11:46:57 -0700

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
>>
>>
>