Py4J: Python to Java Bridge

0.10.9.9 · active · verified Sat Mar 28

Py4J enables Python programs to dynamically access Java objects in a Java Virtual Machine (JVM). It facilitates method calls and allows access to Java collections as if they were native Python objects. Py4J also supports callbacks, enabling Java programs to call Python objects. The current version is 0.10.9.9, and it maintains a consistent release cadence with a strong focus on backward compatibility.

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to connect a Python client to a Java Gateway Server, access an entry point object, and call Java methods. It includes a simplified Python-driven setup for the Java server (which usually runs independently). The example performs basic addition via a custom Java class and interacts with a standard Java `ArrayList`. Users *must* replace `/path/to/py4j<version>.jar` with the actual path to their Py4J JAR file for the Java compilation and execution to succeed.

import os
from py4j.java_gateway import JavaGateway, GatewayParameters
import subprocess
import time

# --- Java Gateway Server Setup (for demonstration) ---
# In a real application, the Java Gateway Server would be started separately.
# For this example, we'll try to start a minimal one programmatically.
# This assumes 'py4j<version>.jar' is in the classpath. For simplicity, 
# we'll use a placeholder for py4j.jar path. Users need to replace it.
# Usually, py4j.jar is found in your Python environment's site-packages/py4j/ directory.

# NOTE: Replace '/path/to/py4j<version>.jar' with the actual path to your Py4J JAR file.
# You can find it by running: `python -c "import py4j; import os; print(os.path.join(os.path.dirname(py4j.__file__), 'py4j0.10.9.9.jar'))"`
PY4J_JAR_PATH = os.environ.get('PY4J_JAR_PATH', '/path/to/py4j0.10.9.9.jar') # Update this path!

java_code = """
import py4j.GatewayServer;

public class AdditionApplication {
    public int addition(int first, int second) {
        return first + second;
    }

    public static void main(String[] args) {
        AdditionApplication app = new AdditionApplication();
        GatewayServer server = new GatewayServer(app, 25333);
        server.start();
        System.out.println("Gateway Server Started on port 25333");
    }
}
"""

# Attempt to compile and run the Java code for quickstart demo
# This is a simplification and might require manual setup for complex environments.
# In a production setting, the Java server would be a separate, long-running process.

# Create temporary files for Java code and compiled class
java_file_name = "AdditionApplication.java"
class_file_name = "AdditionApplication.class"

with open(java_file_name, "w") as f:
    f.write(java_code)

try:
    # Compile Java code (requires javac in PATH)
    compile_process = subprocess.run(
        ["javac", "-cp", PY4J_JAR_PATH, java_file_name],
        capture_output=True, text=True
    )
    if compile_process.returncode != 0:
        print("Java compilation failed:", compile_process.stderr)
        print("Please ensure javac is in your PATH and PY4J_JAR_PATH is correct.")
        # Fallback for systems without javac or if compilation fails: manual start instruction
        print("Alternatively, you can manually compile and run the Java server:")
        print(f"1. Save the Java code above as {java_file_name}")
        print(f"2. Compile: javac -cp {PY4J_JAR_PATH} {java_file_name}")
        print(f"3. Run: java -cp {PY4J_JAR_PATH}:. AdditionApplication")
        # If on Windows, use ';' instead of ':' for classpath separator in commands
        raise RuntimeError("Java compilation failed, cannot run quickstart.")

    # Run Java Gateway Server in a separate process
    # Using 'nohup' and '&' for background on *NIX, or 'start' on Windows
    if os.name == 'nt': # Windows
        java_server_command = ["java", "-cp", f".;{PY4J_JAR_PATH}", "AdditionApplication"]
        java_process = subprocess.Popen(java_server_command, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
    else: # Unix-like
        java_server_command = ["java", "-cp", f":{PY4J_JAR_PATH}", "AdditionApplication"]
        java_process = subprocess.Popen(java_server_command, preexec_fn=os.setsid)

    print("Starting Java Gateway Server...")
    time.sleep(5) # Give Java server time to start

    # --- Python Client --- 
    gateway = JavaGateway(gateway_parameters=GatewayParameters(port=25333))

    # Access the Java entry point (AdditionApplication instance)
    addition_app = gateway.entry_point

    # Call a Java method
    number1 = 5
    number2 = 3
    result = addition_app.addition(number1, number2)
    print(f"Python calling Java: {number1} + {number2} = {result}")

    # Access JVM directly for standard Java objects
    java_list = gateway.jvm.java.util.ArrayList()
    java_list.add("Hello from Python!")
    print(f"Java list created and modified from Python: {java_list}")

except Exception as e:
    print(f"An error occurred during quickstart: {e}")
finally:
    # Clean up Java process if it was started
    if 'java_process' in locals() and java_process.poll() is None:
        print("Shutting down Java Gateway Server...")
        if os.name == 'nt':
            subprocess.run(["taskkill", "/F", "/T", "/PID", str(java_process.pid)], capture_output=True)
        else:
            os.killpg(os.getpgid(java_process.pid), 15) # Send SIGTERM
        java_process.wait(timeout=5)
    
    # Clean up temporary files
    os.remove(java_file_name) if os.path.exists(java_file_name) else None
    os.remove(class_file_name) if os.path.exists(class_file_name) else None
    # Also remove AdditionApplication$*.class files if they exist
    for f in os.listdir('.'):
        if f.startswith('AdditionApplication$') and f.endswith('.class'):
            os.remove(f)

view raw JSON →