[play-framework] GAE + Siena leads to IllegalArg on private fields : workaround + explanation + Play interrogation on the private default constructor created behind the scene

57 views
Skip to first unread message

Pascal Voitot Dev

unread,
Mar 14, 2011, 4:12:41 AM3/14/11
to play-fr...@googlegroups.com
Ok I found the problem!
FIRST, THE QUICK WORKAROUND: DEFINE A DEFAULT CONSTRUCTOR in your siena class extending siena.Model

THEN, THE EXPLANATION:
since recently, Play enhances all classes with a default private constructor if there is no default constructor in the class.

public class PropertiesEnhancer extends Enhancer {
...
            if (!hasDefaultConstructor && !ctClass.isInterface()) {
                CtConstructor defaultConstructor = CtNewConstructor.make("private " + ctClass.getSimpleName() + "() {}", ctClass);
                ctClass.addConstructor(defaultConstructor);
            }
...
}


Thus, when Siena calls the Class<T>.newInstance(), it generates :

Caused by: java.lang.IllegalAccessException: Class siena.gae.GaePersistenceManager can not access a member of class models.City with modifiers "private"
    at java.lang.Class.newInstance0(Class.java:349)
    at java.lang.Class.newInstance(Class.java:308)
    at siena.gae.GaePersistenceManager.mapEntities(GaePersistenceManager.java:210)

because newInstance can't call a private constructor...

SHOULD PLAY DECLARE A PRIVATE DEFAULT CONSTRUCTOR?
I wonder why Play declare the default constructor as private... why not public?
It forces people to call newInstance using the following code
Constructor c = type.getDeclaredConstructor();
c.setAccessible(true);
Object model = c.newInstance();

instead of direct call to clazz.newInstance()

Is it something mandatory (or strongly advised) in Java to call getDeclaredConstructor() to make newInstance() ?

What's your opinions?

Pascal


On Mon, Mar 14, 2011 at 12:45 AM, Pascal Voitot Dev <pascal.v...@gmail.com> wrote:


On Sun, Mar 13, 2011 at 11:57 PM, Nicolas Girardin <ngir...@gmail.com> wrote:
Hi all,

David, I found this error on a bigger project, so I started a new one,
to be sure that no private fields appears anywhere.

Pascal, I'm using play-1.2-preview, downloaded from the
http://download.playframework.org/releases/ page. I can't find any
github 1.2 repo, to test with an much up-to-date version.
I added Siena in the dependencies.yaml and used "play dependencies".
Are you a Siena commiter?


yes I am a siena committer
 
I won't try to join my project to this message as it weight 14 MB, but
you can download it from http://dl.free.fr/lVoqWbkWY (let me know if
you're experiencing very slow downloads). It's eclipsified so an
import should do the job.

Thanks for taking time to inverstigate my issue! :)

Nicolas


