This discussion is archived
4 Replies Latest reply: Oct 30, 2007 9:09 AM by greybird RSS

NullPointerException on polymorphic model registration

575203 Newbie
Currently Being Moderated
I'm attempting to persist this heavily polymorphic heirarchial tree of mine:
@Persistent interface Node
@Persistent interface Child
@Persistent interface Branched
@Persistent abstract class AbstractNode {
  @PrimaryKey(sequence = "Node.id")
  private Long id;
}
@Entity class Root extends AbstractNode implements Branched {
  @SecondaryKey(relate = Relationship.ONE_TO_MANY, relatedEntity = Child.class)
  private Long[] children = new Long[2];
}
@Entity class Branch extends AbstractNode implementes Branched, Child {
  @SecondaryKey(relate = Relationship.ONE_TO_MANY, relatedEntity = Child.class)
  private Long[] children = new Long[2];
  @SecondaryKey(relate = Relationship.MANY_TO_ONE, relatedEntity = Branched.class)
  private Long parent;
}
@Entity class Leaf extends AbstractNode implements Child {
  @SecondaryKey(relate = Relationship.MANY_TO_ONE, relatedEntity = Branched.class)
  private Long parent;
}
This is the problem I hit:
java.lang.NullPointerException
     at com.sleepycat.persist.model.AnnotationModel.updateEntityInfo(AnnotationModel.java:329)
     at com.sleepycat.persist.model.AnnotationModel.getClassMetadata(AnnotationModel.java:134)
     at com.sleepycat.persist.model.EntityModel.registerClass(EntityModel.java:99)
     at org.apache.lucene.clusterer.tree.Tree.<init>(Tree.java:82)


Where Tree.java is:
81:    EntityModel model = new AnnotationModel();
82:    model.registerClass(Node.class);
83:    model.registerClass(Child.class);
I set it up manually because I hit the 'same' exception from my inital getPrimaryIndex(.. when I don't:

java.lang.NullPointerException
     at com.sleepycat.persist.model.AnnotationModel.updateEntityInfo(AnnotationModel.java:329)
     at com.sleepycat.persist.model.AnnotationModel.getClassMetadata(AnnotationModel.java:134)
     at com.sleepycat.persist.model.AnnotationModel.getEntityMetadata(AnnotationModel.java:71)
     at com.sleepycat.persist.impl.Store.checkEntityClass(Store.java:1076)
     at com.sleepycat.persist.impl.Store.getRelatedIndex(Store.java:478)
     at com.sleepycat.persist.impl.Store.openSecondaryIndex(Store.java:653)
     at com.sleepycat.persist.impl.Store.openSecondaryIndexes(Store.java:616)
     at com.sleepycat.persist.impl.Store.getPrimaryIndex(Store.java:368)
     at com.sleepycat.persist.EntityStore.getPrimaryIndex(EntityStore.java:245)
     at org.apache.lucene.clusterer.tree.Tree.<init>(Tree.java:96)
90://    model.registerClass(Cluster.class);
91://    storeConfig.setModel(model);
92:
93:    store = new EntityStore(env, "tree", storeConfig);
94:
95:
96:    rootsById = store.getPrimaryIndex(Long.class, Root.class);
Please advice. Code is available.


/Tony

Not too off topic, I saw in an earlier post (Re: Polymorphism and DPL that someone had problems persisting their polymorphic subclasses via a single method. I solved that using a visitor pattern:
public interface Node {
  public abstract void accept(Visitor visitor);
}

public class Tree  {
  private final Visitor putVisitor = new Visitor() {
    public void visit(Leaf leaf) {
        leafsById.put(leaf);
    }
    public void visit(Branch branch) {
        branchesById.put(branch);
    }
    public void visit(Root root) {
        rootsById.put(root);
    }
  }

  public void doTheThing(Node node) {
    node.accept(putVisitor);
  }
}
Message was edited by:
Tony Clifton
Formatted text
  • 1. Re: NullPointerException on polymorphic model registration
    575203 Newbie
    Currently Being Moderated
    In fact, the simplest test case I could write throws the same exception:
    @Persistent
    public interface Foo {
    }

    @Entity
    public class FooImpl implements Foo {
      @PrimaryKey
      private Long id;
      public Long getId() {
        return id;
      }
      public void setId(Long id) {
        this.id = id;
      }
    }

    public class FooRun {
      public static void main(String[] args) throws Exception {
        EntityModel model = new AnnotationModel();
        model.registerClass(Foo.class); // BANG!
      }
    }
  • 2. Re: NullPointerException on polymorphic model registration
    575203 Newbie
    Currently Being Moderated
    The problem turned out to be SecondaryKey(relatedEntity = My@PersistentInterface)

    So I just have to skip those secondary keys.

    It would be really nice with @PrimaryKey at method-level.


    fin.
  • 3. Re: NullPointerException on polymorphic model registration
    575203 Newbie
    Currently Being Moderated
    Final note,

    I ended up flattening out the polymorphic architecture to a single class called Node. The problem was the access to Child Branched.getChildren[], where I could not figure out a smart way to load the child without knowing what implementation subclass of Child (Branch or Leaf) it was.

    Is there a de facto solution for this in JE?

    If all subclasses share the same primary key sequence, and mine usually does, it is really just a matter of a reversed lookup. Perhaps assigning segments one can seek using a simple binsearch in memory for even faster access.
  • 4. Re: NullPointerException on polymorphic model registration
    greybird Expert
    Currently Being Moderated
    Hi Tony,
    The problem turned out to be
    SecondaryKey(relatedEntity = My@PersistentInterface)
    Right. A relatedEntity must be an entity class. Interfaces may not be @Persistent or @Entity.

    Sorry about the NPE, you should get a more meaningful exception when trying to make an interface @Persistent. I'll fix that and also clarify it in the javadoc.
    It would be really nice with @PrimaryKey at
    method-level.
    Methods are not used to define persistence in the DPL for a couple reasons:

    1) Simplicity. Only fields are persistent, which is a simple rule. However, it is limiting. For example, you can't have a computed secondary key or a secondary key field nested in a sub-object, except by adding a redundant field in the @Entity class. For now, I suggest using the redundant field approach.

    2) Performance. A key design goal for DPL is to avoid un-marshaling entities in the middle of JE read and write operations, in order to extract secondary keys from entity data. DPL stores secondary key values at the front of the record data, so extracting the value is very fast and does not require a full un-marshal. However, perhaps key values returned by annotated methods could be stored redundantly at the front of the record. Or if that isn't feasible, we simply say that you pay a performance penalty for using methods rather than fields to define persistence. More design work is needed, but it is probably feasible.

    For now I'm going to put this aside and list it as something to consider in the future as a DPL enhancement. Please don't expect this to be added soon, but we will be thinking about it.

    --mark