users@jersey.java.net

RE: [Jersey] Resource and XML Schema versioning...

From: Rabick, Mark A (MS) <"Rabick,>
Date: Fri, 27 Feb 2009 10:44:31 -0600

The custom content-type and suffix type negotiation is pretty cool, once
I got the wrinkles out. Now I'm wondering... I want to support both
schema-type and content type negotiation. That is, I have multiple XML
schema versions (jaxb annotations on 2 different packages representing
the same set of entities. Behind the scenes, I map my JPA entity beans
to objects in either schema. It seems like I can let a user specify a
schema version in a suffix mapped to a custom media type as below if the
client doesn't have the ability to modify an Accept header. I would
also like to let a user retrieve either JSON or XML formatted data of
the multiple schemas .

Another request I have is to have a 'constant' URI that will always map
to the most recent version of a schema for a given entity which seems to
be working:

For example. The Node entity bean has 2 XML schemas NodeV1.java and
NodeV2.java.

The URI:

http://host/myrest/nodes/NODEKEYVALUE.v1-xml returns a V1 schema
instance
http://host/myrest/nodes/NODEKEYVALUE also returns a V1 schema
instance

The same is true for JSON.

If I add the V2 schema to the equation, I'd like:

http://host/myrest/nodes/NODEKEYVALUE.v1-xml to still return a V1 schema
instance http://host/myrest/nodes/NODEKEYVALUE.v2-xml to return a V2
schema instance
http://host/myrest/nodes/NODEKEYVALUE also to return a V2 schema
instance

All are implemented in the same resource class with different
@Produces(mediatypes). Would it be simpler to use a URI fragment to
specify the schema version and map .xml and .json to the
MediaType.APPLICATION_XML and _JSON respectively?

http://host/myrest/v1/nodes/NODEKEYVALUE.xml
http://host/myrest/v1/nodes/NODEKEYVALUE.json
http://host/myrest/v2/nodes/NODEKEYVALUE.xml
http://host/myrest/v2/nodes/NODEKEYVALUE.json

How could I get

http://host/myrest/nodes/NODEKEYVALUE.xml
http://host/myrest/nodes/NODEKEYVALUE.json

To map to the V2 URI's?

The context-root of the application is myrest.

Could I use sub-resources/locators?

@Path("/")
Public class NodesResource {
        
        @Path("/v1/nodes")
        public NodesV1Resource getNodesV1Resource() {
                return new NodesV1Resourse();
        }
        @Path("/v2/nodes")
        public NodesV2Resource getNodesV2Resource() {
                return new NodesV2Resourse();
        }
        @Path("/nodes")
        public NodesV2Resource getNodesV2Resource() {
                return new NodesV2Resourse();
        }
}

Public class NodesV1Resource {
        
        @GET @Path("{key}")
        @Produces({"application/xml","application/json"})
        public NodeV1 getNodeV1ByKey(@PathParam("key") String key) {
                return convertToNodeV1( NodeDatabase.getNodeByKey(key)
);
         }
}

Public class NodesV2Resource {
        
        @GET @Path("{key}")
        @Produces({"application/xml","application/json"})
        public NodeV2 getNodeV2ByKey(@PathParam("key") String key) {
                return convertToNodeV2( NodeDatabase.getNodeByKey(key)
);
         }
}

--or--

@Path("/v1/nodes")
Public class NodesV1Resource {
        
        @GET @Path("{key}")
        @Produces({"application/xml","application/json"})
        public NodeV1 getNodeV1ByKey(@PathParam("key") String key) {
                return convertToNodeV1( NodeDatabase.getNodeByKey(key)
);
         }
}

@Path("/v2/nodes")
Public class NodesV2Resource {
        
        @GET @Path("{key}")
        @Produces({"application/xml","application/json"})
        public NodeV2 getNodeV2ByKey(@PathParam("key") String key) {
                return convertToNodeV2( NodeDatabase.getNodeByKey(key)
);
         }
}

I don't see a way to have @Path("v2/nodes") and @Path("/nodes") both
match to the NodesV2Resource. I think the subresource mechanism is the
way to go because of its reuse ability.....

-m

> -----Original Message-----
> From: Paul.Sandoz_at_Sun.COM [mailto:Paul.Sandoz_at_Sun.COM]
> Sent: Thursday, February 26, 2009 2:50 PM
> To: users_at_jersey.dev.java.net
> Subject: Re: [Jersey] Resource and XML Schema versioning...
>
> Hi Mark,
>
> You have not configured your web.xml correctly to use your own
> extension of PackagesResourceConfig.
>
> I do not know what the package name of your class
> CnodbRestResourceConfig is, but for the sake of example and clarity i
> am going to assume it is "mil.cnodb.rs". The servlet declaration
> requires just the following init-param:
>
> <init-param>
> <param-
> name>com.sun.jersey.config.property.resourceConfigClass</param-name>
>
> <param-value>mil.cnodb.rs.CnodbRestResourceConfig</param-value>
> </init-param>
>
> Plus in your CnodbRestResourceConfig you do not require all the WADL
> configuration stuff as copied from the camel web example, i dunno if
> you are using that or not. You just require the following:
>
> public class CnodbRestResourceConfig extends
> PackagesResourceConfig {
>
> public CnodbRestResourceConfig() {
> super("mil.cnodb.rs.resources.v1");
> }
>
> public Map<String, MediaType> getMediaTypeMappings() {
> Map<String, MediaType> m = new HashMap<String,
> MediaType>();
> m.put("v1-xml", CnodbMediaType.APPLICATION_V1XML_TYPE);
> m.put("v1-json", CnodbMediaType.APPLICATION_V1JSON_TYPE);
> return m;
> }
> }
>
> Paul.
>
> On Feb 26, 2009, at 9:27 PM, Rabick, Mark A (MS) wrote:
>
> > Thanks Marc. I just changed the media type to
> application/v1+xml and
> > if I enter the URL:
> >
> > http://host/nodes1
> >
> > IE asks me to open a file 'nodes1' and pick an 'open-with'
> program for
> > file of type 'application/v1+xml'... So far so good, but if
> I change
> > the URI and add the suffix mapped in the PackagesResourceConfig
> > ("v1-xml", "v1-json"),
> >
> > http://host/nodes1/nodes1.v1-xml
> >
> > I get back an empty page??? Same with .v1-json
> >
> > I'm trying to use the suffix to specify both a schema version and
> > media type to return. Is that trying too much?
> >
> > -m
> >
> >
> >> -----Original Message-----
> >> From: Marc.Hadley_at_Sun.COM [mailto:Marc.Hadley_at_Sun.COM]
> >> Sent: Thursday, February 26, 2009 2:19 PM
> >> To: users_at_jersey.dev.java.net
> >> Subject: Re: [Jersey] Resource and XML Schema versioning...
> >>
> >> There are a couple of things you could try:
> >>
> >> (i) Simplest. Change your media type to application/v1+xml.
> >> JAX-RS requires the JAXB message body writer to support
> >> application/xml, text/ xml and application/*+xml but its
> allowed to
> >> ignore other media types.
> >>
> >> (ii) More complex. Write your own message body writer that
> defers to
> >> the built-in JAXB one. You'll need to implement
> >> MessageBodyWriter<NodeV1>, have javax.ws.rs.ext.Providers injected
> >> and use the getMessageBodyWriter method to get yourself a
> reference
> >> to the built-in JAXB writer that you can then call.
> >>
> >> Marc.
> >>
> >> On Feb 26, 2009, at 2:58 PM, Rabick, Mark A (MS) wrote:
> >>
> >>> I'm getting an exception trying to configure a custom media type:
> >>>
> >>> SEVERE: A message body writer for Java type, class
> >>> mil.cnodb.rs.model.v1.NodeV1, and MIME media type,
> >> application/v1-xml,
> >>> was not found Feb 26, 2009 1:50:35 PM
> >>> com.sun.jersey.server.impl.application.WebApplicationImpl
> >> onException
> >>> SEVERE: Internal server error
> >>> javax.ws.rs.WebApplicationException
> >>> at
> >>> com
> >>>
> >>
> .sun.jersey.spi.container.ContainerResponse.write(ContainerResponse.j
> >>> ava:241)
> >>> at
> >>> com
> >>>
> >>
> .sun.jersey.server.impl.application.WebApplicationImpl._handleRequest
> >>> (WebApplicationImpl.java:578)
> >>> at
> >>> com
> >>>
> >>
> .sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(
> >>> WebApplicationImpl.java:502)
> >>> at
> >>> com
> >>>
> >>
> .sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(
> >>> WebApplicationImpl.java:493)
> >>> at
> >>> com
> >>>
> >>
> .sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.j
> >>> ava:308)
> >>> at
> >>> com
> >>>
> >>
> .sun.jersey.spi.container.servlet.ServletContainer.service(ServletCon
> >>> tainer.java:314)
> >>>
> >>> How can I map application/v1-xml to application/xml?
> >> Similarly with
> >>> application/v1-json. Is there a way to avoid writing a custom
> >>> messageWriter for the media type?
> >>>
> >>> I've configured a context resolver:
> >>>
> >>> /**
> >>> *
> >>> */
> >>> @Provider
> >>> @Produces({"application/v1-xml","application/v1-json"})
> >>> public final class Version1ContextResolver implements
> >>> ContextResolver<JAXBContext> {
> >>>
> >>> private final JAXBContext context;
> >>>
> >>> private final Set<Class> types;
> >>>
> >>> /*
> >>> * The list of JAXB class types (top-level) need to be
> >> listed here.
> >>> */
> >>> private final Class[] cTypes = {NodeV1.class};
> >>>
> >>> public Version1ContextResolver() throws Exception {
> >>>
> >>> this.types = new HashSet(Arrays.asList(cTypes));
> >>> this.context = new JSONJAXBContext(
> >>> JSONConfiguration.natural().build(), cTypes);
> >>> }
> >>>
> >>> public JAXBContext getContext(Class<?> objectType) {
> >>> return (types.contains(objectType)) ? context : null;
> >>> }
> >>> }
> >>>
> >>> Created a Resource that
> >>> Produces("application/v1-xml","application/v1-json")
> >>>
> >>> @GET
> >>> @Produces({"application/v1-xml","application/v1-json"})
> >>> public NodeV1 getNode(
> >>> @QueryParam("id") String nodeId,
> >>> @QueryParam("name") String nodeName,
> >>> @QueryParam("level") String nodeLevel,
> >>> @QueryParam("allegiance") String allegiance,
> >>> @QueryParam("cc") String cc,
> >>> @QueryParam("typeGeneral") String
> nodeTypeGeneral,
> >>> @QueryParam("typeSpecific") String
> >>> nodeTypeSpecific) {
> >>>
> >>>
> >>> NodeV1 nodeV1 = new NodeV1();
> >>> nodeV1.setNodeName("Nodes Name is");
> >>>
> >>> return nodeV1;
> >>> }
> >>>
> >>> Extended PackagesResourceConfig:
> >>>
> >>> public class CnodbRestResourceConfig extends
> >> PackagesResourceConfig {
> >>>
> >>> public CnodbRestResourceConfig() {
> >>> super(createProperties());
> >>> }
> >>>
> >>> public Map<String, MediaType> getMediaTypeMappings() {
> >>> Map<String, MediaType> m = new HashMap<String, MediaType>();
> >>> m.put("v1-xml", CnodbMediaType.APPLICATION_V1XML_TYPE);
> >>> m.put("v1-json", CnodbMediaType.APPLICATION_V1JSON_TYPE);
> >>> return m;
> >>> }
> >>>
> >>> protected static Map<String,Object> createProperties() {
> >>> Map<String, Object> properties = new HashMap<String,
> >>> Object>();
> >>>
> >>> properties.put(PackagesResourceConfig.PROPERTY_PACKAGES,
> >>> "mil.cnodb.rs.resources.v1");
> >>>
> >>> WadlGeneratorConfig config = WadlGeneratorConfig
> >>> .generator(WadlGeneratorApplicationDoc.class)
> >>> .prop("applicationDocsFile",
> >>> "classpath:/application-doc.xml")
> >>> .generator(WadlGeneratorGrammarsSupport.class)
> >>> .prop("grammarsFile",
> >>> "classpath:/application-grammars.xml")
> >>> .generator(WadlGeneratorResourceDocSupport.class)
> >>> .prop("resourceDocFile",
> >> "classpath:/resourcedoc.xml")
> >>> .build();
> >>>
> >>>
> properties.put(ResourceConfig.PROPERTY_WADL_GENERATOR_CONFIG,
> >>> config);
> >>> return properties;
> >>> }
> >>>
> >>> }
> >>>
> >>> And edited web.xml
> >>>
> >>> <servlet>
> >>> <servlet-name>CNODB Web Application</servlet-name>
> >>>
> >>> <servlet-
> >>> class>com.sun.jersey.spi.container.servlet.ServletContainer</se
> >>> rvlet-class>
> >>> <init-param>
> >>>
> >>> <param-name>com.sun.jersey.config.property.resourceConfigClass</
> >>> param-na
> >>> me>
> >>>
> >>>
> <param-value>com.sun.jersey.api.core.PackagesResourceConfig</param-
> >>> value
> >>>>
> >>> </init-param>
> >>> <init-param>
> >>>
> >>> <param-name>com.sun.jersey.config.property.packages</param-name>
> >>> <param-value>mil.cnodb.rs</param-value>
> >>> </init-param>
> >>> <load-on-startup>1</load-on-startup>
> >>> </servlet>
> >>>
> >>>> -----Original Message-----
> >>>> From: Paul.Sandoz_at_Sun.COM [mailto:Paul.Sandoz_at_Sun.COM]
> >>>> Sent: Thursday, February 26, 2009 11:12 AM
> >>>> To: users_at_jersey.dev.java.net
> >>>> Subject: Re: [Jersey] Resource and XML Schema versioning...
> >>>>
> >>>>
> >>>> On Feb 26, 2009, at 5:51 PM, Rabick, Mark A (MS) wrote:
> >>>>
> >>>>>> On Feb 25, 2009, at 6:08 PM, Rabick, Mark A (MS) wrote:
> >>>
> >>>>>> A suffix at the end of the URI path, but not part of the
> >>>> application
> >>>>>> and @Path.
> >>>>>
> >>>>> Not part of the application I assume would be not in the
> >> 'context-
> >>>>> root'
> >>>>> of the web app. Sorry to belabor this but how can a suffix
> >>>> be at the
> >>>>> end of the URI path, but NOT Included in an @Path?
> >>>>> Can you give an example?
> >>>>
> >>>> Here is a specific example from the camel web component:
> >>>>
> >>>> https://svn.apache.org/repos/asf/camel/trunk/components/camel-
> >>>>
> >>
> web/src/main/java/org/apache/camel/web/util/CamelResourceConfig.java
> >>>>
> >>>> public class CamelResourceConfig extends PackagesResourceConfig {
> >>>> public CamelResourceConfig() {
> >>>> super(createProperties());
> >>>> }
> >>>>
> >>>> protected static Map<String, Object> createProperties() {
> >>>> Map<String, Object> properties = new HashMap<String,
> >>>> Object>();
> >>>>
> >>>> properties.put(PackagesResourceConfig.PROPERTY_PACKAGES,
> >>>> "org.apache.camel.web");
> >>>>
> >>>> WadlGeneratorConfig config = WadlGeneratorConfig
> >>>> .generator(WadlGeneratorApplicationDoc.class)
> >>>> .prop("applicationDocsFile",
> >> "classpath:/application-
> >>>> doc.xml")
> >>>> .generator(WadlGeneratorGrammarsSupport.class)
> >>>> .prop("grammarsFile", "classpath:/application-
> >>>> grammars.xml")
> >>>> .generator(WadlGeneratorResourceDocSupport.class)
> >>>> .prop("resourceDocFile",
> >>>> "classpath:/resourcedoc.xml")
> >>>> .build();
> >>>>
> >>>>
> >> properties.put(ResourceConfig.PROPERTY_WADL_GENERATOR_CONFIG,
> >>>> config);
> >>>> return properties;
> >>>> }
> >>>>
> >>>> public Map<String, MediaType> getMediaTypeMappings() {
> >>>> Map<String, MediaType> m = new HashMap<String,
> >> MediaType>();
> >>>> m.put("html", MediaType.TEXT_HTML_TYPE);
> >>>> m.put("xml", MediaType.APPLICATION_XML_TYPE);
> >>>> m.put("json", MediaType.APPLICATION_JSON_TYPE);
> >>>> m.put("dot", MediaType.valueOf(Constants.DOT_MIMETYPE));
> >>>> return m;
> >>>> }
> >>>> }
> >>>>
> >>>> See the method "getMediaTypeMappings". Notice that it declares a
> >>>> mapping of suffix to media type.
> >>>>
> >>>> Thus whenever there is say a request URI:
> >>>>
> >>>> http://localhost:8080/path/foo.html
> >>>>
> >>>> Jersey will modify the request URI to be:
> >>>>
> >>>> http://localhost:8080/path/foo
> >>>>
> >>>> and modify the Accept header to be:
> >>>>
> >>>> Accept: text/html
> >>>>
> >>>> The above modifications will occur *before* Jersey processes the
> >>>> request and dispatches to a methods on a resource class.
> >>>> It is essentially a request filter.
> >>>>
> >>>> Hope that helps,
> >>>> Paul.
> >>>>
> >>>>
> >>>>
> >>>>
> >>>>
> >>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>>>> Ie. http://host:8080/fooapplication/foo.foo-v1/
> >>>>>>>
> >>>>>>> public class FooV1Resource {
> >>>>>>>
> >>>>>>> @GET @Path("/foo.foo-v1/{id}")
> >>>>>>> @Produces("application/foo-v1") public FooV1(String
> >>>>>>> @PathParam("id") String fooId) {
> >>>>>>>
> >>>>>>> FooV2 foo2 = FooDatabase.getFooById(fooId);
> >>>>>>> FooV1 foo1 = convertFoo2ToFoo1(foo2)
> >>>>>>> return foo1;
> >>>>>>> }
> >>>>>>> }
> >>>>>>>
> >>>>>>> Ie. http://host:8080/fooapplication/foo.foo-v2/
> >>>>>>>
> >>>>>>> public class FooV2Resource {
> >>>>>>>
> >>>>>>> @GET @Path("/foo.foo-v2/{id}")
> >>>>>>> @Produces("application/foo-v2") public FooV2(String
> >>>>>>> @PathParam("id") String fooId) {
> >>>>>>> FooV2 foo2 = FooDatabase.getFooById(fooId);
> >>>>>>> return foo2;
> >>>>>>> }
> >>>>>>> }
> >>>>>>>
> >>>>>>> With the appropriate ContextResolvers based on the media
> >>>> types, of
> >>>>>>> course...
> >>>>>>>
> >>>>>>> What 'existing Jersey functionality' are you
> referring to that
> >>>>>>> suffixes would help with?
> >>>>>>>
> >>>>>>
> >>>>>> See:
> >>>>>>
> >>>>>>
> >>>>>
> >>>> https://jersey.dev.java.net/source/browse/*checkout*/jersey/tags/
> >>>> jerse
> >>>>> y- 1.0.2/api/jersey/com/sun/jersey/api/core/
> >>>>> ResourceConfig.html#getMediaTyp
> >>>>> eMappings()
> >>>>>>
> >>>>>> Paul.
> >>>>>>
> >>>>>>
> >>>>>
> >>>>>
> >>>>
> >>
> ---------------------------------------------------------------------
> >>>>> 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
>
>
>