Tuesday, October 22, 2013

Re: RequestFactory, ValueProxy, EntityProxy and JSR303 - is it a bug?



On Tuesday, October 22, 2013 2:03:19 PM UTC+2, Nermin wrote:


On Tuesday, October 22, 2013 11:40:48 AM UTC+2, Thomas Broyer wrote:


On Monday, October 21, 2013 11:11:07 PM UTC+2, Nermin wrote:
Dear Thomas,

thank you for your Post. I have checked my OSIV konfiguration and I have it the right way (transaction per service method).

Apparently not (see below)


OK, let me show you my OSIV code:

=========== THREAD LOCAL EM
public class ThreadLocalEntityManager {
    private static ThreadLocal<EntityManager> holder = new ThreadLocal<EntityManager>();
   
    private ThreadLocalEntityManager() {
    }

    public static EntityManager get() {
        return holder.get();
    }

    public static void set(EntityManager em) {
        holder.set(em);
    }
}


=========== FILTER
public class AppHandlerFilter  implements Filter {
....
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
      
        //1. Create new EntityManager for this call
        EntityManager em = factory.createEntityManager();
        ThreadLocalEntityManager.set(em);    //== Create EM

        try {
            chain.doFilter(request, response);
          
        } catch (Exception e) { //Any other Exception
            if(em.getTransaction().isActive()) {
                em.getTransaction().rollback();
            }
            logger.severe(e.getMessage());
            e.printStackTrace();
        } finally {
            if(em.isOpen()) {
                em.close();                    //== Close EM
            }
        }
    }
}


======== Entity:

@Entity
public class TestEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @org.datanucleus.api.jpa.annotations.Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
    private String id;
    @Version
    private Long version;

    @NotNull
    private String myName;
    @Pattern(regexp = "[^@\\s]+@[^@\\s]+\\.[A-Za-z]{2,4}", message="Invalid e-mail address")
    @NotNull
    private String myEmailAddress;
   
    public static TestEntity findTestEntity(String id) {
        if (id == null) {
            return null;
        }
        TestEntity te = null;
        EntityManager em = ThreadLocalEntityManager.get();
        em.getTransaction().begin();
        te = em.find(TestEntity.class, id);
        em.getTransaction().commit();
        return te;
    }

    public ProcessResponse persist() {
        EntityManager em = ThreadLocalEntityManager.get();
        em.getTransaction().begin();
        em.persist(this);
        em.refresh(this);
        em.getTransaction().commit();
        return new ProcessResponse(this.getId(), true);
    }
   
    public ProcessResponse update(){
        EntityManager em = ThreadLocalEntityManager.get();
        em.getTransaction().begin();
        em.merge(this);
        em.getTransaction().commit();
        return new ProcessResponse(true);
    }
   
...
}


==== persistence.xml

    <persistence-unit name="emajstor_persistence" transaction-type="RESOURCE_LOCAL">
        <provider>org.datanucleus.api.jpa.PersistenceProviderImpl</provider>
       
        <class>com.emajstor.server.persistence.TestEntity</class>

        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <properties>
            <property name="datanucleus.NontransactionalRead" value="true"/>
            <property name="datanucleus.NontransactionalWrite" value="true"/>
            <property name="datanucleus.ConnectionURL" value="appengine"/>
            <property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true"/>
                       
            <property name="javax.persistence.validation.group.pre-persist" value="javax.validation.groups.Default, com.emajstor.server.persistence.validator.PrePersitValidationGroup" />
            <property name="javax.persistence.validation.group.pre-update" value="javax.validation.groups.Default" />
            <property name="javax.persistence.validation.group.pre-remove" value="" />
           
        </properties>
    </persistence-unit>

 
The debuggung in ReflectiveServiceLayer#validate showed sofar an appropriate behaviour.

So it correctly returns ConstraintViolations in all cases?

YES! Validation works fine!
 
 
The only problem is that the javax.validation.ConstraintViolationException is turned into a java.lang.RuntimeException in case of an entity update. (see error below.)

There shouldn't be any ConstraintViolation*Exception*.

Why shouldn't be there a ConstraintViolation*Exception*?? The entity contains constraint violation because the provided emailAddress is purposely made wrong!

But ReflectiveServiceLayer#validate returns a Set<ConstraintViolation> without the need to throw a ConstratintViolation*Exception*.
 
I cannot figure out why is this happening.
I personally find GWT beeing an easy and very elegant tool for web development. The Appengine-Datastore-handling part is in my point of view a real pain in the ass.

JPA and JDO are a PITA, whether or not they're used on AppEngine.
There are alternatives though (e.g. Objectify).

I agree. Maybe Objectify would be a "better" alternative. Unfortunately I have 12 Entities which I would need to migrate and to test, and there are almost no examples how to use Objectify 4 with the Requestfactory.

