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
>> >
>> >
>> >
>> >
>

Reply via email to