Call Perl routines from Java

Use XML-RPC to call Perl routines from your Java code

Problem: You need to access a Perl program from your Java program—maybe a custom screen scraper written with LWP (Library for WWW access in Perl), maybe Bugzilla, or maybe one of countless possibilities. You don't want to rewrite the Perl code and would rather just call it somehow.

Solution: You can unite your families with XML-RPC (remote procedure call). Though SOAP is a mature technology often used to solve complex transactional problems, sometimes your problem is simple, and a light API like XML-RPC can allow you to quickly and easily connect formerly independent programs that were separated by language, platform, and location.

This technique is easy to implement and can save a ton of work in either rewriting the Perl routines or explaining to stakeholders why you can't add their functionality to the enterprise system until next quarter. So if you want to learn a get-it-done-now way to hook up Perl and Java, read on.

To make sense of the wrapper structure I describe in this article, we start with the Perl module and routines we want to call, wrap a facade around them, then connect the XML-RPC server to the facade. Next, we call the routine through the Java XML-RPC implementation.

The Perl side

The preexisting Perl module

We want to wire up to a Perl module that already exists. Here's the simplest example I can think of; it is named simply PreExistingMod.pm:

package PreExistingMod;
# import all the preexisting Perl Modules you would wish to call here
##### a routine with no arguments and a single String output #####
sub say_hello {
 return 'Hello from the PreExistingMod' ;
}
##### a Perl routine with an array of arguments #####
##### and a hash for output                     #####
sub echo_args {
my ($PassData) = @_;
      return { Status => 'Test', 
               EchoData => ${$PassData}{SessionID}, 
               FromIP => ${$PassData}{ClientIP}  };
};
###############################################
1;  # Perl Modules require this     

PreExistingMod has two routines say_hello and echo_args. Their output is self-explanatory.

Façade module for PreExistingMod

When you are integrating two systems, you'll find it's better to gather calls into a façade. The simpler the subsystem's view, the better; plus this structure also features security benefits. Though this intermediate module isn't necessary and you can call straight through, placing a façade between the XML-RPC server and PreExistingMod requires little effort and offers great advantages. This structure allows us to protect routines in modules too sensitive to be available over the wire, check authorization if we wish, and add routines from other modules later.

Here's our PerlSystemFacade.pm module:

package PerlSystemFacade;
# import all the pre-existing/legacy Perl Modules you would wish to call here
###############################################
##### Simple methods to test the plumbing #####
###############################################
sub say_hello_psf {
      my $retVal;
      # just return a string
      $retVal = ' hello from PerlSystemFacade.say_hello_psf';
      return $retVal;   
}
sub echo_args_psf {
my ($Refer, $PassData) = @_;
      #create some output with the input
      return {Status => 'Test', EchoData => ${$PassData}{SessionID}, FromIP => ${$PassData}{ClientIP}};
};
################################################
# Facade Methods that use the existing system to produce their result #
################################################
sub say_hello {
      my $retVal;
      # delegate to the legacy method
      use PreExistingMod;
      $retVal = PreExistingMod::say_hello();
      return $retVal; 
}
sub echo_args {
      my ($Refer, $PassData) = @_;
      my $retVal;
      # delegate to the legacy method
      use PreExistingMod;
      $retVal = PreExistingMod::echo_args( ($PassData) );
      return $retVal;   
}
1;  # Perl Modules require this

The routines say_hello_psf and echo_args_psf allow you to test server connectivity. The routines say_hello and echo_args delegate to PreExistingMod.

Notice that PreExistingMod::echo_args takes ($Passdata) = @_;, whereas the first time the façade assigns @_ in PerlSystemFacade::echo_args, it assigns it to ($Refer, Passdata). The Perl XML-RPC library requires this hack to pad the input's first array element. We just throw $Refer away and don't use it thereafter.

Perl XML-RPC CGI script for service to PerlSystemFacade

The Perl XML-RPC server is quite simple. We need to install XMLRPC::Lite on our Web server and load the server.pl file:

use XMLRPC::Transport::HTTP;
my $facadeserver = XMLRPC::Transport::HTTP::CGI
      -> dispatch_to('PerlSystemFacade')
      -> handle;

In simple terms, the XMLRPC library takes the posted input, parses it, stores the routine name being called, creates an argument array structure in Perl form, calls PerlSystemFacade::some_routine_name, and passes it the array, which becomes @_. It then packages the routine's output and puts it in the response.

Now you have all the server pieces in place. In terms of message flow, client XML-RPC calls now call your module methods along this sequence:

XML-RPC server sequence. Click on thumbnail to view full-sized image.

The Java side

The client example class

For this article's implementation, you need Apache XML-RPC. IBM donated this library, which was previously called Helma XML-RPC. It's only one jar file, so the configuration just requires you putting it on your classpath.

The client class that uses XML-RPC to call the server is straightforward:

package perlClientExample;
import org.apache.xmlrpc.XmlRpcClientLite;
import java.util.*;
public class XmlRpcClientExample {
    
    public XmlRpcClientExample() { }
    
