1
votes

I try to use Grails 1.3.7 with plugin gorm-jpa 0.7.1 to enable JPA persistence using EclipseLink 2.2.0 as a persistence provider.

When running the app and clicking on the controller of a JPA annotated entity I get an "UnwantedTokenException" from JPQLParser of EclipseLink:

Executing action [list] of controller [model.PersonController] caused exception:
An exception occurred while creating a query in EntityManager:
Exception Description: Syntax error parsing the query [select person from model.Person as person ], line 1, column 24: syntax error at [.].
Internal Exception: UnwantedTokenException(found=., expected 80);
  at org.eclipse.persistence.internal.libraries.antlr.runtime.BaseRecognizer.recoverFromMismatchedToken(BaseRecognizer.java:587)
  [...]
  at org.eclipse.persistence.internal.jpa.parsing.jpql.JPQLParser.parse(JPQLParser.java:134)

It seems that EclipseLink has a problem with the dot in "model.Person".

How to solve this issue?

To reproduce the issue setup a grails project as follows:

grails create-app GrailsJPA
cd GrailsJPA
grails uninstall-plugin hibernate
grails install-plugin gorm-jpa
grails create-domain-class model.Person

Edit "grails-app\domain\model\Person.groovy" as follows:

package model

import javax.persistence.*;

@Entity
class Person {

    @Id
    @GeneratedValue
    long id;

    @Basic
    long version;

    @Basic
    String firstName

    @Basic
    String lastName

    static constraints = {
        firstName(nullable:true)
        lastName(nullable:true)
    }

}

Generate controller and view for the defined entity:

grails generate-controller model.Person
grails generate-view model.Person

Edit "grails-app\conf\spring\resources.groovy" as follows:

beans = {

    entityManagerFactory(org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean) {
        beanClassLoader = ref("classLoader")
        dataSource = ref("dataSource")
        loadTimeWeaver = new org.springframework.instrument.classloading.SimpleLoadTimeWeaver()
    }

    transactionManager(org.springframework.orm.jpa.JpaTransactionManager) {
        entityManagerFactory = entityManagerFactory
    }

}

Create a file "web-app\WEB-INF\classes\META-INF\persistence.xml" as follows:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
    <persistence-unit name="manager" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <class>model.Person</class>
        <properties>
            <property name="eclipselink.ddl-generation" value="create-tables"/>
        </properties>
    </persistence-unit>
</persistence>

Download EclipseLink 2.2.0 installer ZIP from http://www.eclipse.org/eclipselink/downloads/2.2.0 and extract "eclipselink.jar" from "eclipselink\jlib\" in ZIP to grails project dir:

lib\eclipselink.jar

Now run the grails application:

grails run-app

Browse to

http://localhost:8080/GrailsJPA

Now click on controller "model.PersonController" to reproduce the above exception.

Any ideas on how to resolve this issue?

1

1 Answers

1
votes

I managed to get EclipseLink 2.3.0 JPA provider to work with domain classes that are not inside a package in the following way.

In the following I will show how to setup a grails application that is using EclipseLink as a JPA provider to persist domain classes and run the application in a Tomcat 6 web container.

Start by

  • creating a new grails project
  • uninstall the default hibernate plugin
  • install the gorm-jpa plugin which enables gorm-like behaviour on top of JPA
  • create a new domain class "Person" for testing

    grails create-app GrailsJPA
    cd GrailsJPA
    grails uninstall-plugin hibernate
    grails install-plugin gorm-jpa
    grails create-domain-class Person
    

Hint: Do not put domain classes in packages. EclipseLink will fail when processing e.g. "model.Person" because of the dot in the name, but will do fine with e.g. "Person" (compare to above post).

Edit "grails-app\domain\Person.groovy" as follows:

import javax.persistence.*;

@Entity
class Person {

    @Id
    @GeneratedValue
    long id;

    @Basic
    long version;

    @Basic
    String firstName

    @Basic
    String lastName

    static constraints = {
        firstName(nullable:true)
        lastName(nullable:true)
    }

}

Generate a controller and a view for the defined entity:

grails generate-controller Person
grails generate-view Person

Now we need to modify the generated controller slightly, so that all actions modifying the entity (save, edit, update, delete) are encapsulated in a transaction (compare to bug GPGORMJPA-6). This is done by encapsulating the whole action into a "Person.withTransaction" block.

Edit "grails-app\controllers\PersonController.groovy" as follows:

[...]
def save = {
    Person.withTransaction {
    [...original code stays in this block...]
    }
}
[...]
def edit = {
    Person.withTransaction {
        [...original code stays in this block...]           }
    }
}
def update = {
    Person.withTransaction {
        [...original code stays in this block...]
    }
}
def delete = {
    Person.withTransaction {
        [...original code stays in this block...]
    }
}
[...]

Now we define a persitence unit "manager" that specifies a set of classes to manage persistence for (class "Person") and which JPA provider to use for these classes (in our case EclipseLink).

Create a file "web-app\WEB-INF\classes\META-INF\persistence.xml" as follows:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
    <persistence-unit name="manager" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <class>Person</class>
        <properties>
            <property name="eclipselink.ddl-generation" value="create-tables"/>
        </properties>
    </persistence-unit>
