4 Replies Latest reply: Oct 30, 2007 11:09 AM by Greybird-Oracle RSS

    NullPointerException on polymorphic model registration

    575203
      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
          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
            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
              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-Oracle
                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