I've enabled jdbcStore in QuartzConfig
quartz {
jdbcStore = true
autoStartup = true
}
and provided quartz.properties in grails-app/conf directory:
...
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
...
The problem is that when I run grails in local dev environment with grails rA those properties are ignored and I get an exception:
Caused by: org.quartz.JobPersistenceException: Couldn't store trigger 'RebillJobSimpleTrigger' for 'com.payment.rebill.RebillJob' job:Bad value for type long : \xaced00057.....
at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1241)
at org.quartz.impl.jdbcjobstore.JobStoreSupport$5.execute(JobStoreSupport.java:1147)
at org.quartz.impl.jdbcjobstore.JobStoreSupport$40.execute(JobStoreSupport.java:3670)
at org.quartz.impl.jdbcjobstore.JobStoreCMT.executeInLock(JobStoreCMT.java:242)
In live environment everything works ok, however I'm unable to run the project in embedded jetty.
Elaboration:
The exception is thrown because my custom quartz.properties are ignored (Thus instead of PostgreSQLDelegate the StdJDBCDelegate is used which is unable to read BLOB from DB).
I use quartz grails plugin version 0.4.2 which contains empty grails-app/conf/quartz.properties and I think there is some class loader issue which loads this empty file instead of my own quartz.properties file.
Looking into the plugin the properties file is configured in QuartzGrailsPlugin.groovy's spring bean:
quartzScheduler(SchedulerFactoryBean) {
configLocation = "classpath:quartz.properties"
...
(There is some spring bean builder magic which converts the "classpath:quartz.properties" String to spring's Resource class, since SchedulerFactoryBean's configLocation property is of type Resource).
When I debug the initialization on line 554 of SchedulerFactoryBean the configLocation leads to empty file despite my quartz.properties are not empty.
When I manually change the quartz spring bean to read file from different location everything works:
quartzScheduler(SchedulerFactoryBean) {
configLocation = "classpath:data/quartz.properties"
...
However I can't change the quartz plugin itself. Do you have any idea how to fix this locally ?
I use grails version 2.1.2
Related
I deployed a Grails 3.2.0 WAR on Tomcat 8.5.6 and JDK 1.8.0_91 with a simple controller having following code:
package com.test
class MailController {
static responseFormats = ['json']
def index() {
Map headers = (request.headerNames as List).collectEntries { // It fails on this line
return [(it): request.getHeader(it)]
}
println "Incoming email $headers"
render status: 200
}
}
This code fails with the following exception:
Caused by: java.lang.NoClassDefFoundError: groovy/lang/GroovyObject
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
at groovy.util.ProxyGenerator.instantiateDelegateWithBaseClass(ProxyGenerator.java:225)
at groovy.util.ProxyGenerator.instantiateDelegateWithBaseClass(ProxyGenerator.java:193)
at groovy.util.ProxyGenerator.instantiateDelegate(ProxyGenerator.java:185)
at groovy.util.ProxyGenerator.instantiateDelegate(ProxyGenerator.java:181)
at org.grails.web.converters.ConverterUtil.invokeOriginalAsTypeMethod(ConverterUtil.java:161)
at org.grails.web.converters.ConvertersExtension.asType(ConvertersExtension.groovy:56)
at com.test.MailController.index(MailController.groovy:7)
at org.grails.core.DefaultGrailsControllerClass$MethodHandleInvoker.invoke(DefaultGrailsControllerClass.java:222)
at org.grails.core.DefaultGrailsControllerClass.invoke(DefaultGrailsControllerClass.java:187)
at org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter.handle(UrlMappingsInfoHandlerAdapter.groovy:90)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
... 14 common frames omitted
Caused by: java.lang.ClassNotFoundException: groovy.lang.GroovyObject
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
... 27 common frames omitted
Before building the WAR file, I've changed the embedded tomcat to provided in build.gradle and also commented the groovy-ant dependency related to grails-core#10196
I see a answer here but that didn't worked and the above code is working fine when we run via grails run-app.
Update
I shorted down the issue. It is failing on this part only request.headerNames as List
I am pretty sure the problem is with the use of "as List". Mostly because Grails will overwrite Groovy's asType implementation which makes the "as X" coercion syntax work.
Grails does this to add support for things like JSON for marshalling known Grails types to web transport formats.
Unfortunately, in doing so Grails also breaks any asType function you might have declared yourself. Or in this case Groovy itself already declared for converting an Enumeration into a List.
It's quite annoying as Grails is effectively breaking existing contracts here and forcing you to modify upstream code to allow it to run on Grails.
That or dump Grails because it doesn't play nice with perfectly valid Groovy code.
I believe replacing "as List" with .asType(List) won't even fix the issue as you're still invoking the same code. At best you could try .collect([]) {it} instead. It may not be necessary to add the empty array as the first argument to collect.
I'm trying to get www.stagemonitor.org working with Grails.
I've created a sample project here: https://github.com/jbwiv/teststagemonitor
I've added stagemonitor to grails-app/conf/BuildConfig.groovy as both a compile and runtime dependency. It indeed gets installed to my maven directory after calling "grails refresh-dependencies":
/home/user/.m2/repository/org/stagemonitor
/home/user/.m2/repository/org/stagemonitor/stagemonitor-requestmonitor
/home/user/.m2/repository/org/stagemonitor/stagemonitor-requestmonitor/0.13.0
/home/user/.m2/repository/org/stagemonitor/stagemonitor-requestmonitor/0.13.0/stagemonitor-requestmonitor-0.13.0.pom.sha1
/home/user/.m2/repository/org/stagemonitor/stagemonitor-requestmonitor/0.13.0/_remote.repositories
/home/user/.m2/repository/org/stagemonitor/stagemonitor-requestmonitor/0.13.0/stagemonitor-requestmonitor-0.13.0.jar
/home/user/.m2/repository/org/stagemonitor/stagemonitor-requestmonitor/0.13.0/stagemonitor-requestmonitor-0.13.0.pom
/home/user/.m2/repository/org/stagemonitor/stagemonitor-requestmonitor/0.13.0/stagemonitor-requestmonitor-0.13.0.jar.sha1
/home/user/.m2/repository/org/stagemonitor/stagemonitor-jvm
/home/user/.m2/repository/org/stagemonitor/stagemonitor-jvm/0.13.0
/home/user/.m2/repository/org/stagemonitor/stagemonitor-jvm/0.13.0/stagemonitor-jvm-0.13.0.pom.sha1
/home/user/.m2/repository/org/stagemonitor/stagemonitor-jvm/0.13.0/_remote.repositories
/home/user/.m2/repository/org/stagemonitor/stagemonitor-jvm/0.13.0/stagemonitor-jvm-0.13.0.jar
/home/user/.m2/repository/org/stagemonitor/stagemonitor-jvm/0.13.0/stagemonitor-jvm-0.13.0.jar.sha1
/home/user/.m2/repository/org/stagemonitor/stagemonitor-jvm/0.13.0/stagemonitor-jvm-0.13.0.pom
/home/user/.m2/repository/org/stagemonitor/stagemonitor-javaagent
/home/user/.m2/repository/org/stagemonitor/stagemonitor-javaagent/0.13.0
/home/user/.m2/repository/org/stagemonitor/stagemonitor-javaagent/0.13.0/stagemonitor-javaagent-0.13.0.pom
/home/user/.m2/repository/org/stagemonitor/stagemonitor-javaagent/0.13.0/_remote.repositories
/home/user/.m2/repository/org/stagemonitor/stagemonitor-javaagent/0.13.0/stagemonitor-javaagent-0.13.0.jar.sha1
/home/user/.m2/repository/org/stagemonitor/stagemonitor-javaagent/0.13.0/stagemonitor-javaagent-0.13.0.pom.sha1
/home/user/.m2/repository/org/stagemonitor/stagemonitor-javaagent/0.13.0/stagemonitor-javaagent-0.13.0.jar
/home/user/.m2/repository/org/stagemonitor/stagemonitor-web
/home/user/.m2/repository/org/stagemonitor/stagemonitor-web/0.13.0
/home/user/.m2/repository/org/stagemonitor/stagemonitor-web/0.13.0/stagemonitor-web-0.13.0.jar
/home/user/.m2/repository/org/stagemonitor/stagemonitor-web/0.13.0/_remote.repositories
/home/user/.m2/repository/org/stagemonitor/stagemonitor-web/0.13.0/stagemonitor-web-0.13.0.pom.sha1
/home/user/.m2/repository/org/stagemonitor/stagemonitor-web/0.13.0/stagemonitor-web-0.13.0.jar.sha1
/home/user/.m2/repository/org/stagemonitor/stagemonitor-web/0.13.0/stagemonitor-web-0.13.0.pom
/home/user/.m2/repository/org/stagemonitor/stagemonitor-os
/home/user/.m2/repository/org/stagemonitor/stagemonitor-os/0.13.0
/home/user/.m2/repository/org/stagemonitor/stagemonitor-os/0.13.0/stagemonitor-os-0.13.0.jar.sha1
/home/user/.m2/repository/org/stagemonitor/stagemonitor-os/0.13.0/_remote.repositories
/home/user/.m2/repository/org/stagemonitor/stagemonitor-os/0.13.0/stagemonitor-os-0.13.0.pom.sha1
/home/user/.m2/repository/org/stagemonitor/stagemonitor-os/0.13.0/stagemonitor-os-0.13.0.pom
/home/user/.m2/repository/org/stagemonitor/stagemonitor-os/0.13.0/stagemonitor-os-0.13.0.jar
/home/user/.m2/repository/org/stagemonitor/stagemonitor-jdbc
/home/user/.m2/repository/org/stagemonitor/stagemonitor-jdbc/0.13.0
/home/user/.m2/repository/org/stagemonitor/stagemonitor-jdbc/0.13.0/_remote.repositories
/home/user/.m2/repository/org/stagemonitor/stagemonitor-jdbc/0.13.0/stagemonitor-jdbc-0.13.0.pom.sha1
/home/user/.m2/repository/org/stagemonitor/stagemonitor-jdbc/0.13.0/stagemonitor-jdbc-0.13.0.jar.sha1
/home/user/.m2/repository/org/stagemonitor/stagemonitor-jdbc/0.13.0/stagemonitor-jdbc-0.13.0.pom
/home/user/.m2/repository/org/stagemonitor/stagemonitor-jdbc/0.13.0/stagemonitor-jdbc-0.13.0.jar
/home/user/.m2/repository/org/stagemonitor/stagemonitor-core
/home/user/.m2/repository/org/stagemonitor/stagemonitor-core/0.13.0
/home/user/.m2/repository/org/stagemonitor/stagemonitor-core/0.13.0/_remote.repositories
/home/user/.m2/repository/org/stagemonitor/stagemonitor-core/0.13.0/stagemonitor-core-0.13.0.pom.sha1
/home/user/.m2/repository/org/stagemonitor/stagemonitor-core/0.13.0/stagemonitor-core-0.13.0.jar
/home/user/.m2/repository/org/stagemonitor/stagemonitor-core/0.13.0/stagemonitor-core-0.13.0.pom
/home/user/.m2/repository/org/stagemonitor/stagemonitor-core/0.13.0/stagemonitor-core-0.13.0.jar.sha1
I've also placed a stagemonitor.properties file in src/java, which at runtime gets moved to target/work/resources/stagemonitor.properties. I believe I have that properties file configured properly.
I've installed the templates Grails uses for web.xml and modified to insure metadata-complete=true is not present in the generated web.xml.
However, after grails run-app and navigating to http://localhost:8080/main/index, I get my index page as expected, but no stagemonitor icon to click and it appears no stagemonitor assets are included.
It doesn't appear to be compatible with Grails. If you enable logging
log4j.main = {
error 'org.codehaus.groovy.grails',
'org.springframework',
'org.hibernate',
'net.sf.ehcache.hibernate'
info 'org.stagemonitor'
}
you'll see a bunch of error stacktraces that appear to imply that the way they're using Javassist to wire in tracing code isn't compatible with Groovy and/or the AST transformations that Grails uses:
org.stagemonitor.javassist.NotFoundException: stagemonitortest.PersonController$setErrors
...
org.stagemonitor.javassist.NotFoundException: stagemonitortest.PersonController$getParams$0
...
org.stagemonitor.javassist.NotFoundException: stagemonitortest.Person$count
...
org.stagemonitor.javassist.NotFoundException: stagemonitortest.Person$list$0
...
org.stagemonitor.javassist.NotFoundException: stagemonitortest.PersonController$respond$1
So, I recently upgraded our Grails app from version 1.3.7 to 2.3.4. I'm now getting an exception in a SOAP handler that attempts to extract the message content and log it to the DB. This worked in 1.3.7, but I'm assuming that some new dependency or something has messed with the classpath.
The code looks like this:
private String extractSOAPMessage(SOAPMessageContext smc) {
Source source = smc.getMessage().getSOAPPart().getContent()
TransformerFactory factory = TransformerFactory.newInstance()
Transformer transformer = factory.newTransformer()
transformer.setOutputProperty( OutputKeys.METHOD, "xml" )
java.io.StringWriter writer = new StringWriter()
Result result = new StreamResult( writer )
transformer.transform( source, result )
return writer.toString()
}
The exception I'm seeing is:
Caused by: java.lang.LinkageError: loader constraint violation: loader (instance of <bootloader>) previously initiated loading for a different type with name "javax/activation/DataHandler"
It happens on this line:
Source source = smc.getMessage().getSOAPPart().getContent()
It looks like the culprit is the getSOAPart() call.
Note that I am using the 1.1.1 version of the cxf plugin for Grails. Any help on this would be greatly appreciated. I've found several similar issues with solutions, but none of them have been for the "javax/activation/DataHandler", so I am not sure what's going on here.
I suspect something has a transitive dependency on the activation library which you need to exclude - try running a dependency-report. Since Java 6 that JAR has been un-necessary as it's built in to the core Java class library, but many things still have dependencies on it so they can work on Java 5 (or date back to when Java 5 was still in widespread use).
Using grails 2.3.2, Java 1.6.0_65, trying to compile the following placed in the services directory. Even if it is not a service, and just put in the src/groovy directory, it still causes the same compile error.
I installed groovy 2.1.9 (and tried 2.2.0) which appears to be the version used by grails 2.3.2 and ran groovyc -cp quava-13.0.1.jar TestCache.groovy and it worked fine. So it appears to be something related to grails.
package somewhere
import com.google.common.cache.Cache
import groovy.transform.CompileStatic
#CompileStatic
class TestCacheService {
private final Cache<URL, String> cache
TestCacheService() {
cache = null
}
}
I receive the following error:
General error during instruction selection: sun.reflect.annotation.EnumConstantNotPresentExceptionProxy
java.lang.ArrayStoreException: sun.reflect.annotation.EnumConstantNotPresentExceptionProxy
at sun.reflect.annotation.AnnotationParser.parseEnumArray(AnnotationParser.java:673)
at sun.reflect.annotation.AnnotationParser.parseArray(AnnotationParser.java:462)
at sun.reflect.annotation.AnnotationParser.parseMemberValue(AnnotationParser.java:286)
at sun.reflect.annotation.AnnotationParser.parseAnnotation(AnnotationParser.java:222)
at sun.reflect.annotation.AnnotationParser.parseAnnotations2(AnnotationParser.java:69)
at sun.reflect.annotation.AnnotationParser.parseAnnotations(AnnotationParser.java:52)
at java.lang.Class.initAnnotationsIfNecessary(Class.java:3127)
at java.lang.Class.getAnnotation(Class.java:3086)
at sun.reflect.annotation.AnnotationType.<init>(AnnotationType.java:113)
at sun.reflect.annotation.AnnotationType.getInstance(AnnotationType.java:66)
at sun.reflect.annotation.AnnotationParser.parseAnnotation(AnnotationParser.java:202)
at sun.reflect.annotation.AnnotationParser.parseAnnotations2(AnnotationParser.java:69)
at sun.reflect.annotation.AnnotationParser.parseAnnotations(AnnotationParser.java:52)
at java.lang.reflect.Method.declaredAnnotations(Method.java:693)
at java.lang.reflect.Method.getDeclaredAnnotations(Method.java:686)
at java.lang.reflect.AccessibleObject.getAnnotations(AccessibleObject.java:175)
at org.codehaus.groovy.vmplugin.v5.Java5.configureClassNode(Java5.java:362)
at org.codehaus.groovy.ast.ClassNode.lazyClassInit(ClassNode.java:258)
at org.codehaus.groovy.ast.ClassNode.getInterfaces(ClassNode.java:353)
at org.codehaus.groovy.ast.ClassNode.declaresInterface(ClassNode.java:945)
at org.codehaus.groovy.ast.ClassNode.implementsInterface(ClassNode.java:925)
at org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.getResultType(StaticTypeCheckingVisitor.java:2629)
at org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.visitBinaryExpression(StaticTypeCheckingVisitor.java:421)
at org.codehaus.groovy.ast.expr.BinaryExpression.visit(BinaryExpression.java:49)
at org.codehaus.groovy.ast.CodeVisitorSupport.visitExpressionStatement(CodeVisitorSupport.java:69)
at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitExpressionStatement(ClassCodeVisitorSupport.java:193)
at org.codehaus.groovy.ast.stmt.ExpressionStatement.visit(ExpressionStatement.java:40)
at org.codehaus.groovy.ast.CodeVisitorSupport.visitBlockStatement(CodeVisitorSupport.java:35)
at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitBlockStatement(ClassCodeVisitorSupport.java:163)
at org.codehaus.groovy.ast.stmt.BlockStatement.visit(BlockStatement.java:69)
at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClassCodeContainer(ClassCodeVisitorSupport.java:101)
at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitConstructorOrMethod(ClassCodeVisitorSupport.java:112)
at org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.visitConstructorOrMethod(StaticTypeCheckingVisitor.java:1435)
at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitConstructor(ClassCodeVisitorSupport.java:119)
at org.codehaus.groovy.ast.ClassNode.visitContents(ClassNode.java:1051)
at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClass(ClassCodeVisitorSupport.java:50)
at org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.visitClass(StaticTypeCheckingVisitor.java:162)
at org.codehaus.groovy.transform.sc.StaticCompilationVisitor.visitClass(StaticCompilationVisitor.java:110)
at org.codehaus.groovy.transform.sc.StaticCompileTransformation.visit(StaticCompileTransformation.java:60)
at org.codehaus.groovy.transform.ASTTransformationVisitor.visitClass(ASTTransformationVisitor.java:132)
at org.codehaus.groovy.transform.ASTTransformationVisitor$2.call(ASTTransformationVisitor.java:176)
at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1036)
at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:572)
at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:550)
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:527)
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:506)
at org.codehaus.groovy.tools.FileSystemCompiler.compile(FileSystemCompiler.java:59)
at org.codehaus.groovy.tools.FileSystemCompiler.doCompilation(FileSystemCompiler.java:215)
at org.codehaus.groovy.ant.Groovyc.runCompiler(Groovyc.java:1104)
at org.codehaus.groovy.ant.Groovyc.compile(Groovyc.java:1155)
at org.codehaus.groovy.grails.compiler.Grailsc.compile(Grailsc.java:78)
at org.codehaus.groovy.ant.Groovyc.execute(Groovyc.java:770)
at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
Is this a known bug?
Any workaround?
Google's Cache class uses the Nullable annotation. I had multiple nullable annotation implementations on my classpath. I think removing 'edu.washington.cs.types.checker:checker-framework:1.6.4' from the classpath solved this problem.
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?
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