dev@jsr311.java.net

RE: JSR311: ProviderFactory API needs a bit refactoring?

From: Liu, Jervis <jliu_at_iona.com>
Date: Wed, 7 Nov 2007 02:28:27 -0500

> -----Original Message-----
> From: Marc.Hadley_at_Sun.COM [mailto:Marc.Hadley_at_Sun.COM]
> Sent: 2007?11?7? 0:31
> To: dev_at_jsr311.dev.java.net
> Subject: Re: JSR311: ProviderFactory API needs a bit refactoring?
>
>
> Thanks for bringing this up, we were about to run into a similar
> problem fixing issue 20[1] in Jersey. I've created issue 25[2] to
> track it.
>
> I agree that we need to tweak the SPI to support selection by media
> type and read vs write but I think there's a potential subtle
> problem
> with the method signature you suggest: its possible to obtain an
> entity provider that supports a particular media type for
> read but its
> not clear from the API what expectation a user of the entity
> provider
> should have wrt writes. E.g. if I get an entity provider that reads
> XML, should I expect that I can use the same provider instance to
> write XML also ? I think there are three ways we could approach this:
>
> 1. Add warnings to the specification and Javadoc that an
> EntityProvider might not support the same media types for read and
> write. Pros: simple, keeps everything in one interface. Cons:
> potentially confusing, doesn't separate reading and writing
> concerns,
> people don't always read warnings.
> 2. Require EntityProviders to support the same media types for read
> and write. Pros: simple, no need for the factory method to
> have a read/
> write parameter. Cons: restrictive, may not always be
> possible to read
> everything you can write or write everything you can read.
> 3. Split the EntityProvider SPI into two interfaces (e.g.
> EntityReader<T> and EntityWriter<T>),and have two methods in
> ProviderFactory (createEntityReader(Class<T>, MediaType).
> Pros: Keeps
> reading and writing separate, possible to write an entity provider
> that only does one thing, removes potential confusion since there no
> way to try to reuse a writer as a reader or vice verse. Cons: extra
> interface and methods, more complex at first glance.
>
I prefer option 3. This is almost same as createEntityProvider(Class<T> type, MediaType mediaType, boolean isConsumeMime), essentially they both say give me an EntityProvider that supports a particular media type and class type for read (or write). The reason we have to distinguish read and write is because EntityProvider can be annotated to support different media types for read and write by using @ProduceMime/_at_ConsumeMime (of course, the fundamental reason is because, as you already stated, "may not always be possible to read everything you can write or write everything you can read"). And between these two variants, I prefer createEntityReader(Class<T>, MediaType) rather than createEntityProvider(Class<T> type, MediaType mediaType, boolean isConsumeMime), as for the latter, you cant stop users using an EntityProvided that is supposed to be used as read EntityProvider to write.


> BTW, I don't think there's any need for an array of media
> types in the
> createEntityProvider method: for requests the media type is single
> valued, for responses its the responsibility of either the user to
> specify the media type (via Response or using ProduceMime) or the
> client of the ProviderFactory (the runtime) to select a
> specific media
> type from the list of acceptable types.
>
Agree.

> Another question is whether EntityProvider needs a
> supports(MediaType)
> method similar to the existing supports(Class<?> ? This
> could be used
> when the Produce/Consume specifies a wilcard type, e.g. "text/*" or
> could be used to allow a provider to reject types when parameter
> values are not supported ?
>
If the Produce/Consume annotation is the only place that holds information to determine what media types an EntityProvider can support, I would say no. As this supports(MediaType) method would be redundant to what the annotation has already offered. But if you think we need a second layer of "filters" that can further narrow down an EntityProvider from a list of available EntityProviders that have been calculated using Produce/Consume, we have to come out with some valid use cases first. Maybe you can elaborate your example of wildcard of "text/*" or rejection ("This could be used when the Produce/Consume specifies a wildcard type, e.g. "text/*" or could be used to allow a provider to reject types when parameter values are not supported"). I am not sure I have got the idea.

Thanks,
Jervis

> Thoughts, opinions ?
> Marc.
>
> [1] https://jersey.dev.java.net/issues/show_bug.cgi?id=20
> [2] https://jsr311.dev.java.net/issues/show_bug.cgi?id=25
>
> On Nov 6, 2007, at 12:19 AM, Liu, Jervis wrote:
>
> > Hi, It seems to me that the javax.ws.rs.ext. ProviderFactory API
> > needs a bit refactoring in order to accommodate a more complex
> > EntityProvider searching algorithm. For example, what we have
> > currently in ProviderFactory is as below:
> >
> > public abstract class ProviderFactory {
> > public abstract <T> EntityProvider<T>
> > createEntityProvider(Class<T> type);
> > }
> >
> > I.e., given a Java type then ProviderFactory returns an
> instance of
> > EntityProvider that supports this Java type. However if we
> look into
> > EntityProvider API, we will find a parameter of Java class is not
> > enough for ProviderFactory to identify an EntityProvider to
> use for
> > following reasons:
> >
> > a). EntityProvider can be annotated with @ProduceMime/_at_ConsumeMime
> > and an EntityProvider can support different mime types for produce
> > and consume. In this case when we ask ProviderFactory to
> return us a
> > most appropriate EntityProvider, we really need to tell
> > ProviderFactory the EntityProvider being requested is used for
> > producing or consuming.
> >
> > b). ProviderFactory also needs to know the mine types of the
> > request, this has been clearly stated in the spec, section 3.1.3:
> >
> > "2. Select the set of EntityProvider classes that support
> the media
> > type of the request,"
> >
> > Enclosed below is a ProviderFactory I wrote that can return an
> > instance of EntityProvider based on three parameters. Let
> me know if
> > this is the intended way to use ProviderFactory and EntityProvider
> > APIs.
> >
> > public class ProviderFactoryImpl extends ProviderFactory {
> > public <T> EntityProvider<T> createEntityProvider(Class<T> type,
> > String[] requestedMineTypes,
> > boolean
> > isConsumeMime) {
> >
> > for (EntityProvider<T> ep : entityProviders) {
> > String[] supportedMimeTypes = {"*/*"};
> > if (isConsumeMime) {
> > ConsumeMime c =
> > ep.getClass().getAnnotation(ConsumeMime.class);
> > if (c != null) {
> > supportedMimeTypes = c.value();
> > }
> > } else {
> > ProduceMime c =
> > ep.getClass().getAnnotation(ProduceMime.class);
> > if (c != null) {
> > supportedMimeTypes = c.value();
> > }
> > }
> >
> > if (matchMineTypes(supportedMimeTypes,
> > requestedMineTypes) && ep.supports(type)) {
> > return ep;
> > }
> > }
> >
> > return null;
> > }
> >
> >
> > private boolean matchMineTypes(String[] supportedMimeTypes,
> > String[] requestedMimeTypes) {
> > .
> > }
> > }
> >
> > Thanks,
> > Jervis Liu
> >
> > ----------------------------
> > IONA Technologies PLC (registered in Ireland)
> > Registered Number: 171387
> > Registered Address: The IONA Building, Shelbourne Road, Dublin 4,
> > Ireland
> >
> >
> ---------------------------------------------------------------------
> > To unsubscribe, e-mail: dev-unsubscribe_at_jsr311.dev.java.net
> > For additional commands, e-mail: dev-help_at_jsr311.dev.java.net
> >
>
> ---
> Marc Hadley <marc.hadley at sun.com>
> CTO Office, Sun Microsystems.
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe_at_jsr311.dev.java.net
> For additional commands, e-mail: dev-help_at_jsr311.dev.java.net
>

----------------------------
IONA Technologies PLC (registered in Ireland)
Registered Number: 171387
Registered Address: The IONA Building, Shelbourne Road, Dublin 4, Ireland