views:

587

answers:

3

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 in http://stackoverflow.com/questions/1343967/can-i-configure-grails-with-no-datasource
However 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 anyway 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.)

A: 

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

splix
Thanks splix, if I'm bundling the Hibernate jars then I may as well retain the Hibernate plugin. I've managed to get it working by ensuring all databases are in-memory, see my answer.
Richard Paul
A: 

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'.

Richard Paul
A: 

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 R.I.P.) :


/* 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 ([email protected])
 * 
 * @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.

Vadim Tsyganok
Hey Vadim, looks like a lot of brittle hacking to get Grails to run Quartz without Hibernate. Thanks for sharing, especially the unwinding of the hack! For me, running an in-memory database is a much simpler solution :)
Richard Paul