I am working on a backend Grails application that pulls information periodically from a RESTful service. To do this I installed the Grails Quartz plugin.
grails install-plugin quartz
I then created a job using
grails create-job My
which geneates a MyJob file which I configured with a cron trigger
static triggers = {
cron name: 'myTrigger', cronExpression: '0 0 * * * ?' // hourly
}
Running the application locally in the dev environment works correctly, however once I try to build a testing or production war I get the following exception when the trigger is run.
2010-02-18, 00:04:32 ERROR org.codehaus.groovy.grails.web.context.GrailsContextLoader - Error occurred shutting down plug-in manager: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'quartzScheduler':
Cannot resolve reference to bean 'sessionBinderListener' while setting bean property 'jobListeners' with key [0]; nested
exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionBinderListener': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'sessionFactory': Cannot resolve reference to bean 'hibernateProperties' while setting bean property 'hibernateProperties'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating
bean with name 'hibernateProperties': Cannot resolve reference to bean 'dialectDetector' while setting bean property 'properties' with key [hibernate.dialect]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dialectDetector': Invocation of init method failed; nested exception is org.springframework.jdbc.support.MetaDataAccessException: Error while extracting DatabaseMetaData; nested exception is java.sql.SQLException : Access is denied: Session is closed
As I don't require a database, I tried removing the Hibernate plugin as suggested , but I get compilation problems once the Hibernate plugin has been removed:
Running script C:\Downloads\grails-1.2.1\scripts\RunApp.groovy
Environment set to development
[groovyc] Compiling 18 source files to C:\Projects\myapp\target\classes
[groovyc] org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed, Compile error during compilation with javac.
[groovyc] ...\myapp\plugins\quartz-0.4.1\src\java\org\codehaus\groovy\grails\plugins\quartz\listeners\SessionBinderJobListener.java:19: package org.hibernate does not exist
[groovyc] import org.hibernate.FlushMode;
...
Is there any way to use the Quartz plugin without the Hibernate plugin?
If not, would the best idea be to configure an in-memory database for Quartz to use? (I'm not concerned with the persistence of any of this data.)
I got the Quartz plugin (0.4.2) working fairly cleanly without the Hibernate plugin and without editing the Quartz plugin.
Add a runtime dependency on Hibernate in BuildConfig.groovy just to pull in the jars:
dependencies {
...
// the Quartz plugin depends on some Hibernate classes being available
runtime('org.hibernate:hibernate-core:3.6.7.Final') {
exclude group:'commons-logging', name:'commons-logging'
exclude group:'commons-collections', name:'commons-collections'
exclude group:'org.slf4j', name:'slf4j-api'
exclude group:'xml-apis', name:'xml-apis'
exclude group:'dom4j', name:'dom4j'
exclude group:'antlr', name:'antlr'
}
}
Quartz still installs a SessionBinderJobListener to bind a Hibernate session to the job thread. Create a NOP session binder like this:
import org.quartz.listeners.JobListenerSupport
class NopSessionBinderJobListener extends JobListenerSupport {
String getName() { return "sessionBinderListener" }
}
And create a Spring bean in resources.groovy:
beans = {
...
// dummy session binder to work around issue with Quartz requiring Hibernate
sessionBinderListener(NopSessionBinderJobListener) { }
}
For the 1.0.RC7 version of the Quartz grails plugin, I had to add some additional steps. It's based on @DavidTinker's response.
In resources.groovy, I had to add a Nop session factory and a Nop Session (make sure it's implementing org.hibernate.classic.Session)
beans = {
// dummy session binder to work around issue with Quartz requiring Hibernate
sessionBinderListener(NopSessionBinderJobListener) { }
sessionFactory( NopSessionFactory) {}
}
NopSessionFactory.groovy:
class NopSessionFactory implements SessionFactory
{
@Override
Session openSession() throws HibernateException {
return new NopSession()
}
// Implement all of the methods required by this interface and return a NopSession for any method that returns a session
}
And, in my case, I either need to add a No-op Transaction manager for any services injected into my Quartz jobs, or mark the services as non-transactional (which I chose to do).
class FooService {
static transactional = false
I also had to exclude the hibernate transitive dependencies, but since I'm using gradle to build the project, it looked like this:
// Needed by quartz
runtime('org.hibernate:hibernate-core:3.6.10.Final') {
exclude group:'commons-logging', module:'commons-logging'
exclude group:'commons-collections', module:'commons-collections'
exclude group:'org.slf4j', module:'slf4j-api'
exclude group:'xml-apis', module:'xml-apis'
exclude group:'dom4j', module:'dom4j'
exclude group:'antlr', module:'antlr'
}
Seems that there are code dependency, at quartz-0.4.1\\src\\java\\org\\codehaus\\groovy\\grails\\plugins\\quartz\\listeners\\SessionBinderJobListener.java:1
And then you can't compile quartz plugin
without hibernate classes. Maybe you can put them in lib
folder? or add it as compile
dependency if you use maven/gradle
I've managed to get this working by leaving the Hibernate plugin installed and configuring the in-memory database. In DataSource.groovy
...
environments {
development {
dataSource {
dbCreate = "create-drop" // one of 'create', 'create-drop','update'
url = "jdbc:hsqldb:mem:myDevDb"
}
}
test {
dataSource {
dbCreate = "create-drop"
url = "jdbc:hsqldb:mem:myTestDb"
}
}
production {
dataSource {
dbCreate = "create-drop"
url = "jdbc:hsqldb:mem:myProdDb;shutdown=true"
}
}
}
...
The change was to set "create-drop" on the test & production databases and set the production database to 'mem' instead of 'file'.
So,
Here is the solution I came up with (please note that I was unhappy to keep hibernate at the first place). The solution tested with Grails 1.2.1 and quartz plugin 0.4.1 with hibernate wiped-out regular way ( grails uninstall-plugin hibernate
). Keeping in memory database is also not very best option I could find.
Create or edit scripts/_Events.groovy
:
eventCompileStart = {bindings->
println "Compile Start: Plugin dir is ${grailsSettings.projectPluginsDir}"
// Kill standard listener which actually wants to use hibernate (to restore hibernate session)!
Ant.delete(file:"${grailsSettings.projectPluginsDir}/quartz-0.4.1/src/java/org/codehaus/groovy/grails/plugins/quartz/listeners/SessionBinderJobListener.java")
}
Now to compensate file deletion (it's referred from somewhere in GrailsQuartzPlugin.groovy
, we need to create "safe" version of the class in project, aka at src/java/org/codehaus/groovy/grails/plugins/quartz/listeners/SessionBinderJobListener.java
this is what I put there keeping all original copyrights and author intact - but no hibernate left (let it RIP) :
/* Copyright 2006-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.grails.plugins.quartz.listeners;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.listeners.JobListenerSupport;
/**
* JobListener implementation which binds Hibernate Session to thread before
* execution of job and flushes it after job execution.
*
* @author Sergey Nebolsin (nebolsin@gmail.com)
*
* @since 0.2
*/
public class SessionBinderJobListener extends JobListenerSupport
{
private static final transient Log LOG = LogFactory.getLog(SessionBinderJobListener.class);
public static final String NAME = "sessionBinderListener";
public String getName()
{
return NAME;
}
@Override
public void jobToBeExecuted(final JobExecutionContext context)
{
}
@Override
public void jobWasExecuted(final JobExecutionContext context, final JobExecutionException exception)
{
}
}
Warning: The solution is "hack" which overrides some files from plugin. To remove hack:
make sure you uninstall quartz plugin,
go to ${grailsSettings.projectPluginsDir}
(location is printed by hack during each run of grails compile
and higher-'level' scripts including grails war
) and make sure no artifacts left.
Remove both hack artifacts
Reinstall fresh quartz plugin.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.