My Question:
Is there a best known method for processing different versions of objects that have been deserialized in handlers or callbacks?
Some Background Info:
We are going to be using serialized objects as messages to communicate between various components in a software suite. These could be in JSON form or using something like protobufs. Anytime you start to serialize objects, either for long term storage or between different versions of an application, you have to be able to handle different versions of those objects (maybe using Annotations-Java or Attributes-C#).
I'm trying to avoid code like this:
onRecvMyMsg(MyMsg msg)
{
if (msg.version == 1.0)
// process it here
else if (msg.version < 1.5)
// process it here
else if (msg.version < 2.0)
// process part of it in the 1.5 handler and the other part here
else if // etc...
}
It seems like this would be a maintenance nightmare after many additions/enhancements/changes...
Surely, someone must have been solved this since this seems like a very common practice in software engineering. Any help or suggestions would be appreciated!
The problem with my original approach is that the solution is oriented towards the wrong direction. We are thinking that the processing entity, or consumer of the object, is required to know versions so it can properly handle the differences between them. What we could be thinking about, instead, is how we can get the object to express itself based on the version of the processor (or consumer).
If we use a serialization technology like Protocol Buffers, Apache Thrift, or Apache Avro we are half way there in obtaining what we want. Libraries like these handle versioning for us, in a sense. Generally speaking they behave like this:
if a field is received but not defined it is simply dropped
if a field is defined but not received, a flag indicates that it is
not present and an optional default value can be supplied
These libraries also support "required" fields; however, most people (including the authors) don't recommend using "required" fields on the protocol objects themselves because a "required" field will break comparability for all time, both ways (send and receive) if there is a "required" field not present. They suggest handling required fields on the processing side.
Since the libraries mentioned handle all of the work required to serialize and deserialize objects in a backward and forward compatible manner, all we really need to do is wrap those protocol objects into something else that can expose the data in the form that the consumer expects.
For example, here are 3 versions of the same message that can be processed.
ReviewCommentMsg // VERSION 1
{
string : username
string : comment
}
ReviewCommentMsg // VERSION 2 (added "isLiked")
{
string : username
string : comment
bool : isLiked
}
ReviewCommentMsg // VERSION 3 (added "location", removed "isLiked")
{
string : username
string : comment
string : location
}
The following demonstrates how we could incrementally update the client code to process those messages.
/*******************************************************************************
EXAMPLE OBJECT V1
*******************************************************************************/
class ReviewComment
{
private final String username;
private final String comment;
ReviewComment(ReviewCommentMessage msg)
{
// Throws exception if fields are not present.
requires(msg.hasUsername());
requires(msg.hasComment());
this.username = msg.getUsername();
this.comment = msg.getComment();
}
String getUsername() { return this.username; }
String getComment() { return this.comment; }
}
/*******************************************************************************
EXAMPLE PROCESSOR V1
*******************************************************************************/
public void processReviewComment(ReviewComment review)
{
// Simulate posting the review to the blog.
BlogPost.log(review.getUsername(), review.getComment());
}
/*******************************************************************************
EXAMPLE OBJECT V2
*******************************************************************************/
class ReviewComment
{
private final String username;
private final String comment;
private final Boolean isLiked;
ReviewComment(ReviewCommentMessage msg)
{
// Throws exception if fields are not present.
requires(msg.hasUsername());
requires(msg.hasComment());
this.username = msg.getUsername();
this.comment = msg.getComment();
if (msg.hasIsLiked())
{
this.isLiked = msg.getIsLiked();
}
}
String getUsername() { return this.username; }
String getComment() { return this.comment; }
// Use Java's built in "Optional" class to indicate that this field is optional.
Optional<Boolean> isLiked() { return Optional.of(this.isLiked); }
}
/*******************************************************************************
EXAMPLE PROCESSOR V2
*******************************************************************************/
public void processReviewComment(ReviewComment review)
{
// Simulate posting the review to the blog.
BlogPost.log(review.getUsername(), review.getComment());
Optional<Boolean> isLiked = review.isLiked();
if (isLiked.isPresent() && !isLiked.get())
{
// If the field is present AND is false, send an email telling us someone
// did not like the product.
Stats.sendEmailBadReview(review.getComment());
}
}
/*******************************************************************************
EXAMPLE OBJECT V3
*******************************************************************************/
class ReviewComment
{
private final String username;
private final String comment;
private final String location;
ReviewComment(ReviewCommentMessage msg)
{
// Throws exception if fields are not present.
requires(msg.hasUsername());
requires(msg.hasComment());
requires(msg.hasLocation());
this.username = msg.getUsername();
this.comment = msg.getComment();
this.location = msg.getLocation();
}
String getUsername() { return this.username; }
String getComment() { return this.comment; }
String getLocation() { return this.location; }
}
/*******************************************************************************
EXAMPLE PROCESSOR V3
*******************************************************************************/
public void processReviewComment(ReviewComment review)
{
// Simulate posting the review to the blog.
BlogPost.log(review.getUsername(), review.getComment());
// Simulate converting the location into geo coordinates.
GeoLocation geoLocation = GeoLocation.from(review.getLocation());
// Simulate posting the location to the blog.
BlogPost.log(review.getUsername(), geoLocation);
}
In this example:
PROCESSOR V1 can receive messages (V1, V2, and V3)
PROCESSOR V2 can receive messages (V1, V2, and V3)
PROCESSOR V3 can receive messages (V3)
This approach puts the compatibility concern in the message objects themselves and alleviates clients/processors from having a bunch of version checking.
Admittedly, you still have to perform some semantic checking; however, that seems far less cumbersome than building in version logic to every client.
Related
For a simple Java REST-API I created a save function to persist my model to a DynamoDB table.
The model uses a auto generated range key as you can see here:
#DynamoDBTable(tableName = "Events")
public class EventModel {
private int country;
private String id;
// ...
#DynamoDBHashKey
public int getCountry() {
return country;
}
public void setCountry(int country) {
this.country = country;
}
#DynamoDBRangeKey
#DynamoDBAutoGeneratedKey
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
//...
}
Unfortunately the the DynamoDBMappers .save() method does not return anything. I want to return the created item to set the proper location header in my 201 HTTP response.
public EventModel create(EventModel event) {
mapper.save(event);
return null;
}
How can I make that work? Any suggestions? Of course I could generate the id on the client but I don´t want to do this because solving the potential atomicity issue needs additional logic on client- and server-side.
I´m using the aws-java-sdk-dynamodb in version 1.11.86.
Never mind, I figured out how to do it. The .save() method updates the reference of the object. After calling mapper.save(event); the id property is populated and has its value.
So the way to get it work is just:
public EventModel create(EventModel event) {
mapper.save(event);
return event;
}
That´s it!
There is direct way through dynamo db mapper to get what is saved in dynamodb after put/update Approach mentioned by m4xy# would work if you are saving with DynamoDBConfig as CLOBBER or UPDATE. If you are using UPDATE_SKIP_NULL_ATTRIBUTES, this approach won't work.
If you are using mapper, you have to specifically call db again to get existing value (which might have been updated if there are multiple writers and you might get unxpected result). To ensure read that you expect you can implement locking for write such that if lock is acquired by a given thread, no other thread can write for a given key. But, this approach as a downside of slowing down your application.
Alternatively, you can use dynamoDBClient that has apis to support return db values after write.
https://sdk.amazonaws.com/java/api/2.0.0-preview-11/index.html?software/amazon/awssdk/services/dynamodb/DynamoDbClient.html
As the title says....
I want to build a POJO with four field variables and at certain runtime events create an instance of this POJO with access to possibly maybe two or three of the fields.
public class Category implements Serializable {
private String name;
private String description;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
Lets say I create a new Category object but I only want to be able to have access to the name field during runtime. Is there a design pattern I can use to achieve this? I thought about the strategy pattern and looked at the builder but I am still confused if I can do this in java.
Basically the overall goal is to grab an object from a database and return it as a JSON response in jax rs. But sometimes I dont want a complete object returned but only lets say halve of the object to be accessible at during certain runtime events. My apologies if this seems like a dumb question but I know what I want to do but just don't know the best way.
I have the same problem with you, and my project was used springmvc,and the json tool is jackson.With the problem solved, I just use #JsonIgnore.For more details,just read jackson-how-to-prevent-field-serialization
So someone correct me if I am wrong or see a better option than this...with alot of objects this can be alot of extra code for serialization and deserialization...Jackson Provisions is what I need. I can use the annotation #JsonView(DummyClass.class) on the field variable. I will accept this a the best answer in a day or two unless someone else posts a better response.
// View definitions:
class Views {
static class Public { }
static class ExtendedPublic extends PublicView { }
static class Internal extends ExtendedPublicView { }
}
public class Bean {
// Name is public
#JsonView(Views.Public.class) String name;
// Address semi-public
#JsonView(Views.ExtendPublic.class) Address address;
// SSN only for internal usage
#JsonView(Views.Internal.class) SocialSecNumber ssn;
}
With such view definitions, serialization would be done like so:
// short-cut:
objectMapper.writeValueUsingView(out, beanInstance, ViewsPublic.class);
// or fully exploded:
objectMapper.getSerializationConfig().setSerializationView(Views.Public.class);
// (note: can also pre-construct config object with 'mapper.copySerializationConfig'; reuse)
objectMapper.writeValue(out, beanInstance); // will use active view set via Config
// or, starting with 1.5, more convenient (ObjectWriter is reusable too)
objectMapper.viewWriter(ViewsPublic.class).writeValue(out, beanInstance);
This information was pulled from http://wiki.fasterxml.com/JacksonJsonViews
with jackson 2.3, I can do this with JAX-RS
public class Resource {
#JsonView(Views.Public.class)
#GET
#Produces(MediaType.APPLICATION_JSON )
public List<Object> getElements() {
...
return someResultList;
}
}
I am using gson to produce json of a collection of objects in Java (Some objects have other collections too). This json will be used to populate the web page for users with different clearance levels. Therefore the detail which users can see differs. Web page only shows what it needs to show however if I use the same json for two different pages then html source code will have more data than it should have. Is there a way to inform gson which variables in which class should be added to the json? As far as I search I could not find an easy way. Either I will produce json myself or clear extra data from the json which gson produced.
I need to use same classes for different clearance levels and get different json.
You are trying to use Gson to generate multiple different JSON outputs of the same objects in the same JVM, which is going to be difficult, both in Gson and any good serialization library, because their express goal is essentially the opposite of what you're looking for.
The right thing to do would be to instead represent these different clearance levels with different classes, and simply serialize those different classes with Gson as normal. This way you separate the security model from the serialization, letting you safely pass this information around.
/**
* Core data class, contains all information the application needs.
* Should never be serialized for display to any end user, no matter their level.
*/
public class GlobalData {
private final String username;
private final String private_data;
private final String secure_data;
}
/** Interface for all data display operations */
public interface DisplayData {
/** Returns a JSON representation of the data to be displayed */
public String toJson();
}
/**
* Class for safe display to an untrusted user, only holds onto public
* data anyone should see.
*/
public class UserDisplayData implements DisplayData {
private final String username;
public UserDisplayData(GlobalData gd) {
username = gd.username;
}
public String toJson() {
return gson.toJson(this);
}
}
/**
* Class for safe display to a trusted user, holds private information but
* does not display secure content (passwords, credit cards, etc.) that even
* admins should not see.
*/
public class AdminDisplayData implements DisplayData {
private final String username;
private final String private_data;
public AdminDisplayData(GlobalData gd) {
username = gd.username;
private_data = gd.private_data;
}
public String toJson() {
// these could be different Gson instances, for instance
// admin might want to see nulls, while users might not.
return gson.toJson(this);
}
}
Now you can sanitize and serialize your data as two separate steps, and use type safety to ensure your GlobalData is never displayed.
public void getDisplayData(GlobalData gd, User user) {
if(user.isAdmin()) {
return new AdminDisplayData(gd);
} else {
return new UserDisplayData(gd);
}
}
public void showData(DisplayData data) {
String json = data.toJson();
// display json however you want
}
If you erroneously tried to call showData(gd) you'd get a clear compilation error that you've done something wrong, and it's a quick fix to get the correct result by calling showData(getDisplayData(gd, user)) which safely and clearly does exactly what you want.
you can add a Expose annotations like this on the filed you don't want:
#Expose(serialize = false, deserialize = false)
private String address;
some more information here:
https://sites.google.com/site/gson/gson-user-guide#TOC-Gson-s-Expose
I am working on creating a RESTful API for a project. I am facing a few problems trying to implement it with jersey:
My object model does not contain uri info obviously. e.g, lets say I have a Fruit class. Fruit object would have let's say a FruitName and a FruitColor. But in the response I also need to send a URI. How is this usually handled? Should I create a separate "FruitResource" that has a constructor which takes a "Fruit" and creates a full resource from it, including URI? I need URIs in the nested objects as well, e.g if I am returning a list of Child objects, I need each Child object to also have a URI, but I donlt want the URI to be part of the object model. What is the cleanest way to do this?
I want to have capability to return full and partial views of the same resource. Partial views would just have the name and the URI for example. How to get this done?
Right now what I have is a Service class that accepts the requests, which uses the DAO to create and return the objects as they are modelled from the DB, serialized to JSON using jackson.
There i a way to use JaxB class and you can pass Object Model to JaxB class and JaxB class generates URI.
The below is small prototype.
UserResource Class
#Path("/user")
public class UserResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("{user-id}")
public UserJaxB getUser(#PathParam("user-id") String userId, #Context
HttpServletRequest request) {
// now XYZ is hard-coded value
String serviceEndpoint = request.getContextPath() + "/" + "user";
UserModel userModel = new UserModel(userId, "XYZ");
return new UserJaxB(serviceEndpoint,userModel);
}
}
User JAXB Class
#XmlRootElement
public class UserJaxB {
private String name;
private String id;
private String serviceEndpoint;
private String URI;
public UserJaxB(String serviceEndpoint, UserModel userModel) {
this.name = userModel.getName();
this.id = userModel.getId();
this.serviceEndpoint = serviceEndpoint;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getURI() {
return this.serviceEndpoint + "/" + id;
}
}
User Model Class
public class UserModel {
String name;
String id;
public UserModel(String name, String id) {
this.name = name;
this.id = id;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}
I'm working on a project that supports both of those concerns: https://github.com/skyscreamer/yoga with more information at http://yoga.skyscreamer.org/, including a demo.
It replaces the JAX-RS (or Spring MVC) rendering mechanism with a custom built solution that's more in tune with the needs of a REST System.
For #1, we have an annotation that you have to apply to your Child pojo. JAX-RS's annotations are meant to map URLs to controllers not to the underlying object, so we needed a custom solution... but it's really just comes down to 1 additional annotation per pojo.
For #2, we have a URL based mechanism of specifying which additional information you want from a pojo or a pojo's children (and can be nested further).
I hope this helps.
1) I'm not aware of any Jersey or JAX-RS mechanism supporting this. Seems like a bad practice to have to add the URI to the constructor for each of your domain classes, though. You could create an aspect that would intercept the method and wrap the response in a new object - adding the URI of the resource in the wrapper (you could get the URIInfo via reflection from the interceptor). I've done this when building etag support so I don't have to add cache code to every response. I suppose you could also add something in the same aspect to handle the child URI issue...
You might also want have a look at these dicussions:
http://java.net/projects/jersey/lists/users/archive/2009-01/message/357
http://markmail.org/search/?q=list%3Anet.java.dev.jersey.users+brett.dargan%40gmail.com#query:list%3Anet.java.dev.jersey.users%20brett.dargan%40gmail.com+page:1+mid:7ln7wixfihfodngg+state:results
2) For building "lighter" response entities I typically have a BeanLite.class with just the properties I need for a summary and then a Bean.class extending it with more detail. You can add both to your ORM and provide an option to switch representations in your DAO.
Thanks for all your responses. Going through all the approaches you guys presented and after a little bit of research on my own, this is what I settled on:
1) I am adding uri as part of the object model. This seems to be the cleanest solution to me currently. The URI can be automatically populated whenever the object is created (using other properties of the object). Earlier I thought this is a bad idea, but I am not able to foresee any problems with this approach other than the extra field that will have to keep moving with the objects.
2) For supporting full/partial views, I am trying to use the #JsonView annotation. This seems to be a good approach.
Let me know if there are any other potential issues with this way of handling things.
Not sure if this is a decent question or not but here it goes. We are trying to implement a UI testing framework (selenium web-driver) and want to use a Page driven design for example
class HomePage {
#FindBy(how = How.Id, id="myPageHeaderID")
private String pageHeader
In the simple example above I need to hard-code the "myPageHeaderID" string literal. One of the requirements proposed is that we be able to pull in the "myPageHeaderID" from a property for both maintenance reasons (no code deploy if something changes) and for internationalization reasons. I have been searching around and probably not doing a proper search but is there any way of doing what I am asking above?
I briefly went down this route, but due to our application it wasn't quite achievable (pages aren't always displayed in the same order once you've visited a page).
public class PageElement implements WebElementAdapter, Locatable {
private How how;
private String using;
private boolean required;
#FindBy(how = How.ID_OR_NAME, using = DEFAULT_LOCATION_STRATEGY)
private WebElement backingElement;
public PageElement(How how, String using using) {
this.how = how;
this.using = using;
this.required = true;
}
/**
* This is how the overriding of the element location is done. I then injected
* these values in a spring configured bean file.
*
* This is needed on your config file:
* default-lazy-init="true" default-init-method="initialize">
*/
public final void initElement() {
if (backingElement == null || isStale() {
backingElement = getDriver().findElement(getLocationStrategy());
}
}
public By getLocationStrategy() {
By by = new ByIdOrName(using.replace(DEFAULT_LOCATION_STRATEGY, using));
switch(how) {
case CLASS_NAME:
by = By.className(using.replace(DEFAULT_LOCATION_STRATEGY, using));
break;
//Do for others
}
return by;
}
public WebElement getBackingElement() {
return backingElement;
}
}
public interface WebElementAdapter {
WebElement getBackingElement();
}
public interface Locatable {
By getLocationStrategy();
}
I then created common widgets in POJOs, and injected these into page objects which were a collection of these widgets.
From there I had a simple test harness which was responsible for taking in strings (which were then executed. Basically it allowed for test cases to be written in SpEL and act on the beans which were injected.
It was what I thought a pretty neat project, but I had to shelf it to get some other things done.
Annotations are essentially metadata. Taking database metadata for example, it would be weird if Oracle database would turn into MySQL, right? Here is the article about Annotation Transformers in TestNG. Didn't try it myself, but I think it could be implemented in some way or another.
AFAIK, you can call a method from the Annotation.
#FindBy(how = How.Id, id=getProp())
private String pageHeader;
private String getProp()
{
String prop = //whatever way you want to get the value
return prop;
}
Doesn't that work?