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:
1. Make sure an absolute path to the .jar file is in the CLASSPATH, before using Inline. A well-placed BEGIN block makes this happen.
2. Use STUDY instead of providing Java source code.
3. Add the STUDY directive to the use Inline statement. This tells Inline::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
[email protected] 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).