Rabick, Mark A (MS) wrote:
>
> The project I'm working on is a small group and expect significant
> evolutionary changes to our software, primarily due to an evolving
> database schema. I originally annotated our existing Hibernate
> generated persistence objects (entity beans) with javax.xml.bind.*
> annotation (XmlRootElement, etc) and let JAXB and Jersey create the
> XML schema. We will have customers that will be using the current
> instance of our ReST resources that will not be able to easily migrate
> their software as we evolve. For example, if we remove a column from
> a database table, removing the attribute from our entity bean, and
> from the URI query string, new clients will be able to communicate
> with the newly generated schema but legacy clients will get errors in
> their query strings and my resources would get errors trying to parse
> the incoming xml. One solution we have looked at is to create
> multiple versions of our services:
>
> _http://host:7001/app/v1/toy_
>
> Would be the first instance and the URI would be mapped to
>
> - com.company.rest.resources.ToyV1Resource.java
>
> and return instances of com.company.persistence.Toy.java
>
> Here is the problem... Toy is an EJB3 entity bean created from
> Hibernate that is serviced by our EJB3 Stateless session beans. If I
> removed a column from the database, the client still wants access to
> the data so if I marked that class attribute (say String toyColor) as
> @XmlTransient, JAXB would exclude it from the generated schema but
> client 1 still is expecting the color attribute, even if it is
> meaningless in the new Schema.
>
> We'd like to create a new resource:
>
> - com.company.rest.resourcres.ToyV2Resource.java
> (_http://host:7001/app/v2/toy_)
>
> that is agnostic of the change and update the V1 resource to be aware
> of the change and for example, return the toyColor XML element or
> attribute as an empty string....
>
> Would an external JAXB configuration file be the way to go? Multiple
> JAXB configurations, one for each schema instance? It would seem that
> I would have to manually implement the JAXBContext using an custom JAX
> message body readers and writers...?
>
> In a nutshell, I'm looking for a way to support multiple schemas that
> bind to a single object, and support multiple versions of the schemas
> concurrently to support legacy ReST service users.
>
Now you're starting to get a feel for why deprecated methods are
typically not removed in standard Java APIs :-).
From my own experience, JAXB deals very nicely with *adding* new
properties to your Java classes (and therefore elements to your schema),
as long as you make them optional. I have had a lot of success at not
breaking older Java clients (and we even share the model classes across
both client and server when both are in Java), as long as the server can
make an intelligent default decision when an old client does not specify
a value for a newly added property. Deleting things remains
problematic, although if this is all we do JAXB itself won't complain
... it will just ignore the elements that no longer map to a property on
the JAXB annotated bean class. But that might break some semantic
assumptions in your application's business logic.
The more important consideration, though, is that sometimes you *do*
need to evolve APIs in a manner that is not backwards compatible. For
my own APIs, I like to define a way for the client to indicate (via a
custom HTTP header or whatever) which specification version the client
was programmed to expect. Then, it's up to the server to either "deal
with it" (respond the old way to old clients, and the new way to new
clients") or refuse to deal with clients wanting the old way (perhaps
you've EOL'd support for the old version after a period of time). The
implementation of this approach can get pretty tricky on the server
side, but fortunately there are lots of options, including things like:
* A JAX-RS based server's resource method can examine the version header
and parse the incoming request differently based on it.
* A front end proxy can direct "old" requests to an "old" implementation
and "new" requests to a "new" implementation that are running
concurrently.
(You probably have to give up the idea that you have freedom to
immediately
remove columns from database tables, though, if the DB is shared between
the two versions, unless you're willing to adapt the "old" version
server code).
* Declare different media types for "old" and "new" version requests to
the same
URIs, so JAX-RS's matching algorithm can help you do the right thing.
If you do
this universally, you probably don't need a separate "spec version"
header.
What about clients who don't include the header? My current algorithm
is "assume they want the current version" ... on the theory that we
*gave* the client developer a way to tell us what they wanted, so caveat
emptor. This is pretty similar in spirit to how, for example, Maven
deals with dependencies that don't explicitly or implicitly declare a
version number ... it assumes you want the most recent version.
And, of course, the convenience client libraries (in multiple languages)
we offer to client developers will always include the appropriate
version header for you, if you choose to use one of them.
Craig
>
> --mark
>
> *_______________________________________________*
> *Mark A. Rabick*
> Software Engineer
> Northrop Grumman - C2 Systems (MS/C2SD/IIS)
> 3200 Samson Way
> Bellevue, NE 68123
> *Ph: (402) 293-7091*
> *Em: mark.rabick_at_ngc.com*
> /Remember PFC Ross A. McGinnis.../
> / //_http://www.army.mil/medalofhonor/McGinnis/index.html_/
> /... MA2 Michael A. Monsoor, Lt. Michael P. Murphy, Cpl. Jason Dunham,
> SFC Paul Ray Smith and the rest... /
> / //_http://www.cmohs.org/recipients/most_recent.htm_/
>