Reputation: 164
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
Reputation: 362
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
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