Question

I have a python program, which runs on the CPython implementation, and inside it I must call a function defined in a java program. How can I do this?

It would be nice to be able to use some java objects too.

Jython is not an option. I must run the python part in CPython.

Was it helpful?

Solution

The easiest thing to do is

  1. Write a trivial CLI for your java "function". (There's no such thing, so I'll assume you actually mean a method function of a Java class.)

    public class ExposeAMethod {
        public static void main( String args[] ) {
             TheClassToExpose  x = new TheClassToExpose();
            x.theFunction();
        }
    }
    
  2. Compile and build an executable JAR file with this as the entry point. Call it ExposeAMethod.jar

  3. Call this from a command created by subprocess.

    import subprocess
    p = subprocess.Popen("java -jar ExposeAMethod.jar", shell=True)
    sts = os.waitpid(p.pid, 0)
    

This is the minimum. And it's really not much. I count 6 lines of java, 3 lines of Python and you're up and running.

If you want to pass arguments to this Java class constructor or method function, you'll have to write a few more lines of code. You have two choices.

  • Read the arguments from stdin, write the results on stdout. This is relatively easy and performs really well.

  • Parse the arguments as command-line options to Java, write the results on stdout. This is slightly harder, but generalizes very nicely. The bonus is that you now have a useful command-line Java program that you can reuse.

OTHER TIPS

Apologies for resurrecting the thread, but I think I have a better answer :-)

You could also use Py4J which has two parts: a library that runs in CPython (or any Python interpreter for that matter) and a library that runs on the Java VM you want to call.

There is an example on the frontpage and lots of documentation, but essentially, you just call Java methods from your python code as if they were python methods:

>>> from py4j.java_gateway import JavaGateway
>>> gateway = JavaGateway()                        # connect to the JVM
>>> java_object = gateway.jvm.mypackage.MyClass()  # invoke constructor
>>> other_object = java_object.doThat()
>>> other_object.doThis(1,'abc')
>>> gateway.jvm.java.lang.System.out.println('Hello World!') # call a static method

The communication is done through sockets instead of JNI.

Disclaimer: I am the author of Py4J

If you don't want to go the write your own JNI/C route.

The other option is to use jpype which for me is always what I use to access Oracle databases becuase installing the oracle c drivers on a PC is a pita. You can do stuff like (from docs):

 from jpype import * 
 startJVM("d:/tools/j2sdk/jre/bin/client/jvm.dll", "-ea") # or path to your jvm
 java.lang.System.out.println("hello world") 
 shutdownJVM()

It hasn't been updated in a while and there isn't much in the way of documentation but it does work reasonably well.

You'll have to create a python C extension that embeds Java - based on something like http://www.javaworld.com/javaworld/jw-05-2001/jw-0511-legacy.html or else you'll have to start java in a separate subprocess.

Look at our project python-javabridge. It's a Python wrapper around the JNI, heavily used by CellProfiler. It offers both low-level access to the JNI and a high-level reflection-based access to Java objects.

I don't know for Python, but last time I had to call java from C application (NT service) I had to load jvm.dll. Take a look at JNI documentation.

Also, you call always call

os.system("java com.myapp.MyClass") 

if you are not concerned about performance.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top