After thinking about it a little more, a more correct fix would be adjusting Inline::Java such that the correct return types came back in the first place. The issue is that while java is strongly typed in the sense that you know what _should_ come back, Inline::Java also has to account for any thrown exception coming back as well. The difficulty goes up a notch when you're dealing with interfaces and classes implementing multiple interfaces. One of the problems is that currently Inline::Java is not tracking the expected return type of the call. At least from what I can see after an admittedly rushed look at it. The data structure built up for method signatures is built around finding a signature that the provided arguments fit. Then it calls the method and returns the result. In this case, it winds up working with Oracle's concrete implementation classes, and never casts them back to the 'official' (and expected) interface classes. Since the interfaces are divergent, you get problems like you saw where methods aren't what you'd expect them to be.
For instance, when getConnection is called, you wind up getting back a oracle.jdbc.driver.T4CConnection rather than a java.sql.Connection. That's fine, if you want to code to the native oracle API, and sometimes that's exactly what you want to do. Similarly, oracle.jdbc.driver.T4CPreparedStatement isn't the same as java.sql.PreparedStatement. I'm pretty sure that with some work, the load_jdat and bind_jdat methods of Java.pm could be made to do the casting for you. The trick is going to be handling exceptions such that they propagate correctly as well as avoiding unneeded casts. If Inline::Java were enhanced in this way, JDBC.pm would probably not have these problems at all. It'd also benefit anyone else who has to use inline java to code to interfaces (as opposed to actual classes). On Thu, Oct 1, 2009 at 7:39 AM, Tim Bunce <tim.bu...@pobox.com> wrote: > Wonderfully helpful reply Jason. Many thanks. > > I wrote JDBC as an experiment. I don't have time to maintain it myself, > sadly, but I'd be very happy for others to either contribute or take > over the module. > > Tim. > > On Tue, Sep 29, 2009 at 01:52:25PM -0400, Jason Stelzer wrote: >> I never heard of the JDBC module before now, but having looked at the >> code, it makes a couple of naive assumptions. Fortunately they're >> pretty easily fixed and not a big deal. To be clear, given how new >> JDBC is, it doesn't shock me that there are a couple minor bugs around >> some drivers. >> >> At its core, the problem is type casting. Oracle's drivers are.... >> annoying to deal with here. The problem you're experiencing is due to >> the fact that you're getting back the wrong interface types from the >> driver. Without patching JDBC.pm you can get the script below to work >> like this: >> >> #!/usr/bin/perl >> use JDBC; >> use Inline::Java qw(cast); >> use strict; >> JDBC->load_driver("oracle.jdbc.driver.OracleDriver"); >> my $url = "jdbc:oracle:thin:system/passwo...@localhost:1521:xe"; >> my $con = cast('java.sql.Connection',JDBC->getConnection($url)); >> my $sql = "SELECT TABLE_NAME FROM USER_TABLES"; >> my $ps = $con->prepareStatement($sql); >> my $rs = cast('java.sql.ResultSet',$ps->executeQuery()); >> # Fake out a row num, select it if you really care about it. >> my $foo = 0; >> while ($rs->next) { >> my $bar = $rs->getString(1); >> print "row: foo=$foo, bar=$bar\n"; >> $foo++; >> } >> >> >> One of the problems that the JDBC module is going to face is that >> there are a lot of buggy JDBC drivers out there that do similar things >> as to what is going on here with oracle. One strategy would be to >> create some perl facade objects to wrap the java objects and enforce >> the api by wrapping the calls to the internal java object and cast >> under the hood. I've done some tricks with autoloader so that I've >> only had to wrap methods that need wrapped. >> >> For instance, if one were to take the JDBC java api and hash method >> names to expected return types, you could put a jdbc object within a >> perl object. $obj = PACKAGE->new($java_ref); >> >> Then, take the $method => returnType above and handle it via >> AUTOLOAD.... something like >> >> sub AUTOLOAD { >> my $self = shift; >> return if $AUTOLOAD =~ /::DESTROY$/; >> my ($meth) = $AUTOLOAD =~ /::(\w+)$/; >> $meth = "SUPER::$meth"; >> return $self->castByInternalMap($self->$meth); >> } >> >> Obviously this solution isn't perfect as it doesn't handle arguments, >> but that is trivial. What I'm getting at is, in order for JDBC.pm to >> really shine, it's going to need to do a little more work to enforce >> the rules of the API, but that work isn't that far of a reach. As >> we've seen, there are vendors out there that rely on some magic in >> order to work. >> >> If you were to go that far, it'd be a pretty cool trick to wrap some >> tied arrays and hashes around some things (like result sets) and fake >> out the (old) DBI interface. I did some stuff like this with ejbs. Its >> very convenient to be able to do things in perl like: >> >> my $ar = $ejb->getBigListOfStuff(); >> foreach my $rec (@$ar){ >> ..... >> } >> >> And let the tied array handle the whole calling next, getting an iterator, >> etc. >> >> Anyways, I hope that helps. >> >> >> On Tue, Sep 29, 2009 at 12:44 PM, <j...@joedog.org> wrote: >> > Hi. I posted this question to the author of JDBC and he proposed that I >> > post it here. >> > >> > I'm trying to execute the following code: >> > >> > #!/usr/bin/perl >> > use JDBC; >> > use strict; >> > JDBC->load_driver("oracle.jdbc.driver.OracleDriver"); >> > my $url = "jdbc:oracle:thin:haha/pa...@server.haha.com:1521:lsd1"; >> > my $con = JDBC->getConnection($url); >> > my $sql = "SELECT TABLE_NAME FROM USER_TABLES"; >> > my $ps = $con->prepareStatement($sql); >> > my $rs = $con->executeQuery(); >> > >> > while ($rs->next) { >> > my $foo = $rs->getInt(1); >> > my $bar = $rs->getString(2); >> > print "row: foo=$foo, bar=$bar\n"; >> > } >> > >> > And I get the following error: >> > >> > lsnas003 # perl ./haha >> > You are not allowed to invoke method prepareStatement in class >> > oracle.jdbc.driver.T4CConnection: Class InlineJavaUserClassLink can not >> > access a member of class oracle.jdbc.driver.PhysicalConnection with >> > modifiers "public synchronized" at (eval 56) line 927 >> > at ./haha line 12 >> > >> > Any thoughts? >> > >> > TIA, >> > Jeff >> > >> > >> > >> > >