Listen Print Discuss

Using Java Classes in Perl

by Andrew Hanenkamp
December 21, 2006

I started a new job recently to refocus my career from systems administration to web development. Part of that move meant using Java as my primary language at work and using a relatively new technology from the Java Community Process (JCP), the Content Repository API for Java (JCR), which is a hierarchical database standard for storing content. However, not wanting to let the skills in my favorite language waste away, I've been toying with similar technologies at home using Perl. I decided to make a direct port of the JCR to Perl and did so by making Perl use an existing Java implementation via Inline::Java. While I ran into some snags along the way, I was happily surprised to find the process of using Java classes from Perl was fabulously easy.

Bringing the JCR to Perl

The key to using JCR from Perl is Inline::Java. This library allows a Perl program to call Java methods with very little effort. For an introduction to Inline::Java, I suggest starting where I did, Phil Crow's 2003 Bringing Java into Perl article on Perl.com about Inline::Java. I also relied heavily upon the documentation for Inline::Java, which is very complete, if not exhaustive.

To get started on using the JCR, I used the reference implementation, Jackrabbit. I downloaded the Jackrabbit JAR file, along with all the prerequisites, which I found on the Jackrabbit website under First Hops. Then, I wrote a small script using Inline::Java to load the Java classes from Jackrabbit, create a repository, and then quit. I was able to take the First Hop with Jackrabbit in Perl as fast or faster than in Java:

#!/usr/bin/perl
use strict; 
use warnings;
use Inline
    Java => 'STUDY',
    STUDY => [ qw(
        org.apache.jackrabbit.core.TransientRepository
        javax.jcr.Repository
    ) ],
    AUTOSTUDY => 1;

my $repository = org::apache::jackrabbit::core::TransientRepository->new;
my $session = $repository->login;
eval {
    my $user = $session->getUserID;
    my $name = $repository
        ->getDescriptor($javax::jcr::Repository::REP_NAME_DESC);
    print "Logged in as $user to a $name repository.\n";
};

if ($@) {
    print STDERR "Exception: ", $@->getMessage, "n";
}

$session->logout;

This code is a direct Perl port of the first tutorial on the Jackrabbit website. To run the code, you must make sure your class path is correct. Because I initially dropped the JCR files into my working directory, I just ran these commands to get it to work:

% export CLASSPATH=$CLASSPATH:`echo *.jar | tr ' ' ':'`
% perl firsthop.pl

Within five minutes, I had a Perl script that could access the Jackrabbit libraries, create a repository, and login as anonymous. This answered my first question: Can I port the JCR to Perl? Yes.

First Snags

After proceeding to the "Second Hop" in the Jackrabbit tutorial, I ran into my first snag. To create nodes and properties with Jackrabbit, you must log in using a username and password. However, the JCR uses an array of characters for the password argument. Because Inline::Java helpfully translates Java string objects into Perl scalars, I could not determine a way to do so.

I also realized that I did not want to use lengthy Java namespaces in my Perl code. Writing out org::apache::jackrabbit::core::TransientRepository or javax::jcr::Repository is not a very productive use of my time and makes for odd-looking Perl code.

In addition, I didn't want a library that depended on Jackrabbit. There are several other JCR implementations either already written or on the way. Day has CRX, there's another Open Source implementation named Jaceira in the works, and eXo has also created a JCR implementation, to name a few.

Given these difficulties and the potential for other problems that I knew would come up, it was time to build this project as a Perl module.

Creating the Wrappers

To create the abstraction I desired, it quickly became apparent that I needed a way to build wrappers around the stubs generated by Inline::Java. Therefore, I set about writing a script that could generate a Perl package for each library in the JCR. Each wrapper package would, in addition to helping wrap special cases, clean up the Java namespace using naming conventions that are more common to Perl code (particularly my Perl code, which is similar to Conway's conventions from Perl Best Practices).

Using Java Reflection

I first needed to discover the classes, methods, and fields to wrap. There are more than 50 classes, interfaces, and exceptions in the JCR specification--I'm too lazy to type all that. Furthermore, the JCR is currently under revision via JSR 283, I don't want to update the class list again later. Finally, I want my wrappers to handle each method specifically because the use of AUTOLOAD() is evil (sometimes useful, but still evil).

I wrote a Java program to find all the classes in the JCR JAR file and write those class names out with additional information about methods, constructors, and fields. I used a YAML-formatted file to store the information. I made heavy use of the Java Reflection API to make this happen. You can see the full source of JCR package generator in the Java::JCR distribution. Here's one entry in the YAML JCR package output file:

javax.jcr.SimpleCredentials:
  isa:
   - java.lang.Object
   - javax.jcr.Credentials
  has_constructors: 1
  methods:
    instance:
      getAttributeNames: Array:java.lang.String
      getUserID: java.lang.String
      toString: java.lang.String
      getPassword: Array:char
      getAttribute: java.lang.Object
      setAttribute: void
      removeAttribute: void

The information I chose to place in the YAML file is mostly the outcome of experimentation with the Perl generator script. Because I wrote a generic handler to perform the required unwrapping that can handle any set of arguments, I didn't bother to remember them here. On the other hand, knowing the return type, recorded after each method name, is helpful to my implementation.

Pages: 1, 2, 3

Next Pagearrow





Contact Us | Advertise with Us | Privacy Policy | Press Center | Jobs | Submissions Guidelines

Copyright © 2000-2008 O’Reilly Media, Inc. All Rights Reserved. | (707) 827-7000 / (800) 998-9938
All trademarks and registered trademarks appearing on the O'Reilly Network are the property of their respective owners.

For problems or assistance with this site, email