    private static final String SERVER_URL = "http://192.168.1.101:8080/server.pl";
    
    public String say_hello() {
        String retVal = "NaN";
        try {
            XmlRpcClientLite xmlrpc = new XmlRpcClientLite(SERVER_URL);
            String methodName = "PerlSystemFacade.say_hello";
            Vector params = new Vector(0);
            retVal = (String) xmlrpc.execute( methodName, params );       
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return retVal;
    }
     
    public Object echo_args() {
        Object retVal = new Vector();
        try {
            XmlRpcClientLite xmlrpc = new XmlRpcClientLite(SERVER_URL);
            String methodName = "PerlSystemFacade.echo_args";
            Vector params = new Vector(2);
            Hashtable hashArg = new Hashtable();
            hashArg.put("SessionID",  "TestSessionIDValue");
            hashArg.put("ClientIP", "10.1.1.22");
            params.add( hashArg );
            
            retVal = xmlrpc.execute( methodName, params );      
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return retVal;
    }
    
    public static void main(String[] args) {
        XmlRpcClientExample  x  =  new XmlRpcClientExample();
        System.out.println ("Output from say_hello() : " +  x.say_hello().toString() );
        System.out.println ("Output from echo_args() : " +  x.echo_args().toString() );
    }
}

If you compile and run XmlRpcClientExample, it will return:

Output from say_hello() : Hello from thePreExistingMod
Output from echo_args() : {Status=Test, EchoData=TestSessionIDValue, FromIP=10.1.1.22}

If you are somewhat familiar with Perl, you know that you package an array for the routine arguments. Similarly, you do the same here. Look carefully at Vector params and PreExistingMod::say_hello, and you will see the correspondence. Basically, our client's params maps to the Perl routine's @_ array, while the client's hashArg Hashtable maps to $PassData. Remember that $Refer argument is just a hack to pad the first element of @_. The real payload, as far as Perl goes, is in the second $Passdata arg.

When handling the response, you need specific knowledge of the structure being returned. The return values are arrays, hashtables, and strings, which seem to be all the Perl XMLRPC::Lite will return at present. There are no nicely typed object attributes to walk through. If you need those attributes, move to SOAP. If an element of the returned array is expected to be another primitive type, like int or double, cast it to be sure from the string.

Apache's XML-RPC implementation does not require WSDL (Web Services Description Language), so it's easier to configure than SOAP or Sun's JAX-RPC (Java API for XML-Remote Procedure Call). The flip side is the absence of a method for clients to use to discover the correct signatures for calling your server's exposed methods. Regardless, we aren't looking to provide a universally available service here. For a project where we just want to wrap Perl routines and call them from Java, it remains a nice fit.

Some real-world issues

  • Perl scripts don't have throwable exceptions. Instead, most Perl programmers use die(...) statements to handle exceptions under the assumption that the script runs from the command line. The Perl XML-RPC server does not handle the fault correctly and just closes the socket. Luckily, the Apache XML-RPC client detects that the socket closed unexpectedly and throws an exception you can catch: org.apache.xmlrpc.XmlRpcException: Server returned an invalid fault response.
  • The server should be locked down to the client's IP addresses, which should be on their own network segment. SSL (Secure Socket Layer) isn't a bad idea either. A discussion on authentication and authorization schemes reaches beyond this article's scope, but using the Façade pattern will at least give you a central point for managing them.
  • Perl lacks named arguments for its functions. This is okay for a tight, single file program that is less than 1,000 lines of code, with one developer handling the code for its entire lifetime. But that's not an enterprise reality, and a large ambiguous API quickly becomes frustrating thereafter.
  • Perl doesn't give great feedback, so test all the façade methods locally with a Perl test harness before adding the extra plumbing. I put a simple test harness (assuming no framework) in the zip file that can be downloaded from Resources.
  • Threading on both the Java client and the Perl server is less than optimal. The asynchronous method seems to top out at about 70. The error is "connection refused," so it's most likely the Perl XML-RPC server I chose.
  • There is no standard way to control nested or distributed transactions, which should be simple atomic transactions. If you need that sort of thing, look to SOAP.

Conclusion

We started off with the problem of how to wire our Java application to call Perl functions. To prepare the Perl routines for calling, we added a façade, and then connected the façade to a Perl implementation of XML-RPC. We then called the routines from our Java application using a Java implementation of XML-RPC. The end result is access to those Perl routines.

This article focused on how XML-RPC can solve the problem of how to integrate two programs separated by language. XML-RPC also addresses the problem of physical separation. I've found it to be a handy and reliable tool. Its benefit is tactical, more than strategic, and thus is realized quickly. I hope it also helps you.

Richard Lawson graduated from Simon Fraser University in 1996 with bachelor's of science in mathematics. He started an ISP and, along the way, discovered a love of programming. After selling the ISP, he has since worked as a contract software developer with IPCubed Technology and has done interesting projects with Microsoft, Starbucks, and some major telecommunications companies. When he's not programming, he likes to spend time with his family and play hockey.

Learn more about this topic

Recommended
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more