5

I'm pretty new to Perl but have been programming in java for several months now (coming from a C++ background). I wrote a Perl script that parses some data logs and now the customer I'm working for wants a GUI. The GUI has been created as a java applet (using Netbeans) and I would like to "embed" the perl script inside its jar file as a default safety feature. Multiple updates are expected for the perl script later in the future, so I want to set it up so that all the user has to do when an update comes along is define a new file path to the latest perl script through the GUI. I've already implemented this functionality with a file browser and everything works fine.

The problem I'm running into is something very simple that's probably not very hard for someone with more java experience. Just in case one of the updated perl scripts they receive in the future doesn't work properly, I want them to be able to use the default "embedded" script if they have to resort to that. When I'm running the applet through Netbeans, everything works perfectly however when I try and run the jar file from the command line, the program returns an error saying it cannot find the file. I might not be using the correct terminology to search for a solution to this problem, but I would like to be able to have my jar file execute the embedded perl script at runtime. Any suggestions are appreciated. I've tried placing the perl file in the same package as the java files and calling for the script by its filename alone, but that was a no go.

uɐɪ
  • 2,540
  • 1
  • 20
  • 23
Josh Bradley
  • 4,630
  • 13
  • 54
  • 79

2 Answers2

3

You can access any file in the jar as a classpath resource, but the problem you're going to have is users may not have a perl interpreter installed.

EDIT: Since you've mentioned that users will have a Perl runtime, then this is doable. You can try piping the contents of the file using Process.getOutputStream() or just copy the contents to a temp file with File.createTempFile() and pass that file name as an argument to the perl interpreter.

jiggy
  • 3,828
  • 1
  • 25
  • 40
  • Ok, thanks for the information! Perl is installed by default on all the machines the customer is using, so there should be no problem there. – Josh Bradley Jul 27 '11 at 14:44
  • I see in the javadocs how to use the classpath resource to read in a file as an InputStream, but how would I get from there to passing in the file path to Runtime.exec()? – Josh Bradley Jul 27 '11 at 15:29
  • You can't pass a path to a file that's inside a jar. I edited my answer with some other possibilities. – jiggy Jul 27 '11 at 15:43
  • I had come to the same conclusion about copying to a temp file too. Wouldn't it be nice to be able to run a script within a jar like that though or what kind of bad implications would that method present? – Josh Bradley Jul 27 '11 at 16:46
  • You can try the output stream method although I can't vouch for it. Java actually comes with a `ScriptEngine` that can run scripts directly in the JVM, but it requires a suitable interpreter and Perl is the biggest language that doesn't have one. JavaScript, Groovy, Python, Ruby and some others are supported, but not Perl :( – jiggy Jul 27 '11 at 19:22
1

I have the same problem, here's how I solved it based on Josh and Jiggy's discussion above. First look for the file in src/main/resources/perl (so it works in Eclipse). If it does not exist then copy the Perl file from the perl directory inside the jar to src/main/resources/perl. I building with Maven so using the src/main/resources/perl directory means when I build the jar, Maven automatically includes the perl directory in the jar.

This is a similar strategy to the one used to load resources from jars such as properties files.

I am using this approach because I have a multi-module Maven project when each submodule builds a jar. We have one that does general information extraction, then another one that specializes that module for a particular client. The Perl code lives inside the general module, but it is needed in the specialized one. Copying files between modules in Maven is rather awkward, so it is easier just to put it in resources, then let the Java code solve the problem.

See this related question for a good answer of an alternative approach to embedding native code such as C in jars.

The code looks like this (I'm using Apache Commons IO):

public class PerlTableParser {

  private static final String RESOURCES_DIR = "src/main/resources";
  private static final String LIB_PATH = RESOURCES_DIR + "perl/";
  private static final String PERL_PARSER = "perl/parser.pl";
  private static final String PERL_CMD = String.format("perl -I %s %s",
        LIB_PATH, RESOURCES_DIR + PERL_PARSER);

  public PerlTableParser() {
    File perlCodeDir = new File(LIB_PATH);
    if (!perlCodeDir.exists()) {
        perlCodeDir.mkdirs();
    }
    File perlParserFile = new File(RESOURCES_DIR, PERL_PARSER);
    try {
        if (!perlParserFile.exists()) {
            FileUtils.copyInputStreamToFile(getClass().getClassLoader()
                    .getResourceAsStream(PERL_PARSER), perlParserFile);
        }
    } catch (IOException e) {
        MyLogger.logger.error(
                "Failed to copy Perl code to local directory " + e, e);
    }
}
Community
  • 1
  • 1
Mark Butler
  • 4,361
  • 2
  • 39
  • 39