You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@openjpa.apache.org by atanamir <st...@hyarros.com> on 2010/05/14 04:16:15 UTC

Generics Support

Hi devs,

I'd like to start work on adding generics support into OpenJPA. 
Specifically, I'd like to allow the following:

@MappedSuperclass
public class Identifiable<T> {
  private T id; 

  @Id
  public T getId() { return id; }
  private void setId(T i) { id = i; }
}

@Entity
public class Concrete extends Identifiable<Integer> {
  ...
}

@Entity
public class CompoundConcrete extends Identifiable<CompoundConcrete.PK> {
  @Embeddable 
  protected static class PK {
     ... 
  }
}

Currently, for example, the code fails when trying to get the metadata for
Concrete because it believes that Id is of type java.lang.Object, which "is
not a persistent type".  This is because the getReturnType() method in
java.reflection.Method returns java.lang.Object if the return type is a
generic; additional API calls must be used.

>From my searches I've not found a precise bug that matches this exact
problem, but I've found several other bugs related to generics that have not
been assigned to anyone yet.  Hopefully, I'll be able to fix all the
generics-related bugs.  

With this in mind, is there any potential problems I should be aware of
before attempting to hack at the code?  Anything you guys can foresee that
makes generics impossible?  From what I've read, nothing in the JPA spec
refers to generics directly at all; but since Generics is a compile-time
transformation, I don't see why it shouldn't work with JPA/OpenJPA.  

Shall I open a bug with this exact problem also, before I get going?

Thanks,
atanamir
-- 
View this message in context: http://openjpa.208410.n2.nabble.com/Generics-Support-tp5049201p5049201.html
Sent from the OpenJPA Developers mailing list archive at Nabble.com.

Re: Generics Support

Posted by atanamir <st...@hyarros.com>.
Hi Kevin/Other devs,

Thanks for the reply and encouragement.  I've been investigating / figuring
out the OpenJPA code for the past few days, and it seems it'll require a
rather significant overhaul of the Metadata-related classes for this to work
right, in addition to the way the metadata code is called from other sites. 
I'll try to describe the basic work that needs to be done here, and
hopefully you guys can help me determine the right place to start (since
there's a lot of code, hopefully someone more experienced can give me some
pointer on exactly where to begin).  

*** QUICK INTRO TO GENERICS REFLECTION *** 

Given the following class structure:

class Base<T> { T field; } 
class A extends Base<Something> { ... }
class B extends Base<AnotherThing> { ... }
class C extends Base<SomethingFancy<BlahBlah> > { ... }
class D extends A { ... }

Reflection will return Base<T> as the Class for getSuperclass() when called
on an instance of any of A, B, or C (as expected).  All of the Class
instance representing the Base<T> is equal() for instances of A, B, and C. 
Furthermore, there will be no type argument information in the Class
instance for Base<T>.  This information is kept in the class instance for A,
B, and C itself, and can be accessed using getGenericSuperclass().  When the
field 'field' is accessed in any of the derived classes, reflection will
simply return java.lang.Object for the type of the field with standard APIs. 
There's nothing to hint that it's acutally a generic -- this is because of
Java's type erasure.  An additional API call, getGenericType() will let you
know the type of the field is acutally a TypeVariable.  The resolution of
this Type Variable to a concrete class must be done at the level of derived
classes, matching up the TypeVariable with the supplied actual type
arguments.  

In the case of class D, which extends A, getting the type of field 'field'
is still going to be java.lang.Object.  One must acutally traverse up into A
to get the type arguments, and traverse up again into Base to determine that
Base is the container for field 'field'.  

So we'll need a nice chunk of logic to resolve type variables and match them
up with the type arguments, since they can be separated by large distances
in the type hierarchy, e.g.:

class A<T> { T x; } 
class B<T> extends A<T> { ... }
class C extends B<SomeClass> { ... }
class D  extends C { ... }
class E extends D<AnotherClass> { ... } <-- try to resolve the type of 'x'
from here!

*** END JAVA API TALK ***

*** START OpenJPA CODE TALK ***

Given the scenario described above, it's quite troublesome in OpenJPA right
now.  A class is parsed from bottom up, recursively.  If E is being parsed,
it will first parse the superclasses and construct their metadata before
continuing with E itself.  The problem is, the construction of the
superclass' field metadata will require E itself to figure out the type
arguments (or just classes farther down in the hierarchy).  Passing the
whole class tree along with the metadata construction calls seems kind of
clumsy.  That's the first problem to figure out.

