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.
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)
}
}
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.
Related
I have cypher queries that make use of APOC functions. It works without problem if running the app directly but I would also like to test those queries. I tried to use following approach but getting an exception Unknown function 'apoc.coll.toSet'
My sample test class:
public class ApocTest {
private static Neo4j neo4j;
private static Driver driver;
#BeforeAll
static void initializeNeo4j() {
// Make sure that the plugins folder is listed in -cp
Path pluginDirContainingApocJar = Paths.get("src/main/resources/neo4j-plugins/");
if (!Files.exists(pluginDirContainingApocJar)) {
throw new IllegalArgumentException("Invalid path to plugins directory");
}
neo4j = Neo4jBuilders
.newInProcessBuilder()
.withDisabledServer()
.withFixture("CREATE (p1:Person)-[:knows]->(p2:Person)-[:knows]->(p3:Person)")
.withConfig(GraphDatabaseSettings.plugin_dir, pluginDirContainingApocJar)
.withConfig(GraphDatabaseSettings.procedure_unrestricted, List.of("apoc.*"))
.build();
driver = GraphDatabase.driver(neo4j.boltURI(), AuthTokens.none());
}
#AfterAll
static void stopNeo4j() {
driver.close();
neo4j.close();
}
#Test
public void testApoc(){
String query = "MATCH path=()-[:knows*2]->()\n" +
"RETURN apoc.coll.toSet(nodes(path)) AS nodesSet";
List<Object> nodesSet = driver.session()
.beginTransaction()
.run(query)
.single()
.get("nodesSet")
.asList();
assertEquals(3, nodesSet.size());
}
}
Any idea how to fix that?
This sample project on the github
Versions:
neo4j-java-driver: 4.1.1
neo4j-harness 4.1.6
org.neo4j.procedure: 4.1.0.5
Update:
So I tried to update:
Path pluginDirContainingApocJar = new File(
ApocConfig.class.getProtectionDomain().getCodeSource().getLocation().toURI())
.getParentFile().toPath();
That means that I don't need to manipulate with apoc jars, right?
But I'm still getting error:
Caused by: org.neo4j.kernel.lifecycle.LifecycleException: Component 'org.neo4j.procedure.impl.GlobalProceduresRegistry#27dc627a' was successfully initialized, but failed to start. Please see the attached cause exception "Unable to set up injection for procedure `CypherProcedures`, the field `cypherProceduresHandler` has type `class apoc.custom.CypherProceduresHandler` which is not a known injectable component.".
at org.neo4j.kernel.lifecycle.LifeSupport$LifecycleInstance.start(LifeSupport.java:463)
at org.neo4j.kernel.lifecycle.LifeSupport.start(LifeSupport.java:110)
at org.neo4j.graphdb.facade.DatabaseManagementServiceFactory.startDatabaseServer(DatabaseManagementServiceFactory.java:189)
... 58 more
Caused by: org.neo4j.kernel.api.exceptions.ComponentInjectionException: Unable to set up injection for procedure `CypherProcedures`, the field `cypherProceduresHandler` has type `class apoc.custom.CypherProceduresHandler` which is not a known injectable component.
at org.neo4j.procedure.impl.FieldInjections.createInjector(FieldInjections.java:98)
at org.neo4j.procedure.impl.FieldInjections.setters(FieldInjections.java:81)
at org.neo4j.procedure.impl.ProcedureCompiler.compileProcedure(ProcedureCompiler.java:264)
at org.neo4j.procedure.impl.ProcedureCompiler.compileProcedure(ProcedureCompiler.java:226)
at org.neo4j.procedure.impl.ProcedureJarLoader.loadProcedures(ProcedureJarLoader.java:114)
at org.neo4j.procedure.impl.ProcedureJarLoader.loadProceduresFromDir(ProcedureJarLoader.java:85)
at org.neo4j.procedure.impl.GlobalProceduresRegistry.start(GlobalProceduresRegistry.java:342)
at org.neo4j.kernel.lifecycle.LifeSupport$LifecycleInstance.start(LifeSupport.java:442)
... 60 more
Update 2 - working on 4.0:
For some reason downgrade to Neo4j 4.0, same version as in recommended, was enough to make it working. Now I won't spend more time to try to run it on Neo4j 4.1/4.2.
My code
Probably the way the path to the plugin directory was created. There's an example from Michael Simons here that explains using the neo4j classloader: https://github.com/michael-simons/neo4j-examples-and-tips/blob/master/examples/testing-ogm-against-embedded-with-apoc/src/test/java/org/neo4j/tips/testing/testing_ogm_against_embedded_with_apoc/ApplicationTests.java#L53
i excute the following code via grails console instead of BootStrap :
class BootStrap {
def fixtureLoader
def init = { servletContext ->
fixtureLoader.load("MockRecords")
}
}
I get the following error :
java.lang.NullPointerException: Cannot invoke method load() on null object
at Script1.run(Script1.groovy:16)
at org.grails.plugins.console.ConsoleService.eval(ConsoleService.groovy:57)
at org.grails.plugins.console.ConsoleService.eval(ConsoleService.groovy:37)
at org.grails.plugins.console.ConsoleController$_closure2.doCall(ConsoleController.groovy:61)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1146)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:679)
undefined
How can i use console to load fixtures
More easy ,
Without using getBean:
ctx.fixtureLoader.load("MockRecords")
it works.
DI(dependency injection) is done automatically in grails through the pattern CoC.
So if you copy / paste the code, the console will not inject the dependency. So it must be injected manually by calling the foctory ctx.getBean ('')
def fixtureLoader=ctx.getBean('fixtureLoader');
fixtureLoader.load("MockRecords")
I have a service that implements InitializingBean and DisposableBean
class MyService implements InitializingBean, DisposableBean {
static transactional = false
def grailsApplication
#Override
void afterPropertiesSet() {
System.setProperty("JMS_TIMEOUT", grailsApplication.config.JMS_TIMEOUT);
// code performing a JDNI lookup
}
}
enter code here
The system properties are used to initialize some other components in the service. I have added the configs in Config.groovy.
grails.config.locations = [ "file:${basedir}/grails-app/conf/myconfig.properties" ]
This works fine when running the application. However I'm writing an integration test in test/integration that injects the service.
class MyServiceIntegrationTests extends GrailsUnitTestCase {
def myService
void testMyService() {
}
}
When running the test I get a StackTrace with the folllowing root cause:
Caused by: javax.naming.NameNotFoundException: Name [ConnectionFactory] not bound; 0 bindings: []
at javax.naming.InitialContext.lookup(InitialContext.java:354)
at com.ubs.ecredit.common.jmsclient.DefaultConnector.<init>(DefaultConnector.java:36)
Seems that the Config could not be loaded or are different in the Integration Tests. Any idea how I can change the config or code, so that these properties are also set for my integration test, before the service is instantiated?
UPDATE:
It turned out the cause was not the configurations but a JDNI lookup and a bug in Grails.
See: http://jira.grails.org/browse/GRAILS-5726
${basedir} gets different paths in different environments. As an alternative, you can use PropertiesLoaderUtils.loadProperties to load your customized configurations:
import org.springframework.core.io.support.PropertiesLoaderUtils
import org.springframework.core.io.ClassPathResource
....
void afterPropertiesSet() {
def configProperties = PropertiesLoaderUtils.loadProperties(
new ClassPathResource("myconfig.properties"))
System.setProperty("JMS_TIMEOUT", configProperties.getProperty("JMS_TIMEOUT"))
....
}
It turned out the cause was a JNDI lookup used by a library method, I have not shown in afterPropertiesSet() in my Service, which can be seen in the StackTrace.
After doing some research I found that this was a bug in Grails: http://jira.grails.org/browse/GRAILS-5726
Adding the mentioned workaround, resolved the issue for now.
I get an NotSerializableException when shutting down Glassfish (3.1), due to an HtmlSelectOneMenu. I use HtmlSelectOneMenu in other controller beans, but they are not bound to the backing bean as this one. This happens only if there are sessions active on the server of course.
How can I avoid exception when starting/stopping Glassfish for the bounded JSF component ?
Controller Class
#ManagedBean
#SessionScoped
...
public class ActivityController implements Serializable {
..
private DataModel<MyObjcet> items = null;
private HtmlSelectOneMenu myMenu;
...
Exception
INFO: PWC2785: Cannot serialize session attribute activityController for session a4591e053e65effc743dade67eef
java.io.NotSerializableException: javax.faces.component.html.HtmlSelectOneMenu
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1164)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1518)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1483)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)
at org.apache.catalina.session.StandardSession.writeObject(StandardSession.java:2067)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1469)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)
at org.apache.catalina.session.StandardManager.writeSessions(StandardManager.java:699)
at org.apache.catalina.session.StandardManager.doUnloadToFile(StandardManager.java:618)
at org.apache.catalina.session.StandardManager.unload(StandardManager.java:589)
at org.apache.catalina.session.StandardManager.stop(StandardManager.java:879)
at org.apache.catalina.core.StandardContext.stop(StandardContext.java:5509)
at com.sun.enterprise.web.WebModule.stop(WebModule.java:529)
at org.apache.catalina.core.ContainerBase.removeChild(ContainerBase.java:1049)
at com.sun.enterprise.web.WebContainer.unloadWebModule(WebContainer.java:2191)
at com.sun.enterprise.web.WebContainer.unloadWebModule(WebContainer.java:2146)
at com.sun.enterprise.web.WebApplication.stop(WebApplication.java:151)
at org.glassfish.internal.data.EngineRef.stop(EngineRef.java:169)
at org.glassfish.internal.data.ModuleInfo.stop(ModuleInfo.java:302)
at org.glassfish.internal.data.ApplicationInfo.stop(ApplicationInfo.java:314)
at com.sun.enterprise.v3.server.ApplicationLifecycle.unload(ApplicationLifecycle.java:997)
at com.sun.enterprise.v3.server.ApplicationLifecycle.disable(ApplicationLifecycle.java:1952)
at com.sun.enterprise.v3.server.ApplicationLoaderService.stopApplication(ApplicationLoaderService.java:443)
at com.sun.enterprise.v3.server.ApplicationLoaderService.preDestroy(ApplicationLoaderService.java:411)
at com.sun.hk2.component.AbstractCreatorInhabitantImpl.dispose(AbstractCreatorInhabitantImpl.java:83)
at com.sun.hk2.component.SingletonInhabitant.release(SingletonInhabitant.java:81)
at com.sun.hk2.component.EventPublishingInhabitant.release(EventPublishingInhabitant.java:108)
at com.sun.hk2.component.LazyInhabitant.release(LazyInhabitant.java:133)
at com.sun.enterprise.v3.server.AppServerStartup.stop(AppServerStartup.java:415)
at com.sun.enterprise.glassfish.bootstrap.GlassFishImpl.stop(GlassFishImpl.java:88)
at com.sun.enterprise.v3.admin.StopServer.doExecute(StopServer.java:70)
at com.sun.enterprise.v3.admin.StopDomainCommand.execute(StopDomainCommand.java:95)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$1.execute(CommandRunnerImpl.java:355)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$2.run(CommandRunnerImpl.java:383)
Make it transient.
private transient HtmlSelectOneMenu myMenu;
This way the field will be skipped on (de)serialization. By the way, the DataModel is also not serializable.
On the other hand, binding components directly to the backing bean is in most cases a smell. You should consider looking (or asking) for an alternative approach which does not require binding components to the bean.
I try to execute raw SQL in Grails with this code:
class PlainSqlService {
def dataSource // the Spring-Bean "dataSource" is auto-injected
def newNum = {
def sql = new Sql(dataSource) // Create a new instance of groovy.sql.Sql with the DB of the Grails app
def q = "SELECT a.xaction_id, a.xdin FROM actions a WHERE a.is_approved = 0"
def result = sql.rows(q) // Perform the query
return result
}
}
But I get this exception at runtime.
sql object is not null!
How can I debug it?
2011-02-13 15:55:27,507 [http-8080-1] ERROR errors.GrailsExceptionResolver - Exception occurred when processing request: [GET] /moderator/login/index
Stacktrace follows:
java.lang.NullPointerException
at moderator.PlainSqlService$_closure1.doCall(PlainSqlService.groovy:17)
at moderator.PlainSqlService$_closure1.doCall(PlainSqlService.groovy)
at moderator.LoginController$_closure1.doCall(LoginController.groovy:29)
at moderator.LoginController$_closure1.doCall(LoginController.groovy)
at java.lang.Thread.run(Thread.java:662)
It's hard to tell what's going on from the limited code you're providing, but there are some things to check. Is the service injected into the controller with a class-scope field "def plainSqlService" like you have here for the dataSource, or are you calling new PlainSqlService()? If you're creating a new instance then the dataSource bean won't be injected and the groovy.sql.Sql constructor won't fail, but queries will.
One thing to try is grails clean - whenever something like this that should work doesn't, a full recompile often helps.
One important but unrelated point - you should never use Closures in services. Controllers and taglibs require that actions and tags be implemented with a Closure, but a Service is just a Spring bean defined in Groovy. Spring knows nothing about Closures and since they're just a field that Groovy executes as if it were a method, any proxying that you're expecting from Spring (in particular transactional behavior, but also security and other features) will not happen since Spring only looks for methods.
So newNum should be declared as:
def newNum() {
...
}