AFAICT, there's absolutely nothing special; Objectify takes care of the request-scoped caching (required by RequestFactory, the only reason you need OSIV with JPA/JDO).
 
 

com.google.web.bindery.event.shared.UmbrellaException: Exception caught: Server Error 500 <html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
<title>Error 500 Validation failed for com.emajstor.server.persistence.TestEntity@59778fd8 during pre-update for groups [interface javax.validation.groups.Default] - exceptions are attached</title>
</head>
<body><h2>HTTP ERROR 500</h2>
<p>Problem accessing /unAuthRequestFactory. Reason:
<pre>    Validation failed for com.emajstor.server.persistence.TestEntity@59778fd8 during pre-update for groups [interface javax.validation.groups.Default] - exceptions are attached</pre></p><h3>Caused by:</h3><pre>javax.validation.ConstraintViolationException: Validation failed for com.emajstor.server.persistence.TestEntity@59778fd8 during pre-update for groups [interface javax.validation.groups.Default] - exceptions are attached
    at org.datanucleus.validation.BeanValidatorHandler.validate(BeanValidatorHandler.java:71)
    at org.datanucleus.validation.BeanValidatorHandler.preStore(BeanValidatorHandler.java:86)
    at org.datanucleus.api.jpa.JPACallbackHandler.preStore(JPACallbackHandler.java:102)
    at org.datanucleus.state.JDOStateManager.flush(JDOStateManager.java:3827)
    at org.datanucleus.ObjectManagerImpl.flushInternalWithOrdering(ObjectManagerImpl.java:3888)
    at org.datanucleus.ObjectManagerImpl.flushInternal(ObjectManagerImpl.java:3811)
    at org.datanucleus.ObjectManagerImpl.flush(ObjectManagerImpl.java:3751)
    at org.datanucleus.ObjectManagerImpl.preCommit(ObjectManagerImpl.java:4141)
    at org.datanucleus.ObjectManagerImpl.transactionPreCommit(ObjectManagerImpl.java:428)
    at org.datanucleus.TransactionImpl.internalPreCommit(TransactionImpl.java:398)
    at org.datanucleus.TransactionImpl.commit(TransactionImpl.java:287)
    at org.datanucleus.ObjectManagerImpl.close(ObjectManagerImpl.java:1090)
    at org.datanucleus.api.jpa.JPAEntityManager.close(JPAEntityManager.java:193)
    at com.emajstor.server.AppHandlerFilter.doFilter(AppHandlerFilter.java:91)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)

Your OSIV filter is causing a TransactionImpl.commit, which means there's a transaction spanning the entire request (or maybe you have auto-commit enabled, I don't know how JPA/JDO really work).
That commit triggers validation, and the ConstraintViolationException is shadowing the RequestFactory response (which, if ReflectiveServiceLayer#validate behaved expectedly, would be an onConstraintViolations response)

OK .... I see ... YOU ARE RIGHT.
I have tested it using: <property name="datanucleus.NontransactionalWrite" value="false"/> I have expected this to prevent non-transactional write as you mentioned.
I am getting the following error which lets me think that requestfactory is trying to "auto-comit" data into the DB on its own!!

RequestFactory is storage-agnostic (everything's handled by user code in locators or findXxx static methods for loading, and in services themselves for everything else)
 
Am I missing some configurations on GWT level??

No. There's no configuration on GWT's side. You might want to ask the DataNucleus and/or AppEngine experts how to configure DataNucleus (as I said, I don't know JPA or JDO, they only caused me trouble when I tried to use them, so I can't help you further).
 

SEVERE: Server error caused by:org.datanucleus.exceptions.NucleusUserException
Cant write fields outside of transactions. You may want to set 'NontransactionalWrite=true'.
org.datanucleus.exceptions.NucleusUserException: Cant write fields outside of transactions. You may want to set 'NontransactionalWrite=true'.
    at org.datanucleus.api.jpa.state.PersistentNontransactional.transitionWriteField(PersistentNontransactional.java:170)
    at org.datanucleus.state.AbstractStateManager.transitionWriteField(AbstractStateManager.java:871)
    at org.datanucleus.state.JDOStateManager.preWriteField(JDOStateManager.java:3575)
    at org.datanucleus.state.AbstractStateManager.setStringField(AbstractStateManager.java:1998)
    at com.emajstor.server.persistence.TestEntity.jdoSetmyEmailAddress(TestEntity.java)
    at com.emajstor.server.persistence.TestEntity.setMyEmailAddress(TestEntity.java:88)

RequestFactory is only calling your setter; it's expected this value will not be persisted until you explicitly persist() the entity.
(it might be normal with DataNucleus, but I find it strange that there's mention of JDO here whereas you're using the JPA API)

--
You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsubscribe@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.
Visit this group at http://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/groups/opt_out.

No comments:

Post a Comment