简体   繁体   中英

Using the grails Quartz plugin without Hibernate

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM