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