views:

323

answers:

6

Hi, I have a python application which I cant edit its a black box from my point of view. The python application knows how to process text and return processed text. I have another application written in Java which knows how to collect non processed texts.

Current state, the python app works in batch mode every x minutes.

I want to make the python

processing part of the process: Java app collects text and request the python app to process and return processed text as part of a flow.

What do you think is the simplest solution for this?

Thanks, Rod

+5  A: 

Look into Jython - you can run Python programs directly from Java code, and interact seamlessly back and forth.

Steven Schlansker
The python app is a black box for me. I cant edit it.
Rod
You should be able to make Jython work even without changing your python code.
Steven Schlansker
I want to treat the Python as a black closed box. More as a server. How Python well python work with sockets?
Rod
@Rod: Jython is python running on JVM, and it's still a "black box". It's just that Java can call it without using sockets and all that. And since you're using Java, this is the best way.
Tor Valamo
Do I have to embed the Python app code in my the Java app? Or is it can be used as an intermediate technology?
Rod
Jython will not work if the Python code relies upon any C extensions (for example numpy). Additionally, not all Python features exist in Jython, leading to bugs such as this one (http://bugs.jython.org/issue1398) in NLTK. It would not hurt to try running on Jython, but I would personally use one of the solutions that launches a separate process.
John Paulett
A: 

Expose one of the two as a service of some kind, web service maybe. Another option is to port the python code to Jython

Perpetualcoder
A: 

One possible solution is jpype. This allows you to launch a JVM from Python and pass data back and forth between them.

Another solution may be to write the Python program as a filter (reading data from stdin and writing result to stdout) then run it as a pipe. However I do not know how well Java supports this - according to the Sun docs their concept of pipes only supports communication between threads on the same JVM.

Dave Kirby
+3  A: 

I don't know nothing about Jython and the like. I guess it's the best solution if you can execute two programs without executing a new process each time the Java app needs to transform text. Anyway a simple proof of concept is to execute a separate process from the Java App to make it work. Next you can enhance the execution with all that tools.

Executing a separate process from Java

String[] envprops = new String[] {"PROP1=VAL1", "PROP2=VAL2" };
Process pythonProc = Runtime.getRuntime().exec(
   "the command to execute the python app", 
    envprops, 
    new File("/workingdirectory"));

// get an outputstream to write into the standard input of python
OutputStream toPython = pythonProc.getOutputStream();

// get an inputstream to read from the standard output of python
InputStream fromPython = pythonProc.getInputStream();

// send something
toPython.write(.....);
// receive something
fromPython.read(....);

Important: chars are NOT bytes

A lot of people understimate this.

Be careful with char to byte conversions (remember Writers/Readers are for chars, Input/OutputStreams are for bytes, encoding is necesary for convertir one to another, you can use OuputStreamWriter to convert string to bytes and send, InputStreamReader to convert bytes to chars and read them).

helios
Of course it means the python app must read from the stdin and write to the stdout. Good luck!
helios
Wont it just return the exit code of the python process? If not, what would be considered as available from the inputStream?
Rod
Nop. From the invoked (python) process you have an STDIN and a STDOUT (and a STDERR also :). Java can hook them, write in one and read the other. When you do printf("...") (sorry, I don't know about Python) you are writing to stdout. When you do something like "dir | more" in the Windows command line or "ls | nl" in Linux you are redirecting STDOUT of the first program to the STDIN of the second. If you want the return code of the process you have to call pythonProc.waitFor(). That returns an int.
helios
A: 

An option is making the python application work as a server, listens for request via sockets (TCP).

Rod
+2  A: 

Use ProcessBuilder to execute your Python code as a filter:

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class PBTest {

    public static void main(String[] args) {
        ProcessBuilder pb = new ProcessBuilder("python", "-c", "print 42");
        pb.redirectErrorStream(true);
        try {
            Process p = pb.start();
            String s;
            BufferedReader stdout = new BufferedReader (
                new InputStreamReader(p.getInputStream()));
            while ((s = stdout.readLine()) != null) {
                System.out.println(s);
            }
            System.out.println("Exit value: " + p.waitFor());
            p.getInputStream().close();
            p.getOutputStream().close();
            p.getErrorStream().close();
         } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
trashgod