</persistence>

Please note: The file will be deleted if "target" dir of project is cleaned, e.g. when "grails clean" is called explicitly. So it's a good idea to make a backup of the file outside of the "target" dir.

To make EclipseLink available to the grails project, download EclipseLink 2.3.0 installer ZIP and extract "eclipselink.jar" from "eclipselink\jlib\" in ZIP to "lib" folder of grails project:

lib\eclipselink.jar

Now we need to make sure, that an "entityManagerFactory" and "transactionManager" bean are created when the web application is started. The beans will provide the needed access to the JPA provider that will manage all the persistency.

Edit "grails-app\conf\spring\resources.groovy" as follows:

beans = {

    entityManagerFactory(org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean) {
        dataSource = ref("dataSource")
        beanClassLoader = ref("classLoader")
        loadTimeWeaver = new org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver()
    }

    transactionManager(org.springframework.orm.jpa.JpaTransactionManager) {
        entityManagerFactory = entityManagerFactory
    }

}

Please note that we specified a special loadTimeWeaver in the above declarations, that enables the JPA persistence provider to hook into java bytecode on the fly during runtime. When using EclipseLink this is essential. If just using org.springframework.instrument.classloading.SimpleLoadTimeWeaver for testing purposes, this enables the web app to start, but when accessing JPA entities you will encounter exceptions like "0 is not a known entity type", as the persistence provider is unable to hook into the management of the entity classes at runtime.

The use of InstrumentationLoadTimeWeaver is a bit of a hassle, as it will only work, if

  • the java virtual machine executing the web app server is started using "spring-agent" java agent, AND
  • our web app is loaded by the Tomcat server using a special classloader named "TomcatInstrumentableClassLoader"

To achieve this, first download spring-agent-2.5.6.SEC02.jar and spring-instrument-tomcat-3.0.5.RELEASE.jar.

Assume that you have installed Tomcat server in directory %CATALINA_HOME%.

  • Copy the downloaded jar files "spring-agent-2.5.6.SEC02.jar" and "spring-instrument-tomcat-3.0.5.RELEASE.jar" to %CATALINA_HOME%\lib

Now we modify "%CATALINA_HOME%\bin\catalina.bat" that is used by e.g. the start and stop scripts of Tomcat to make sure the JVM executing Tomcat runs with the "spring-agent" java agent.

Add the following to "%CATALINA_HOME%\bin\catalina.bat" in section "Execute The Requested Command" after all the echos:

if exist "%CATALINA_HOME%\lib\spring-agent-2.5.6.SEC02.jar" set JAVA_OPTS=%JAVA_OPTS% -javaagent:%CATALINA_HOME%\lib\spring-agent-2.5.6.SEC02.jar

When Tomcat is started, the script will now check if "spring-agent-2.5.6.SEC02.jar" is in the lib dir, and if this is the case, it will add it as the java agent to the JAVA_OPTS that are used as command line parameters to the JVM when starting Tomcat.

To enable the "TomcatInstrumentableClassLoader" as class loader for our web application we add the file "web-app\META-INF\context.xml" to our grails project, with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
</Context>

Please note that the tags in "context.xml" are case sensitive. So do not try to change <Context> to <context> (or similar things) - this will fail!

Before we package and deploy our web application, we define which datasource to use for persistency. For testing, we just define a HSQLDB in-memeory database.

Edit "grails-app\conf\DataSource.groovy" as follows:

[...]
development {
    dataSource {
        dbCreate = "create-drop"
        url = "jdbc:hsqldb:mem:testDB"
    }
}
test {
    dataSource {
        dbCreate = "create-drop"
        url = "jdbc:hsqldb:mem:testDb"
    }
}
production {
    dataSource {
        dbCreate = "create-drop"
        url = "jdbc:hsqldb:mem:testDB"
    }
}
[...]

Now we are ready to create a WAR archive of our web app using:

grails war

The WAR will be placed in the "target" directory of our project.

In "%CATALINA_HOME%\webapps"

  • create a directory "GrailsJPA"
  • extract all contents of the created WAR archive into this directory

Please note: Do not place WAR in "%CATALINA_HOME%\webapps" and hope that it is deployed on startup of Tomcat. This will fail!

Now start the Tomcat server using script

"%CATALINA_HOME%\bin\startup.bat"

After Tomcat is up and running, use your browser to browse to

http://localhost:8080/GrailsJPA

Now click on controller "PersonController" to create, update, list and delete Person entities via JPA using EclipseLink JPA provider.

But using domain classes that are inside of a package will still fail with the exception mentioned in the above post. This issue is still unresolved.

Download links

  • Grails 1.3.7

    http://grails.org/Download
    
  • EclipseLink 2.3.0

    http://www.eclipse.org/eclipselink/downloads/2.3.0
    
  • Tomcat 6

    http://tomcat.apache.org/download-60.cgi
    
  • spring-instrument-tomcat-3.0.5.RELEASE.jar

    http://mvnrepository.com/artifact/org.springframework/spring-instrument-tomcat
    
  • spring-agent-2.5.6.SEC02.jar

    http://mvnrepository.com/artifact/org.springframework/spring-agent