Hello everyone
I intend to use ABCL in one of my projects. I've started to connectiing to a postgresq database via jdbc. I've tried something like:
-------8<------- (add-to-classpath (merge-pathnames #p".m2/repository/postgresql/postgresql/8.4-702.jdbc4/postgresql-8.4-702.jdbc4.jar"
(user-homedir-pathname)))
(jstatic "forName" "java.lang.Class" "org.postgresql.Driver") -------8<-------
But it always returns -------8<------- Java exception 'java.lang.ClassNotFoundException: org.postgresql.Driver'. [Condition of type JAVA-EXCEPTION] -------8<------- The jar file exists, and has the proper class inside. I've read the manual, and googled a bit, but still get the same error. I'm using Ubuntu Linux, with OpenJDK 1.6.0_24, and the latest and greatest ABCL downloaded as a binary from ABCL website. Can someone point me what I'm doing wrong?
Many thanks for this great project!!
On Thu, May 17, 2012 at 11:04 PM, Francisco Vides Fernández fvides@igaleno.com wrote:
Hello everyone
I intend to use ABCL in one of my projects. I've started to connectiing to a postgresq database via jdbc. I've tried something like:
-------8<------- (add-to-classpath (merge-pathnames #p".m2/repository/postgresql/postgresql/8.4-702.jdbc4/postgresql-8.4-702.jdbc4.jar" (user-homedir-pathname)))
(jstatic "forName" "java.lang.Class" "org.postgresql.Driver") -------8<-------
But it always returns -------8<------- Java exception 'java.lang.ClassNotFoundException: org.postgresql.Driver'. [Condition of type JAVA-EXCEPTION] -------8<------- The jar file exists, and has the proper class inside. I've read the manual, and googled a bit, but still get the same error. I'm using Ubuntu Linux, with OpenJDK 1.6.0_24, and the latest and greatest ABCL downloaded as a binary from ABCL website. Can someone point me what I'm doing wrong?
TL;DR: use (java:jclass "org.postgresql.Driver").
Longer explanation: the JVM loads classes using an abstraction called a ClassLoader. Different class loaders form separated code repositories, so to speak. Also, classloaders form a hierarchy, because each one (except the bootstrap one) has a parent. Class.forName uses a classloader that does not know about ABCL's dynamic classloader, i.e. it's not affected by add-to-classpath - which really has effect only on ABCL, not on the JVM as a whole, as per the above. ABCL's classloader, on the other hand, does know about its parent - which is the one used to load abcl.jar, i.e. the system one in a standalone application, and probably the same Class.forName will use.
jclass knows and uses the right classloader, so it does what you want.
You *can* use Class.forName if you really want to, because a) ABCL's classloader is exposed via a dynamic variable (java:*classloader*? Sorry but I don't remember the details right now...) and b) IIRC there's an overload for Class.forName that takes an explicit ClassLoader argument. But I don't see any reason for doing that.
You're not the first to be bitten by those complications, and not just on ABCL. Any language or library that introduces a mechanism to dynamically load jars faces the same issues.
I hope I solved your problem, and... have fun with ABCL! :)
Peace, Alessio
El 18/05/12 00:21, Alessio Stalla escribió:
On Thu, May 17, 2012 at 11:04 PM, Francisco Vides Fernández fvides@igaleno.com wrote:
Hello everyone
I intend to use ABCL in one of my projects. I've started to connectiing to a postgresq database via jdbc. I've tried something like:
-------8<------- (add-to-classpath (merge-pathnames #p".m2/repository/postgresql/postgresql/8.4-702.jdbc4/postgresql-8.4-702.jdbc4.jar" (user-homedir-pathname)))
(jstatic "forName" "java.lang.Class" "org.postgresql.Driver") -------8<-------
But it always returns -------8<------- Java exception 'java.lang.ClassNotFoundException: org.postgresql.Driver'. [Condition of type JAVA-EXCEPTION] -------8<------- The jar file exists, and has the proper class inside. I've read the manual, and googled a bit, but still get the same error. I'm using Ubuntu Linux, with OpenJDK 1.6.0_24, and the latest and greatest ABCL downloaded as a binary from ABCL website. Can someone point me what I'm doing wrong?
TL;DR: use (java:jclass "org.postgresql.Driver").
[snip]
Yes, that did the trick, but now I've the following code: --------------8x-------------- (java:jclass "org.postgresql.Driver") #<java.lang.Class class org.postgresql.Driver {67AFF9E8}> ;; the driver loads, yay!
(java:jstatic "getConnection" "java.sql.DriverManager" "jdbc:postgresql://localhost:5432/test") --------------8x-------------- Throws:
--------------8x-------------- Java exception 'java.sql.SQLException: No suitable driver found for jdbc:postgresql://localhost:5432/test'. [Condition of type JAVA-EXCEPTION] --------------8x--------------
But the equivalent java code works: --------------8x-------------- public class JdbcConn { public static void main(String[] args) { try { Class.forName("org.postgresql.Driver"); java.sql.Connection c = java.sql.DriverManager.getConnection("jdbc:postgresql://localhost:5432/test"); } catch(Exception e) { e.printStackTrace(System.err); } } } --------------8x--------------
I fail to see how the ClassLoader machinery affects this. The org.postgresql.Driver class self-registers with driver manager, in a static block of code:
--------------8x-------------- java.sql.DriverManager.registerDriver(new Driver()); --------------8x--------------
But apparently that isn't happening. I've even tried to do that by hand but nothing.
Some clue on what's happening?
On Fri, May 18, 2012 at 11:20 AM, Francisco Vides Fernández fvides@dedaloingenieros.com wrote:
El 18/05/12 00:21, Alessio Stalla escribió:
On Thu, May 17, 2012 at 11:04 PM, Francisco Vides Fernández fvides@igaleno.com wrote:
Hello everyone
I intend to use ABCL in one of my projects. I've started to connectiing to a postgresq database via jdbc. I've tried something like:
-------8<------- (add-to-classpath (merge-pathnames #p".m2/repository/postgresql/postgresql/8.4-702.jdbc4/postgresql-8.4-702.jdbc4.jar" (user-homedir-pathname)))
(jstatic "forName" "java.lang.Class" "org.postgresql.Driver") -------8<-------
But it always returns -------8<------- Java exception 'java.lang.ClassNotFoundException: org.postgresql.Driver'. [Condition of type JAVA-EXCEPTION] -------8<------- The jar file exists, and has the proper class inside. I've read the manual, and googled a bit, but still get the same error. I'm using Ubuntu Linux, with OpenJDK 1.6.0_24, and the latest and greatest ABCL downloaded as a binary from ABCL website. Can someone point me what I'm doing wrong?
TL;DR: use (java:jclass "org.postgresql.Driver").
[snip]
Yes, that did the trick, but now I've the following code: --------------8x-------------- (java:jclass "org.postgresql.Driver") #<java.lang.Class class org.postgresql.Driver {67AFF9E8}> ;; the driver loads, yay!
(java:jstatic "getConnection" "java.sql.DriverManager" "jdbc:postgresql://localhost:5432/test") --------------8x-------------- Throws:
--------------8x-------------- Java exception 'java.sql.SQLException: No suitable driver found for jdbc:postgresql://localhost:5432/test'. [Condition of type JAVA-EXCEPTION] --------------8x--------------
But the equivalent java code works: --------------8x-------------- public class JdbcConn { public static void main(String[] args) { try { Class.forName("org.postgresql.Driver"); java.sql.Connection c = java.sql.DriverManager.getConnection("jdbc:postgresql://localhost:5432/test"); } catch(Exception e) { e.printStackTrace(System.err); } } } --------------8x--------------
I fail to see how the ClassLoader machinery affects this. The org.postgresql.Driver class self-registers with driver manager, in a static block of code:
--------------8x-------------- java.sql.DriverManager.registerDriver(new Driver()); --------------8x--------------
But apparently that isn't happening. I've even tried to do that by hand but nothing.
Some clue on what's happening?
Bummer. Classes might not be initialized (or whatever is the correct technical term) when loaded, depending on how you load them. That includes (not) running static initializer code. Evidently jclass is using the wrong method for your use case.
As a workaround, you could try to force initialization of the class by accessing some static field or method or ask for some metadata (jclass-methods, jclass-name, ...). But I don't know if that's guaranteed to work.
Or you can revert to Class.forName, but ensuring to use the overload that takes an explicit classloader, and feeding it the one used by ABCL (look in java.lisp to see how to retrieve it).
On the long term, though, jclass really needs to take an extra argument to force initialization.
Alessio
On 5/18/12 12:14 , Alessio Stalla wrote:
On Fri, May 18, 2012 at 11:20 AM, Francisco Vides Fernández
[…]
I intend to use ABCL in one of my projects. I've started to connectiing to a postgresq database via jdbc. I've tried something like:
-------8<------- (add-to-classpath (merge-pathnames #p".m2/repository/postgresql/postgresql/8.4-702.jdbc4/postgresql-8.4-702.jdbc4.jar" (user-homedir-pathname)))
(jstatic "forName" "java.lang.Class" "org.postgresql.Driver") -------8<-------
But it always returns -------8<------- Java exception 'java.lang.ClassNotFoundException: org.postgresql.Driver'. [Condition of type JAVA-EXCEPTION]
Or you can revert to Class.forName, but ensuring to use the overload that takes an explicit classloader, and feeding it the one used by ABCL (look in java.lisp to see how to retrieve it).
On the long term, though, jclass really needs to take an extra argument to force initialization.
[…]
One might try using the JSS:NEW operator which has a slightly more aggressive method of locating Java classes with the [java.sql.DriverManager API][1], which is the preferred way of loading JDBC drivers rather than the Class.forName() method.
[1]: http://docs.oracle.com/javase/6/docs/api/java/sql/DriverManager.html
For loading Oracle drivers, the following code works for me. I would expect that it would for Postgres as well with suitable modification of the parameters.
(require 'abcl-contrib) (require 'abcl-asdf) (require 'jss)
(defparameter *driver-pathname* "~/dist/ojdbc6.jar") (defparameter *driver-class* "oracle.jdbc.OracleDriver")
(defun ensure-driver () (java:add-to-classpath *driver-pathname*) (#"registerDriver" 'java.sql.DriverManager (new *driver-class*)))
One might try using the JSS:NEW operator which has a slightly more aggressive method of locating Java classes with the [java.sql.DriverManager API][1], which is the preferred way of loading JDBC drivers rather than the Class.forName() method.
Even this didn't work in my case.
(java:jnew "org.postgresql.Driver") #<org.postgresql.Driver org.postgresql.Driver@48507269 {2B42FB92}>
(java:jstatic "registerDriver" "java.sql.DriverManager" (java:jnew "org.postgresql.Driver")) NIL
(open-database) ; Evaluation aborted on #<JAVA-EXCEPTION com.hp.hpl.jena.db.RDFRDBException: Failure to instantiate DB Driver:PostgreSQL java.sql.SQLException: No suitable driver found for jdbc:postgresql:jena_test {6E18E546}>.
On Fri, May 18, 2012 at 1:57 PM, Alex Mizrahi alex.mizrahi@gmail.com wrote:
One might try using the JSS:NEW operator which has a slightly more aggressive method of locating Java classes with the [java.sql.DriverManager API][1], which is the preferred way of loading JDBC drivers rather than the Class.forName() method.
Even this didn't work in my case.
(java:jnew "org.postgresql.Driver") #<org.postgresql.Driver org.postgresql.Driver@48507269 {2B42FB92}>
(java:jstatic "registerDriver" "java.sql.DriverManager" (java:jnew "org.postgresql.Driver")) NIL
(open-database) ; Evaluation aborted on #<JAVA-EXCEPTION com.hp.hpl.jena.db.RDFRDBException: Failure to instantiate DB Driver:PostgreSQL java.sql.SQLException: No suitable driver found for jdbc:postgresql:jena_test {6E18E546}>.
I think that should be jdbc:postgresql://jena_test - note the double slash.
A.
I think that should be jdbc:postgresql://jena_test - note the double slash.
This makes no difference:
#<JAVA-EXCEPTION com.hp.hpl.jena.db.RDFRDBException: Failure to instantiate DB Driver:PostgreSQL java.sql.SQLException: No suitable driver found for jdbc:postgresql://jena_test {27FEFFAC}>.
But everything works (without double slash) when jar is specified via java -cp
As a workaround, you could try to force initialization of the class by accessing some static field or method or ask for some metadata (jclass-methods, jclass-name, ...). But I don't know if that's guaranteed to work.
I've got it working once using something like java:jclass in SLIME REPL, but not it doesn't seem to work (in a slightly different environment).
Note that if jar is added to classpath via java commandline Class.forName will work fine. That's probably the easiest way to get it working. Only loading jar dynamically via classloader is a problem.
Or you can revert to Class.forName, but ensuring to use the overload that takes an explicit classloader, and feeding it the one used by ABCL (look in java.lisp to see how to retrieve it).
Just tried it, doesn't seem to work either.
El 18/05/12 13:28, Alex Mizrahi escribió:
As a workaround, you could try to force initialization of the class by accessing some static field or method or ask for some metadata (jclass-methods, jclass-name, ...). But I don't know if that's guaranteed to work.
I've got it working once using something like java:jclass in SLIME REPL, but not it doesn't seem to work (in a slightly different environment).
Note that if jar is added to classpath via java commandline Class.forName will work fine. That's probably the easiest way to get it working. Only loading jar dynamically via classloader is a problem.
I've also tried that. Started ABCL with 'java -cp $JDBC_JAR abcl.jar' and Class.forName still didn't work. Am I missing some parameter?
Or you can revert to Class.forName, but ensuring to use the overload that takes an explicit classloader, and feeding it the one used by ABCL (look in java.lisp to see how to retrieve it).
Just tried it, doesn't seem to work either.
armedbear-devel mailing list armedbear-devel@common-lisp.net http://lists.common-lisp.net/cgi-bin/mailman/listinfo/armedbear-devel
I've got it working once using something like java:jclass in SLIME REPL, but not it doesn't seem to work (in a slightly different environment).
Note that if jar is added to classpath via java commandline Class.forName will work fine. That's probably the easiest way to get it working. Only loading jar dynamically via classloader is a problem.
I've also tried that. Started ABCL with 'java -cp $JDBC_JAR abcl.jar' and Class.forName still didn't work. Am I missing some parameter?
It looks like you can't use both -cp and -jar.
Here's what works for me:
--- $ java -cp abcl.jar:postgresql.jdbc3.jar org.armedbear.lisp.Main
Armed Bear Common Lisp 1.0.1-svn-13932 Java 1.6.0_20 Sun Microsystems Inc. OpenJDK 64-Bit Server VM Low-level initialization completed in 1.024 seconds. Startup completed in 3.393 seconds. Type ":help" for a list of available commands.
CL-USER(1): (jstatic "forName" "java.lang.Class" "org.postgresql.Driver") #<java.lang.Class class org.postgresql.Driver {218C6982}>
CL-USER(6): (java:jstatic "getConnection" "java.sql.DriverManager" "jdbc:postgresql:jena_test" "alex" "xxxxxx") #<org.postgresql.jdbc3.Jdbc3Connection org.postgresql.jdbc3.Jdbc3Connec.... {4FC0CB76}> ---
So, I specify both postgresql and abcl jars in -cp and load class org.armedbear.lisp.Main (it starts interpreter and repl). Then, well, it works. I use older version of PostgreSQL driver, but I think it doesn't really matter.
HTH
On May 18, 2012, at 14:54 , Alex Mizrahi wrote:
I've got it working once using something like java:jclass in SLIME REPL, but not it doesn't seem to work (in a slightly different environment).
Note that if jar is added to classpath via java commandline Class.forName will work fine. That's probably the easiest way to get it working. Only loading jar dynamically via classloader is a problem.
[…]
Apparently the postgres-jdbc DriverManager API has problems if the 'postgresql.jar' is not on the system classpath, which doesn't seem to be true for the Oracle JDBC classes.
The following uses the getConnection() method directly on the org.postgresql.Driver:
(add-to-classpath "/opt/local/share/java/postgresql.jar") (jcall "connect" (jnew "org.postgresql.Driver") "jdbc:postgresql:test" (jnew "java.util.Properties"))
I would recommend using this over messing with the ABCL classpath, as you'll have to include "extra" instructions to people to use your interface beyond the Lisp code you ship.
P.S. The "-jar" and "-cp" options to Java are always mutually exclusive.
I have had trouble if there is an associated native library that needs to be loaded - in that case the java initializer will expect to be able to load the native library, but I don't know any way to dynamically extend the native library path. That may not be happening in your case, but I figured I'd add this factoid to the thread for later searching. If anyone knows how to actually change java.library.path at run time (and have the change matter), do let me know. -Alan
On Fri, May 18, 2012 at 9:08 AM, Mark Evenson evenson@panix.com wrote:
On May 18, 2012, at 14:54 , Alex Mizrahi wrote:
I've got it working once using something like java:jclass in SLIME
REPL, but not it doesn't seem to work (in a slightly different environment).
Note that if jar is added to classpath via java commandline
Class.forName will work fine. That's probably the easiest way to get it working.
Only loading jar dynamically via classloader is a problem.
[…]
Apparently the postgres-jdbc DriverManager API has problems if the 'postgresql.jar' is not on the system classpath, which doesn't seem to be true for the Oracle JDBC classes.
The following uses the getConnection() method directly on the org.postgresql.Driver:
(add-to-classpath "/opt/local/share/java/postgresql.jar") (jcall "connect" (jnew "org.postgresql.Driver") "jdbc:postgresql:test" (jnew "java.util.Properties"))
I would recommend using this over messing with the ABCL classpath, as you'll have to include "extra" instructions to people to use your interface beyond the Lisp code you ship.
P.S. The "-jar" and "-cp" options to Java are always mutually exclusive.
armedbear-devel mailing list armedbear-devel@common-lisp.net http://lists.common-lisp.net/cgi-bin/mailman/listinfo/armedbear-devel
On Thu, May 24, 2012 at 4:30 AM, Alan Ruttenberg alanruttenberg@gmail.com wrote:
I have had trouble if there is an associated native library that needs to be loaded - in that case the java initializer will expect to be able to load the native library, but I don't know any way to dynamically extend the native library path. That may not be happening in your case, but I figured I'd add this factoid to the thread for later searching. If anyone knows how to actually change java.library.path at run time (and have the change matter), do let me know.
The only way I know is mucking with ClassLoader via reflection. Not pretty and not portable, since the accessed fields are private.
Alessio
armedbear-devel@common-lisp.net