views:

1610

answers:

3

I am randomly getting an org.datanucleus.exceptions.ClassNotPersistableException when I try to perform a query on the local JDO data store of my GWT/App Engine application. This only happens when I run the application on Hosted mode. When I deploy it to the Google App Engine everything works perfectly.

Stack Trace:

org.datanucleus.exceptions.ClassNotPersistableException: The class "com.wayd.server.beans.WinePost" is not persistable. This means that it either hasnt been enhanced, or that the enhanced version of the file is not in the CLASSPATH (or is hidden by an unenhanced version), or the Meta-Data/annotations for the class are not found.
    at org.datanucleus.jdo.NucleusJDOHelper.getJDOExceptionForNucleusException(NucleusJDOHelper.java:305)
    at org.datanucleus.ObjectManagerImpl.getExtent(ObjectManagerImpl.java:3700)
    at org.datanucleus.jdo.JDOPersistenceManager.getExtent(JDOPersistenceManager.java:1515)
    at com.wayd.server.WinePostServiceImpl.getPosts(WinePostServiceImpl.java:212)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:585)
    at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:527)
    ... 25 more
Caused by: org.datanucleus.exceptions.ClassNotPersistableException: The class "com.wayd.server.beans.WinePost" is not persistable. This means that it either hasnt been enhanced, or that the enhanced version of the file is not in the CLASSPATH (or is hidden by an unenhanced version), or the Meta-Data/annotations for the class are not found.
    at org.datanucleus.ObjectManagerImpl.assertClassPersistable(ObjectManagerImpl.java:3830)
    at org.datanucleus.ObjectManagerImpl.getExtent(ObjectManagerImpl.java:3693)
    ... 32 more)

WinePost class is a very simple JDO persistence capable class:

@PersistenceCapable(identityType = IdentityType.APPLICATION) public class WinePost {

@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;

@Persistent
private User author;

@Persistent
private String grape;

@Persistent
private String comment;

public WinePost(final User author, final String grape,
        final String comment) {
    super();
    this.grape = grape;
    this.comment = comment;
}

public User getAuthor() {
    return author;
}

public void setAuthor(final User author) {
    this.author = author;
}

public Long getId() {
    return id;
}

public void setId(final Long id) {
    this.id = id;
}

public String getGrape() {
    return grape;
}

public void setGrape(final String grape) {
    this.grape = grape;
}

public String getComment() {
    return comment;
}

public void setComment(final String comment) {
    this.comment = comment;
}

public String getUserNickname() {
    String retVal = null;
    if (author != null) {
        retVal = author.getNickname();
    }
    return retVal;
}

public WinePostModel getWinePostModel() {
    final WinePostModel winePostModel = new WinePostModel(grape, vintage, getUserNickName());
    return winePostModel;
}

}

The datastore query is performed by the following method:

public ArrayList<WinePostModel> getPosts() {
        final ArrayList<WinePostModel> posts = new ArrayList<WinePostModel>();
        final PersistenceManager persistenceManager = PMF.get()
        .getPersistenceManager();

        final Extent<WinePost> winePostExtent = persistenceManager.getExtent(
                WinePost.class, false);
        for (final WinePost winePost : winePostExtent) {
            posts.add(winePost.getWinePostModel());
        }
        winePostExtent.closeAll();

        return posts;
    }
+1  A: 

"The class "com.wayd.server.beans.WinePost" is not persistable. This means that it either hasnt been enhanced, or that the enhanced version of the file is not in the CLASSPATH (or is hidden by an unenhanced version), or the Meta-Data/annotations for the class are not found."

So why not check each of those 3 conditions ? One of them is true.

DataNucleus
I checked them and everything seems to be fine. The app works fine when I deploy it on the App Engine and I get this exception randomly.
Otavio
perhaps report a bug? java support is still in "beta"
Chii
If that message appears then one of those 3 things are true. Nothing else it can be. There are bytecode decompilers available to check enhancement. The CLASSPATH can easily be printed out with respect to whether metadata/annotations are present. The log (at DEBUG level) can easily be checked and gives large amounts of information, about what metadata is found.
DataNucleus
-1 The question was asked in good faith. You've essentially told him to read the manual.
Eric W.
And the manual is incorrect ? Please provide your evidence that it wasn't, and i'd be happy to update it. As the other reply says, change a class and it gets re-enhanced ... so the class wasn't enhanced. And then you trace back and find that GAE/J plugin for Eclipse is unreliable in detecting when a class is changed.
DataNucleus
+2  A: 

I've seen something very similar before and it was related to the persistantManager being called by different methods at the same time. This happens 'cause the pm is supposed to be a single instance (which is not a singleton so you have to manage it yourself).

Conditions could change between the development environment and the production one once deployed, so that could explain also what you're seeing.

The problem was solved by using synchronized in the declarations of methods which use the pm.

Maybe it's not your case but you might wanna give it a shot.

JohnIdol
+1 I'm not sure if I follow your distinction between a singleton and a single instance. But the point about not having two open PersistenceManagers is a good one and seems to have been the source of my own difficulties.
Eric W.
this is a common problem indeed - a singleton has a private constructor and a getInstance method which checks if there is already an instance and returns that if so otherwise creates a new instance. A single instance it's a normal class which is meant to be instatiated only once throughout the application, meaning that some kind of wrapper is needed to make sure you are always using the same. Either that or you use synchronized in the methods that use that given class.
JohnIdol
+3  A: 

I am pretty sure there's nothing wrong with your code. The reason you get this error is because of a problem with the Datanucleus enhancer.

If you see this error, quit Jetty and check the console inside Eclipse (you'll need to select the correct console in the little toolbar above the console window). It should say something like:

DataNucleus Enhancer (version 1.1.4) : Enhancement of classes DataNucleus Enhancer completed with success for X classes. Timings : input=547 ms, enhance=76 ms, total=623 ms. Consult the log for full details

... where X is the number of classes it has processed. The number should be equal to the number of 'entity' classed you have defined.

But sometimes, for some reason it will say 0 which is why you get the ClassNotPersistableException error.

To fix, open an entity class and change something (add a space or something) and save. Check the console until it says that it has enhanced all of your entity classes. Then restart the debugger and try again.

moin