Bringing Java into Perl
In this article, I will show how to bring Java code into a Perl program with Inline::Java
. I won’t probe the internals of Inline
or Inline::Java
, but I will tell you what you need to make a Java class available in a program or module. The program/module distinction is important only in one small piece of syntax, which I will point out.
The article starts with the Java code to be glued into Perl, then shows several approaches for doing so. First, the code is placed directly into a Perl program. Second, the code is placed into a module used by a program. Finally, the code is accessed via a small Perl proxy in the module.
The Java Code
Consider the following Java class:
public class Hi {
String greeting;
public Hi(String greeting) {
this.greeting = greeting;
}
public void setGreeting(String newGreeting) {
greeting = newGreeting;
}
public String getGreeting() {
return greeting;
}
}
This class is for demonstration only. Each of its objects is nothing but a wrapper for the string passed to the constructor. The only operations are accessors for that one string. Yet with this, we will learn most of what we need to know to use Java from Perl. Later, we will add a few features, to show how arrays are handled. That’s not as interesting as it sounds, since Inline::Java
almost always does all of the work without help.
A Program
Since we’re talking about Perl, there is more than one way to incorporate our trivial Java class into a Perl program. (Vocabulary Note: Some people call Perl programs “scripts.” I try not to.) Here, I’ll show the most direct approach. Subsequent sections move to more and more indirect approaches, which are more often useful in practice.
Not surprisingly, the most direct approach is the simplest to understand. See if you can follow this:
#!/usr/bin/perl
use strict; use warnings;
use Inline Java => <<'EOJ';
public class Hi {
// The class body is shown in the Java Code above
}
EOJ
my $greeter = Hi->new("howdy");
print $greeter->getGreeting(), "\n";
The Java class is the one above, so I have omitted all but the class declaration. The Perl code just wraps it, so it is tiny. To use Inline::Java
, say use Inline Java => code
where code
tells Inline
where to look for the code. In this case, the code follows inline (clever naming, huh?). Note that single-quote context is safest here. There are other ways to include the code; we’ll see my favorite way later. The overly curious are welcome to consult the perldoc for all of the others.
Once Inline::Java
has worked its magic – and it is highly magical – we can use the Java Hi
class as if it was a Perl package. Inline::Java
provides several ways to construct Java objects. I usually use the one shown here; namely, I pretend the Java constructor is called new
, just like many Perl constructors are. In honor of Java, you might rather say my $greeter = new Hi("howdy");
, but I usually avoid this indirect object form. You can even call the constructor by the class name as in my $greeter = Hi->Hi("howdy");
(or, you could even say the pathological my $greeter = Hi Hi("howdy");
). Class methods are accessed just like the constructor, except that their names are the Java method names. Instance methods are called through an object reference, as if the reference were a Perl object.
Note that Inline::Java
performs type conversions for us, so we can pass and receive Java primitive types in the appropriate Perl variables. This carries over to arrays, etc. When you think about what must be going on under the hood, you’ll realize what a truly magical module this is.
A Module
I often say that most Perl code begins life in a program. As time goes by, the good parts of that code, the ones that can be reused, are factored out into modules. Suppose our greeter is really popular, so many programs want to use it. We don’t want to have to include the Java code in each one (and possibly require each program to compile its own copy of the class file). Hence, we want a module. My module looks a lot like my earlier program, except for two features. First, I changed the way Inline
looks for the code, which has nothing to do with whether the code is in a program or a module. Second, reaching class methods from any package other than main
requires careful – though not particularly difficult – qualification of the method name.
package Hi;
use strict; use warnings;
use Inline Java => "DATA";
sub new {
my $class = shift;
my $greeting = shift;
return Hi::Hi->new($greeting);
}
1;
__DATA__
__Java__
public class Hi {
// The class body is shown in The Java Code above
}
The package starts like all good packages, by using strict
and warnings
. The use Inline
statement is almost like the previous one, but the code lives in the __DATA__
segment instead of actually being inline. Note that when you put the code in the __DATA__
segment, you must include a marker for your language so that Inline
can find it. There are usually several choices for each language’s marker; I chose __Java__
. This allows Inline
to glue from multiple languages into one source file.
The constructor is needed so that the caller does not need to know they are interfacing with Inline::Java
. They call the constructor with Hi->new("greeting")
as they would for a typical package called Hi
. Yet, the module’s constructor must do a bit of work to get the right object for the caller. It starts by retrieving the arguments, then returns the result of the unusual call Hi::Hi->new(...)
. The first Hi
is for the Perl package and the second is for the Java class; both are required. Just as in the program from the last section, there are multiple ways to call the constructor. I chose the direct method with the name new
. You could use the indirect object form and/or call the method by the class name. The returned object can be used as normal, so I just pass it back to the caller. All instance methods are passed directly through Inline::Java
without help from Hi.pm
. If there were class methods (declared with the static
keyword in Java), I would either have to provide a wrapper, or the caller would have to qualify the names. Neither solution is particularly difficult, but I favor the wrapper, to keep the caller’s effort to a minimum. This is my typical laziness at work. Since there will likely be several callers, and I will have to write them, I want to push any difficult parts into the module.
If you need to adapt the behavior of the Java object for your Perl audience, you may insert routines in Hi.pm
to do that. For instance, perhaps you want a more typical Perl accessor, instead of the get
/set
pair used in the Java code. In this case, you must make your own genuine Perl object and proxy through it to the Java class. That might look something like this:
package Hi2;
use strict; use warnings;
use Inline Java => "DATA";
sub new {
my $class = shift;
my $greeting = shift;
bless { OBJECT => Hi2::Hi->new($greeting) }, $class;
}
sub greeting {
my $self = shift;
my $new_value = shift;
if (defined $new_value) {
$self->{OBJECT}->setGreeting($new_value);
}
return $self->{OBJECT}->getGreeting();
}
1;
__DATA__
__Java__
public class Hi {
// Body omitted again
}
Here, the object returned from Inline::Java
, which I’ll call the Java object for short, is stored in the OBJECT
key of a hash-based Hi2
object that is returned to the caller. The distinction between the Perl package and the Java class is clear in this constructor call. The Perl package comes first, then the Java class, then the class method to call.
The greeting
method, shifts in the $new_value
, which the caller supplies if she wants to change the value. If $new_value
is defined, greeting
passes the set
message to the Java object. In either case, it returns the current value to the caller, as Perl accessors usually do.
A Pure Proxy
In the last section, we saw how to make a Perl module access Java code. We also saw how to make the Perl module adapt between the caller’s expectation of Perl objects and the underlying Java objects. Here, we will see how to access Java classes that can’t be included in the Perl code.
There are a lot of Java libraries. These are usually distributed in compiled form in so-called .jar (java archive) files. This is good design on the part of the Java community, just as using modules is good design on the part of the Perl community. Just as we wanted to make the Hi
Java class available to lots of programs – and thus placed it in a module – so the Java people put reusable code in .jars. (Yes, Java people share the bad pun heritage of the Unix people, which brought us names like yacc
, bison
, more
, and less
.)
Suppose that our humble greeter is so popular that it has been greatly expanded and .jarred for worldwide use. Unless we provide an adapter like the one shown earlier, the caller must use the .jarred code from Perl in a Java-like way. So I will now show three pieces of code: 1) an expanded greeter, 2) a Perl driver that uses it, and 3) a mildly adapting Perl module the driver can use.
Here’s the expanded greeter; the two Perl pieces follow later:
import java.util.Random;
public class Higher {
private static Random myRand = new Random();
private String[] greetings;
public Higher(String[] greetings) {
this.greetings = greetings;
}
public void setGreetings(String[] newGreetings) {
greetings = newGreetings;
}
public String[] getGreetings() {
return greetings;
}
public void setGreeting(int index, String newGreeting) {
greetings[index] = newGreeting;
}
public String getGreeting() {
float randRet = myRand.nextFloat();
int index = (int) (randRet * greetings.length);
return greetings[index];
}
}
Now there are multiple greetings, so the constructor takes an array of Strings
. There are get
/set
pairs for the whole list of greetings and for single greetings. The single get
accessor returns one greeting at random. The single set
accessor takes the index of the greeting to replace and its new value.
Note that Java arrays are fixed-size; don’t let Inline::Java
fool you into thinking otherwise. It is very good at making you think Java works just like Perl, even though this is not the case. Calling setGreeting
with an out-of-bounds index will be fatal unless trapped. Yes, you can trap Java exceptions with eval
and the $@
variable.
This driver uses the newly expanded greeter through Hi3.pm
:
#!/usr/bin/perl
use strict; use warnings;
use Hi3;
my $greeter = Hi3->new(["Hello", "Bonjour", "Hey Y'all", "G'Day"]);
print $greeter->getGreeting(), "\n";
$greeter->setGreeting(0, "Howdy");
print $greeter->getGreeting(), "\n";
The Hi3
module (directly below) provides access to the Java code. I called the constructor with an anonymous array. An array reference also works, but a simple list does not. The constructor returns a Java object (at least, it looks that way to us); the other calls just provide additional examples. Note, in particular, that setGreeting
expects an int
and a String
. Inline::Java
examines the arguments and coerces them into the best types it can. This nearly always works as expected. When it doesn’t, you need to look in the documentation for “CASTING.”
Finally, this is Hi3.pm
(behold the power of Perl and the work of the Inline
developers):
package Hi3;
use strict; use warnings;
BEGIN {
$ENV{CLASSPATH} .= ":/home/phil/jar_home/higher.jar";
}
use Inline Java => 'STUDY',
STUDY => ['Higher'];
sub new {
my $class = shift;
return Hi3::Higher->new(@_);
}
1;
To use a class hidden in a .jar I need to do three things:
- Make sure an absolute path to the .jar file is in the
CLASSPATH
, before usingInline
. A well-placedBEGIN
block makes this happen. - Use
STUDY
instead of providing Java source code. - Add the
STUDY
directive to theuse Inline
statement. This tellsInline::Java
to look for named classes. In this case, the list has only one element:Higher
. Names in this list must be fully qualified if the corresponding class has a Java package.
The constructor just calls the Higher
constructor through Inline::Java
, as we have seen before.
Yes, this is the whole module, all 15 lines of it.
If you need an adapter between your caller and the Java library, you can put it in either Perl or Java code. I prefer to code such adapters in Perl when possible, following the plan we saw in the previous section. Yet occasionally, that is too painful, and I resort to Java. For example, the glue module Java::Build::JVM
uses both a Java and a Perl adapter to ease communication with the genuine javac
compiler. Look at the Java::Build
distribution from CPAN for details.
Anatomy of Automated Compiling: A Brief Discussion
So what is Inline::Java
doing for us? When it finds our Java code, it makes a copy in the .java file of the proper name (javac
is adamant that class names and file names match). Then it uses our Java compiler to build a compiled version of the program. It puts that version in a directory, using an MD5 sum to ensure that recompiling happens when and only when the code changes.
You can cruise through the directories looking at what it did. If something goes wrong, it will even give you hints about where to look. Here’s a tour of some of those directories. First, there is a base directory. If you don’t do anything special, it will be called _Inline, under the working directory from which you launched the program. If you have a .Inline directory in your home directory, all Inline
modules will use it. If you use the DIRECTORY
directive in your use Inline
statement, its value will be used instead. For ease of discussion, I’ll call the directory _Inline.
Under _Inline is a config file that describes the various Inline
languages available to you. More importantly, there are two subdirectories: build and lib. If your code compiles, the build directory will be cleaned. (That’s the default behavior; you can include directives in your use Inline
statement to control this.) If not, the build directory has a subdirectory for your program, with part of the MD5 sum in its name. That directory will hold the code in its .java file and the error output from javac in cmd.out.
Code that successfully compiles ends up in lib/auto. The actual .class files end up in a subdirectory, which is again named by class and MD5 sum. Typically, there will be three files there. The .class file is as normal. The other files describe the class. The .inl file has an Inline
description of the class. It contains the full MD5 sum, so code does not need to be recompiled unless it changes. It also says when the code was compiled, along with a lot of other information about the Inline::Java
currently installed. The .jdat file is specific to Inline::Java
. It lists the signatures of the methods available in the class. Inline::Java
finds these using Java’s reflection system (reflection is the Java term for symbolic references).
See Also
For more information on Inline
and Inline::Java
and the other inline modules, see their perldoc. If you want to join in, sign up for the inline@perl.org mailing list, which is archived at nntp.x.perl.org/group/perl.inline.
Acknowledgements
Thanks to Brian Ingerson for Inline
and Patrick LeBoutillier for Inline::Java
. These excellent modules have saved me much time and heartache. In fact, I doubt I would have had the courage to use Java in Perl without them. Double thanks to Patrick LeBoutillier, since he took the time to read this article and correct some errors (including my failure to put “Bonjour” in the greetings list).
Tags
Feedback
Something wrong with this article? Help us out by opening an issue or pull request on GitHub