Jason, On Thu, Oct 1, 2009 at 10:10 AM, Jason Stelzer <men...@neverlight.com>wrote:
> 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. > When I developped Inline::Java I chose to present the Java objects in Perl using their most specific type to make the behavior more Perlish. That way, if you have a method whose return type is java.lang.Object, you can immediately use the object in Perl with it's correct type, no need to cast. Actually the cast() feature in Inline::Java only appeared much later. Choosing to always give objects the return types of the methods that returned them would break a lot of things, and would actually introduce more casting (i.e. anytime you would work with generic containers, like Vectors, you would always need to cast (just like in Java basically)). That in my opinion would remove some of the "magic"... And because all overridden Java methods are virtual, meaning you can't really call the wrong method when it's overridden (unlike C++ I think), in reality this only seems to be a problem in these specific circumstances: non-public classes that implement public interfaces or extend public base classes. I'll try to get a dev release out that tries it's best to detect this case (not entirely trivial though...) and only in this case cast the object using the method return type. That shouldn't break existing code to much and should help in these cases. Jason: if I can send a tarball to the mailing list, can you give it a try and see if it solves the JDBC issues (I don't have a JDBC setup handy)? Note: If anyone has input on this please don't hesitate to share it. Patrick > 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 > >> > > >> > > >> > > >> > > > > -- ===================== Patrick LeBoutillier Rosemère, Québec, Canada