Secondly, is the accessing of the 'type' of a field.  Because a superclass
will only have *one* ClassMetaData instance, every time one of the members
are accessed in that class, a subclass may need to be passed in for
context/resolution purposes.  If I have the class list:

class Base<T> { @Id T getId(); void setId(T id); }
class B extends Base<Integer> { ... }

I'll need to let the metadata for Base know that I'm working within the
context of B, so it can access the "extends Base<Integer>" portion of the
code to know what T is.  This is the second part of the work.

Anyways, I hope my message isn't too loquatious.  Just trying to see if
anyone has tips before i try to hack at the code alone.  Also entertaining
tips on how to make the implementation clean (e.g. i'm not passing tons of
class hierarchy / context information around).  I'm toying currently with
the idea with adding another layer between FieldMetaData and the type of the
field with something like :

abstract class Type { public Class<?> resolve(Class<?> context); }
class ConcreteType { ... }
class GenericType { ... }

Hopefully the names will explain themselves.  ConcreteType is used for a
non-generic field, and can have a lightweight implementtation , while all
the generics resolution work is done in the GenericType class.  Then the
FieldMetaData will only contain instances of Type, instead of Class<?>.

--Atanamir
-- 
View this message in context: http://openjpa.208410.n2.nabble.com/Generics-Support-tp5049201p5061239.html
Sent from the OpenJPA Developers mailing list archive at Nabble.com.

Re: Generics Support

Posted by Kevin Sutter <kw...@gmail.com>.
Hi Atanamir,
I'm not a Generics expert, but I don't see any roadblocks with what you are
attempting.  You're basically trying to use Generics to create a template
Class for creating Entity types, correct?  Based on your notes, it sounds
like the tricky part will to know when to use the additional reflection
methods to get the root types.  Or, maybe there's some "signal" to indicate
that the root type has been found.

The best way to start is to open a (feature) JIRA.  Add your notes and
findings to this JIRA.  This way, you can start a conversation and
discussion on the topic and keep it all in one place.  When you think you
have something to share, then you could post a patch to the JIRA and ask for
reviews/comments.  Hopefully, you'll find a "committer partner" through this
process and they can help guide you the process of getting your changes into
the code base.

I'm assuming you are looking at the trunk release for this enhancement?

Good luck and Thanks for contributing!
Kevin

On Thu, May 13, 2010 at 9:16 PM, atanamir <st...@hyarros.com> wrote:

>
> Hi devs,
>
> I'd like to start work on adding generics support into OpenJPA.
> Specifically, I'd like to allow the following:
>
> @MappedSuperclass
> public class Identifiable<T> {
>  private T id;
>
>  @Id
>  public T getId() { return id; }
>  private void setId(T i) { id = i; }
> }
>
> @Entity
> public class Concrete extends Identifiable<Integer> {
>  ...
> }
>
> @Entity
> public class CompoundConcrete extends Identifiable<CompoundConcrete.PK> {
>  @Embeddable
>  protected static class PK {
>     ...
>  }
> }
>
> Currently, for example, the code fails when trying to get the metadata for
> Concrete because it believes that Id is of type java.lang.Object, which "is
> not a persistent type".  This is because the getReturnType() method in
> java.reflection.Method returns java.lang.Object if the return type is a
> generic; additional API calls must be used.
>
> From my searches I've not found a precise bug that matches this exact
> problem, but I've found several other bugs related to generics that have
> not
> been assigned to anyone yet.  Hopefully, I'll be able to fix all the
> generics-related bugs.
>
> With this in mind, is there any potential problems I should be aware of
> before attempting to hack at the code?  Anything you guys can foresee that
> makes generics impossible?  From what I've read, nothing in the JPA spec
> refers to generics directly at all; but since Generics is a compile-time
> transformation, I don't see why it shouldn't work with JPA/OpenJPA.
>
> Shall I open a bug with this exact problem also, before I get going?
>
> Thanks,
> atanamir
> --
> View this message in context:
> http://openjpa.208410.n2.nabble.com/Generics-Support-tp5049201p5049201.html
> Sent from the OpenJPA Developers mailing list archive at Nabble.com.
>