2011/3/13 Pascal Voitot Dev <pascal.v...@gmail.com>:
> I think something has changed in play because I use siena quite often with
> Play and doesn't have any problem.
> But I'm still using Play 1.1... not a very recent version!
> anyway as I'm trying to finalize Siena 1.0, it's the right time to correct
> such things ;)
>
> On Sun, Mar 13, 2011 at 8:31 PM, David González <alle...@gmail.com> wrote:
>>
>> Opps, they are public.... ignore my previous comment then !
>>
>> On Sun, Mar 13, 2011 at 3:29 PM, David González <alle...@gmail.com>
>> wrote:
>>>
>>> > can not access a member of class models.City with modifiers "private"
>>>
>>> I think the fields are supposed to be public if I'm not mistaken.. try it
>>> out
>>> On Sun, Mar 13, 2011 at 8:29 AM, Pascal Voitot Dev
>>> <pascal.v...@gmail.com> wrote:
>>>>
>>>> if you could give me a small project example so that I can reproduce the
>>>> problem, it would be great!
>>>>
>>>> Pascal
>>>>
>>>> On Sun, Mar 13, 2011 at 12:58 PM, Pascal Voitot Dev
>>>> <pascal.v...@gmail.com> wrote:
>>>>>
>>>>> you are the second one to report this kind of problem that never
>>>>> appeared before...
>>>>>
>>>>> What version of Play do you use?
>>>>> Is it a recent one?
>>>>>
>>>>> Pascal
>>>>>
>>>>> On Sun, Mar 13, 2011 at 10:11 AM, Nicolas GIRARDIN
>>>>> <ngir...@gmail.com> wrote:
>>>>>>
>>>>>> Hi Players!
>>>>>> I'm stuck from hours on a strange exception, maybe one of you could
>>>>>> help?  I'm using the Google App Engine and Siena modules to store simple
>>>>>> "City" objects and retrieve them.
>>>>>> My models/City.java looks like this:
>>>>>> package models;
>>>>>> import siena.Generator;
>>>>>> import siena.Id;
>>>>>> import siena.Model;
>>>>>> import siena.Query;
>>>>>> public class City extends Model {
>>>>>> @Id(Generator.AUTO_INCREMENT)
>>>>>> public Long id;
>>>>>> public String name;
>>>>>> public String code;
>>>>>> public City(String name, String code) {
>>>>>> this.name = name;
>>>>>> this.code = code;
>>>>>> }
>>>>>> public static Query<City> all() {
>>>>>> return Model.all(City.class);
>>>>>> }
>>>>>> }
>>>>>> My controller is as simple as the model:
>>>>>> package controllers;
>>>>>> import models.City;
>>>>>> import play.mvc.Controller;
>>>>>> public class Application extends Controller {
>>>>>> public static void index() {
>>>>>> City strasbourg = new City("Strasbourg", "SXB");
>>>>>> City dubai = new City("Dubai", "DXB");
>>>>>> strasbourg.insert();
>>>>>> dubai.insert();
>>>>>> renderJSON(City.all().fetch());
>>>>>> }
>>>>>> }
>>>>>> Launching "play run" and going to "http://localhost:9000/", leads to
>>>>>> the given error:
>>>>>> Internal Server Error (500) for request GET /
>>>>>> Execution exception (In /app/controllers/Application.java around line
>>>>>> 16)
>>>>>> SienaException occured : java.lang.IllegalAccessException: Class
>>>>>> siena.gae.GaePersistenceManager can not access a member of class models.City
>>>>>> with modifiers "private"
>>>>>> play.exceptions.JavaExecutionException:
>>>>>> java.lang.IllegalAccessException: Class siena.gae.GaePersistenceManager can
>>>>>> not access a member of class models.City with modifiers "private"
>>>>>> at play.mvc.ActionInvoker.invoke(ActionInvoker.java:225)
>>>>>> at Invocation.HTTP Request(Play!)
>>>>>> Caused by: siena.SienaException: java.lang.IllegalAccessException:
>>>>>> Class siena.gae.GaePersistenceManager can not access a member of class
>>>>>> models.City with modifiers "private"
>>>>>> at
>>>>>> siena.gae.GaePersistenceManager.mapEntities(GaePersistenceManager.java:220)
>>>>>> at siena.gae.GaeQuery.map(GaeQuery.java:79)
>>>>>> at siena.gae.GaeQuery.fetch(GaeQuery.java:85)
>>>>>> at controllers.Application.index(Application.java:16)
>>>>>> at
>>>>>> play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:432)
>>>>>> at
>>>>>> play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:427)
>>>>>> at play.mvc.ActionInvoker.invoke(ActionInvoker.java:155)
>>>>>> ... 1 more
>>>>>> Caused by: java.lang.IllegalAccessException: Class
>>>>>> siena.gae.GaePersistenceManager can not access a member of class models.City
>>>>>> with modifiers "private"
>>>>>> at java.lang.Class.newInstance0(Class.java:349)
>>>>>> at java.lang.Class.newInstance(Class.java:308)
>>>>>> at
>>>>>> siena.gae.GaePersistenceManager.mapEntities(GaePersistenceManager.java:213)
>>>>>> ... 7 more
>>>>>> Replacing "renderJSON(City.all().fetch());" by
>>>>>> "renderJSON(City.all().count());" works as exepected and no exception is
>>>>>> thrown...
>>>>>> Any idea? :)
>>>>>> Nicolas GIRARDIN
>>>>>>
>>>>>>
>>>>>> --
>>>>>> You received this message because you are subscribed to the Google
>>>>>> Groups "play-framework" group.
>>>>>> To post to this group, send email to play-fr...@googlegroups.com.
>>>>>> To unsubscribe from this group, send email to
>>>>>> play-framewor...@googlegroups.com.
>>>>>> For more options, visit this group at
>>>>>> http://groups.google.com/group/play-framework?hl=en.
>>>>>
>>>>
>>>> --
>>>> You received this message because you are subscribed to the Google
>>>> Groups "play-framework" group.
>>>> To post to this group, send email to play-fr...@googlegroups.com.
>>>> To unsubscribe from this group, send email to
>>>> play-framewor...@googlegroups.com.
>>>> For more options, visit this group at
>>>> http://groups.google.com/group/play-framework?hl=en.
>>>
>>>
>>>
>>> --
>>> Best wishes,
>>> David
>>
>>
>>
>> --
>> Best wishes,
>> David
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "play-framework" group.
>> To post to this group, send email to play-fr...@googlegroups.com.
>> To unsubscribe from this group, send email to
>> play-framewor...@googlegroups.com.
>> For more options, visit this group at
>> http://groups.google.com/group/play-framework?hl=en.
>
> --
> You received this message because you are subscribed to the Google Groups
> "play-framework" group.
> To post to this group, send email to play-fr...@googlegroups.com.
> To unsubscribe from this group, send email to
> play-framewor...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/play-framework?hl=en.
>

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To post to this group, send email to play-fr...@googlegroups.com.
To unsubscribe from this group, send email to play-framewor...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.



