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