Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objectify List<Ref<T>> not serialized by Google App Engine endpoints

Hi have two related Entities: Customers and Cars. Each Customer can own several Cars

This is a summarized view of the entities:

public class Customer 
{
    //Inner classes for partial loads
    public static class NoCars{}

    @Id protected String id;
    private String fullName;
    @Load(unless=NoCars.class) private List<Ref<Car>> cars;
}

public class Car
{
    @Id private Long id;
    private String makeAndModel;
    private String plateNumber;
}

And this is a method that retrieves a Customer from datastore and all the Cars he owns:

public Customer getCustomer(@Named("id") String customerId)
{
    Customer customer =  ofy().load().type(Customer.class).id(customerId).now();
    if (customer==null)
        throw new NotFoundException("customer not found");
    else
        return customer;
}

endpoints.sh cannot implement this, as the type List <Ref<Car>> included in the return type Customer is not supported, but I found this funny workaround:

I created the class CustomerPOJO

public class CustomerPOJO
{
    private String fullName;
}

and modified the class Customer to extend from it

public class Customer extends CustomerPOJO
{
    //Inner classes for partial loads
    public static class NoCars{}

    @Id protected String id;
    @Load(unless=NoCars.class) private List<Ref<Car>> cars = new ArrayList<>();
}

and the getter method to be:

public CustomerPOJO getCustomer(@Named("id") String customerId)
{
    Customer customer =  ofy().load().type(Customer.class).id(customerId).now();
    if (customer==null)
        throw new NotFoundException("customer not found");
    else
        return customer;
}

Note that the method declares CustomerPOJO as return type, but it actually returns a "full" Customer!!!

This works in AppEngine 1.8.0 + Objectify 4.0 RC2. It gets the customer data and all the cars he owns with their properties (makeAndModel and plateNumber) in a single call.

The issue came after upgrading to 1.8.9. endpoints.sh still works, as the return type is fully compatible, but i get an exception at runtime, when parsing the Customer objecto to JSON.

java.io.IOException: com.google.appengine.repackaged.org.codehaus.jackson.map.JsonMappingException: Direct self-reference leading to cycle (through reference chain: entity.Customer["cars"]->java.util.ArrayList[0]->com.googlecode.objectify.impl.ref.LiveRef["key"]->com.googlecode.objectify.Key["root"])

Any other workaround that works in 1.8.9? Is @ApiTransformer the only choice?

like image 831
cvigo Avatar asked Feb 04 '14 14:02

cvigo


1 Answers

The solution was pretty simple... Just hide the Ref field. It does now provide any value to the API consumer and it just create trouble to the parser:

I replaced the List<Ref<Car>> getter and setter by:

public List<Car> getCars()
{
    List<Car> ret = new ArrayList<>();
    Iterator<Ref<Car>> it = cars.iterator();

    while (it.hasNext())
        ret.add(it.next().getValue());
    return ret;
}

public void setCars(List<Car> newCars)
{
    Iterator<Car> it = newCars.iterator();

    while (it.hasNext())
        addCar(it.next());
}

That's all

like image 72
cvigo Avatar answered Nov 15 '22 15:11

cvigo