2

I am using Google App engine1.9.3, Eclipse, Objectify5.03. My Class is as follows:

import com.googlecode.objectify.Ref;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Load;

@Entity
public class User {

@Id private Long userId;
private String userName;
@Load private Ref<UserDetails> userDetails;
@Load private Ref<UserPassword> userPassword;

//getters & setters 

}

When I try creating the google endpoint for this class thru Eclipse, I get the following error: java.lang.IllegalArgumentException: Parameterized type com.googlecode.objectify.Ref not supported

This is my first attempt at Objectify.

Any ideas what I am doing wrong. From whatever I have read so far GAE endpoints and Objectify should work, correct?

DavidC
  • 185
  • 2
  • 10
  • You should probably tag this question with something related to Endpoints, because that is the community that can answer your question. – stickfigure Jul 07 '14 at 02:58
  • I'm guessing your `User` class will have a getters to return `UserDetails` and `UserPassword` objects rather than their respective `Ref<>`s? If so, you may like to look at the `@ApiResourceProperty` in [Endpoint annotation docs](https://developers.google.com/appengine/docs/java/endpoints/annotations#apiresourceproperty) as you may need to tell Cloud Endpoints to ignore your private Objectify `Ref<>` members. – tx802 Jul 07 '14 at 15:03

2 Answers2

10

Google Cloud Endpoints is unable to serialise the Ref object because it is an arbitrary object defined by objectify, therefore not supported as the error indicates.

This is known limitation with Cloud Endpoints in that it does not allow custom objects to be used. There is a whole discussion thread on this point in particular if you're interested: Cloud endpoints .api generation exception when using objectify (4.0b1) parameterized key

You will have to annotate your methods with @ApiResourceProperty and set its ignored attribute to true as illustrated in the code below:

import com.googlecode.objectify.Ref;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Load;
import com.google.api.server.spi.config.AnnotationBoolean;
import com.google.api.server.spi.config.ApiResourceProperty;

@Entity
public class User 
{
    @Id private Long userId;
    private String userName;
    @Load private Ref<UserDetails> userDetails;
    @Load private Ref<UserPassword> userPassword;

    //getters & setters
    @ApiResourceProperty(ignored = AnnotationBoolean.TRUE) 
    public UserDetail getUserDetails(){
    }

    @ApiResourceProperty(ignored = AnnotationBoolean.TRUE) 
    public UserPassword getUserPassword(){
    }
}

If you still want to use the data held in those objects then consider adding some fields to your class to hold the data and initialise them after your User class has finished loading like so:

@Ignore String firstName;
@OnLoad
void trackUserDetails() 
{ 
    this.firstName = getUserDetails().getFirstName(); 
    // add more code here to set other fields, you get the gist
}

But in my opinion a better approach would be to reconsider the design of your class, or rather rethink what you're trying to do.

elcid
  • 574
  • 4
  • 10
  • elcid, I understand that this is a simple implementation and I can have all the fields in the same entity but I was trying to find if Objectfy + Google endpoints will be able to support one-to-one and one-to-many relationships. For example if my User has a list of accounts, I was planning to add List> accounts. And when my front-end retrieves the user from the datastore, the list of accounts should also be available with the user. Are we saying that this is not possible this way and I need to add a separate query on to get the accounts for a user. – DavidC Jul 07 '14 at 23:24
  • I added @ApiResourceProperty(ignored = AnnotationBoolean.TRUE) to the getters and setters as detailed by elcid above. But when I try to create Cloud endpoint class for User thru Eclipse, I still get 'Caused by: java.lang.IllegalArgumentException: Parameterized type com.googlecode.objectify.Ref not supported'. If anyone has any other suggestions, please let me know. – DavidC Jul 09 '14 at 19:08
  • @DavidC As far as I know, Google Cloud Endpoints (GCE) does not know how to serialise/de-serialise the custom Ref object defined by objectify, hence why the compilation fails because it doesn't know how to send your list of accounts to to your clients. What you can try do is send to your client your actual entities instead of Ref. Regards your most recent question, try annotating the Ref property with @ApiResourceProperty(ignored = AnnotationBoolean.TRUE). – elcid Jul 09 '14 at 21:11
0

ApiResourceProperty annotation does not work for Google Emdpoints+Objectify combinations as Ref or Key is Objectify specific class and Google Endpoints does not recognize them and will give a error when you try to generate the Client Libraries. I changed the User class as below.

 @Id private Long userId;
 @Index private String userName;
 @Load private UserDetails userDetails;
 @Load private UserPassword userPassword;
 @Load private ArrayList<Account> userAccounts;

 //getters and setters

When I retrieve the user by user name as below, I get the User, UserDetails, UserPassword and also the list of User accounts thru the getters (in one shot)

@ApiMethod(name = "getUserByName", path = "get_user_by_name")
public User getUserByName(@Named("userName") String userName) {

    User user = null;
    try {
         user = ofy().load().type(User.class).filter("userName", userName).first().now();
         if(user != null)
             log.info("UserEndpoint.getUserByName...user retrieved="+user.getUserId());
         else
             log.info("UserEndpoint.getUserByName...user is null");
    } catch(Exception e) {
        log.info("UserEndpoint.getUserByName...exception="+e.getMessage());
    }
    return user;
}

When I use the Datastore Viewer on Google Console to view the data, I see some entries in the userDetails, userPassword and Accounts columns in the User table. I assume that these are references to the actual data in their respective tables and not a copy of the data itself. Hope this helps.

DavidC
  • 185
  • 2
  • 10
  • what you've done here is create a list of embedded entities, whereas when you work with @Ref you have a relationship to another entity.. thats why now you see the complete object in 1 column in de DS viewer. – Pega88 Aug 13 '15 at 06:45