user1452701
user1452701

Reputation: 164

Grails Service can't create Sql instance

How can I create an instance of class groovy.sql.Sql in a Grails service without using dataSource or anything GORM Hibernate related? In my Grails app, in my Service class when I try to create a Sql instance I get an exception:

Caused by: java.lang.ClassNotFoundException: org.postgresql.Driver
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    at java_lang_ClassLoader$loadClass$1.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
    at com.tav.admin.DbService.copyFileToTable(DbService.groovy:87)

Seems to be a class loading issue. Here is the service method.

long copyFileToTable(String sql, FileReader fileReader) {

    println("System Class Loader: ${ClassLoader.getSystemClassLoader().getClass().getName()}")
    println("Groovy Class Loader: ${Thread.currentThread().getContextClassLoader().getClass().getName()}")
    Thread.currentThread().getContextClassLoader().loadClass(driverClassName)
    ClassLoader.getSystemClassLoader().loadClass(driverClassName) // Note throws exception here!

    props = new Properties()
    props.setProperty('user', username)
    props.setProperty('password', password)
    PgConnection copyOperationConnection = (PgConnection) Sql.newInstance(url, props, driverClassName)
    CopyManager copyManager = new CopyManager(copyOperationConnection)
    return copyManager.copyIn(sql, fileReader)
}

Output:

System Class Loader: sun.misc.Launcher$AppClassLoader
Groovy Class Loader: groovy.lang.GroovyClassLoader

The PostgreSQL driver is registered with DriverManager. I can use the injected dataSource in my service but I need to create and new connection to the database unrelated to GORM Hibernate to use driver specific functionality (PostgreSQL CopyManager).

In DriverManager.isDriverAllowed()

Class.forName(driver.getClass().getName(), true, classLoader);

return null.

Version info: Grails 2.5.1, Java jdk1.7.0_79, Groovy 2.0.6


I have narrowed the problem down to the line of code below:

ClassLoader.getSystemClassLoader().loadClass(driverClassName) // Note throws exception here!

Seems like Groovy/Grails loads the JDBC driver using the GroovyClassLoader but when you try to get a connection from DriverManager.getConnection(), driver manager tries to load the JDBC driver using the System Class Loader. The system class loader seems unaware that the Groovy class loader has already loaded the class. It's like the DriverManager and the System Class loader are sandboxed away from the Groovy loaded classes.

Upvotes: 0

Views: 342

Answers (2)

In my case I have the same situation (use Sql without GORM) but with MySQL.

I declared a dataSource bean at resources.groovy :

beans{
   "otherDataSource"(BasicDataSource) {
      driverClassName = 'com.mysql.Driver'
      username = 'xxx'
      password = 'xxxx'
      url = 'jdbc:blablabla'
   }
}

And inject it at the service

class MyService{
    def otherDataSource
    void doIt(){
        def sql = new Sql(otherDataSource)
    }
}

Upvotes: 2

andrewdleach
andrewdleach

Reputation: 2466

In order to interface with PostgreSQL you need to have a suitable driver in the classpath as the stack trace says.

You need to have a dependency included or an external library bundled in a jar that will provide you with a package that implements the `javax.sql.*' interface.

Upvotes: 0

Related Questions