Florian Gutmann

unread,
Mar 14, 2011, 9:24:16 AM3/14/11
to play-fr...@googlegroups.com
Thank you for the explanation Pascal. It just helped me to solve another JPA Related problem that just occurred to me when switching to the play master branch. The cause seems to be the private constructor here as well.

I have a Model with two entities: File and Folder. They both share the same Mapped Superclass FileSystemObject. FileSystemObject doesn't have a default constructor.

With play 1.1 everything worked fine. After the switch to the master branch of play the following exception occurred:

play.exceptions.JPAException: Unable to build EntityManagerFactory
Caused by: org.hibernate.InstantiationException: could not instantiate test objectmodels.Folder
Caused by: java.lang.IllegalAccessError: tried to access method models.FileSystemObject.<init>()V from class models.Folder at models.Folder.<init>(Folder.java)

The last part shows that the constructor of the Mapped Superclass can't be called.
Interestingly this problem only appears on Mapped Superclasses and not on ordinary Entities with no public constructor.

Adding a public default constructor to the Superclass solves the problem.

Thanks Pascal!
-- 
Florian Gutmann

Pascal Voitot Dev

unread,
Mar 14, 2011, 9:31:21 AM3/14/11
to play-fr...@googlegroups.com, Florian Gutmann
On Mon, Mar 14, 2011 at 2:24 PM, Florian Gutmann <f.gu...@irregular.at> wrote:
Thank you for the explanation Pascal. It just helped me to solve another JPA Related problem that just occurred to me when switching to the play master branch. The cause seems to be the private constructor here as well.

I have a Model with two entities: File and Folder. They both share the same Mapped Superclass FileSystemObject. FileSystemObject doesn't have a default constructor.

With play 1.1 everything worked fine. After the switch to the master branch of play the following exception occurred:

play.exceptions.JPAException: Unable to build EntityManagerFactory
Caused by: org.hibernate.InstantiationException: could not instantiate test objectmodels.Folder
Caused by: java.lang.IllegalAccessError: tried to access method models.FileSystemObject.<init>()V from class models.Folder at models.Folder.<init>(Folder.java)

The last part shows that the constructor of the Mapped Superclass can't be called.
Interestingly this problem only appears on Mapped Superclasses and not on ordinary Entities with no public constructor.


Maybe it's linked to JPAEnhancer + ClassEnhancer...
 
Adding a public default constructor to the Superclass solves the problem.


Yes the same for Siena: anyway, I wonder if it's a good solution because all our entities shall now provide a default constructor in order to work in Play!... I could modify Siena to manage this but are there other unknown issues linked to this default constructor that will appear for other apis or modules? I don't know...
 

Florian Gutmann

unread,
Mar 14, 2011, 10:12:10 AM3/14/11
to play-fr...@googlegroups.com
I also think that this default private constructor has a great potential for errors and misleadings.

It seems that the change was made for better scala integration.
The log message of the commit says "Better binder for Scala".

Maybe this enhancement can only be done if the scala module is active?
Does this make sense?

Pascal Voitot Dev

unread,
Mar 14, 2011, 10:20:17 AM3/14/11
to play-fr...@googlegroups.com
On Mon, Mar 14, 2011 at 3:12 PM, Florian Gutmann <f.gu...@irregular.at> wrote:
I also think that this default private constructor has a great potential for errors and misleadings.

It seems that the change was made for better scala integration.
The log message of the commit says "Better binder for Scala".

Maybe this enhancement can only be done if the scala module is active?
Does this make sense?


I don't code at all in Scala so I can't say...
I think we need the opinion of someone from Play ;)

 

Guillaume Bort

unread,
Mar 14, 2011, 5:36:38 PM3/14/11
to play-fr...@googlegroups.com
Ok I've modified the PropertiesEnhancer to make the generated default
constructor public.

On Mon, Mar 14, 2011 at 3:20 PM, Pascal Voitot Dev

--
Guillaume Bort, http://guillaume.bort.fr

For anything work-related, use g...@zenexity.fr; for everything else,
write guillau...@gmail.com

Pascal Voitot Dev

unread,
Mar 14, 2011, 5:41:47 PM3/14/11
to play-fr...@googlegroups.com, Guillaume Bort
On Mon, Mar 14, 2011 at 10:36 PM, Guillaume Bort <guillau...@gmail.com> wrote:
Ok I've modified the PropertiesEnhancer to make the generated default
constructor public.

Oh you did it directly...
Don't you see any problems making it public?
was there any reason to choose the private modifier?
 
Anyway is it possible to retrofit this patch on v1.1.x ?

Pascal

Alison Winters

unread,
Mar 15, 2011, 2:44:42 PM3/15/11
to play-framework
I can't speak for Guillaume and the Play project, but the normal
reason to use a private no-args constructor in JPA objects is so that
the ORM (Hibernate) can instantiate the object via that constructor
and populate the fields manually when returning an object from the
database. Meanwhile a public multi-args constructor will often exist
to force programmers to always initialize new objects with a certain
set of fields to help enforce things like NOT NULL constraints
programatically. E.g. good practice might be:

@Entity
public class User {

@Column(nullable=false)
private String email;

@Column
private String other;

private User() {
// used by Hibernate
}

public User(String email) {
if (email == null) throw new NullPointerException();
this.email = email;
}

}

Of course if the no-args constructor is created by Play only at
runtime then you mostly get that programmatic protection anyway,
because the constructor won't be visible in the IDE.

Alison

On Mar 14, 5:41 pm, Pascal Voitot Dev <pascal.voitot....@gmail.com>
wrote:
> On Mon, Mar 14, 2011 at 10:36 PM, Guillaume Bort
> <guillaume.b...@gmail.com>wrote:

Arnaud Rolly

unread,
Mar 15, 2011, 5:27:00 PM3/15/11
to play-fr...@googlegroups.com
Every JPA entity MUST have a no args public or protected constructor.

EJB 3 spec, chapter 2.1

2011/3/15 Alison Winters <alison...@gmail.com>:

Pascal Voitot Dev

unread,
Mar 15, 2011, 7:31:06 PM3/15/11
to play-fr...@googlegroups.com, Arnaud Rolly
is it a mandatory doctrine? ;)

Alison Winters

unread,
Mar 15, 2011, 7:34:30 PM3/15/11
to play-framework
Ah you just reminded me now of a bug we once found back when I was
doing Spring MVC :) Something about private no-args constructors
wouldn't always work because they were Hibernate-specific. Still, the
reasoning behind using non-public no-args constructors (protected in
this case) is the same.

Alison

On Mar 15, 5:27 pm, Arnaud Rolly <traxt...@gmail.com> wrote:
> Every JPA entity MUST have a no args public or protected constructor.
>
> EJB 3 spec, chapter 2.1
>
> 2011/3/15 Alison Winters <alisonatw...@gmail.com>:

Pascal Voitot Dev

unread,
Mar 15, 2011, 7:44:10 PM3/15/11
to play-fr...@googlegroups.com, Alison Winters
yes I don't use hibernate at all (neither EJB) so I don't really want to fit those specific requirements :D
Reply all
Reply to author
Forward
0 new messages