I wrote a Python program that consists out of five .py script files.
I want to execute the main of those python scripts from within a Java Application.
What are my options to do so? Using the PythonInterpreter doesn't work, as for example the datetime module can't be loaded from Jython (and I don't want the user to determine his Python path for those dependencies to work).
I compiled the whole folder to .class files using Jython's compileall. Can I embed these .class files somehow to execute the main file from within my Java Application, or how should I proceed?
Have a look at the ProcessBuilder class in java: https://docs.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html.
The command used in the java constructor should be the same as what you would type in a command line. For example:
Process p = new ProcessBuilder("python", "myScript.py", "firstargument").start();
(the process builder does the same thing as the python subprocess module).
Have a look at running scripts through processbuilder
N.B. as for the Jython part of the question, if you go to the jython website (have a look at the FAQ section of their website www.jython.org). Check the entry "use jython from java".
I'm also interested in running Python code directly within Java, using Jython, and avoiding the need for an installed Python interpreter.
The article, 'Embedding Jython in Java Applications' explains how to reference an external *.py Python script, and pass it argument parameters, no installed Python interpreter necessary:
#pymodule.py - make this file accessible to your Java code
def square(value):
return value*value
This function can then be executed either by creating a string that
executes it, or by retrieving a pointer to the function and calling
its call method with the correct parameters:
//Java code implementing Jython and calling pymodule.py
import org.python.util.PythonInterpreter;
import org.python.core.*;
public class ImportExample {
public static void main(String [] args) throws PyException
{
PythonInterpreter pi = new PythonInterpreter();
pi.exec("from pymodule import square");
pi.set("integer", new PyInteger(42));
pi.exec("result = square(integer)");
pi.exec("print(result)");
PyInteger result = (PyInteger)pi.get("result");
System.out.println("result: "+ result.asInt());
PyFunction pf = (PyFunction)pi.get("square");
System.out.println(pf.__call__(new PyInteger(5)));
}
}
Jython's Maven/Gradle/etc dependency strings can be found at http://mvnrepository.com/artifact/org.python/jython-standalone/2.7.1
Jython JavaDoc
It is possible to load the other modules. You just need to specify the python path where your custom modules can be found. See the following test case and I am using the Python datatime/math modules inside my calling function (my_maths()) and I have multiple python files in the python.path which are imported by the main.py
#Test
public void testJython() {
Properties properties = System.getProperties();
properties.put("python.path", ".\\src\\test\\resources");
PythonInterpreter.initialize(System.getProperties(), properties, new String[0]);
PythonInterpreter interpreter = new PythonInterpreter();
interpreter.execfile(".\\src\\test\\resources\\main.py");
interpreter.set("id", 150); //set variable value
interpreter.exec("val = my_maths(id)"); //the calling function in main.py
Integer returnVal = (Integer) interpreter.eval("val").__tojava__(Integer.class);
System.out.println("return from python: " + returnVal);
}
Related
So I just received a task for creating a Java Shell App, without using any 3rd party libraries, and without using Runtime.exec() or ProcessBuilder APIs.
I don't want the solution (obviously I want to do this myself) but I do need a hint how to do this? I want the app to open a shell prompt which will accept various commands with usage of JDK 8 (Nashorn?).
Thanks!
Not really clear what you want to achieve. If you want to run a Nashhorn shell you can achieve it like this (Java 8)
import jdk.nashorn.tools.Shell;
public class NashornShell {
public static void main(String[] args) {
Shell.main(new String[]{ "-scripting"});
}
}
When you see the Nashorn prompt jjs> you can execute Linux commands...
jjs> $EXEC("ls");
which will list the current directory (using the Linux ls command).
... or execute Java commands ...
jjs> java.lang.System.out.println("foo");
... or execute JavaScript commands ...
jjs> print("foo");
For more information have a look in the nashorn guide.
edit If you want to pass only yourCommand as parameter.
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine engine = factory.getScriptEngine(new String[]{"-scripting"});
String yourCommand = "ls";
Object eval = engine.eval("$EXEC(\"" + yourCommand + "\")");
System.out.println(eval);
edit do you think that instead of Nashorn I could just use raw streams directed to the OS from JVM
Following is possible
Commands.java
class Commands {
public static void main(String[] args) {
System.out.println("ls");
System.out.println("whoami");
}
}
run.sh
#!/bin/sh
java Commands | while read command; do
echo
echo "command: $command"
$command
done
But obviously this is not to recommend when you want to execute the output of Commands:
your Java application has no control about the return state of the executed single commands
if one command wait for user input your Java application don't know it
your Java application has no access to the output produced by the commands
all commands are blindly exected
and some more downsides
Regardless of what you're trying to do, using nashorn internal class like jdk.nashorn.tools.Shell is not a good idea. With java 9, this package is not an exported package of jdk.scripting.nashorn module. So, with java 9, you'll get package access failure (even in the absence of security manager - as module read/export access check is more like member access check for classes).
In essence I have this program:
from sympy.solvers import solve
from sympy import Symbol
x = Symbol('x')
print solve(x**2 - 1, x)
And I call this from Java using this code:
public static BufferedReader runFile(Class<?> c, String py, List<String> args) {
String cmd = c.getResource(py).getPath();
cmd=cmd.split(py)[0];
cmd=cmd.substring(0,cmd.length()-1);
args.add(0, py);
args.add(0, "python");
final ProcessBuilder pb = new ProcessBuilder(args);
new ProcessBuilder();
pb.directory(new File(cmd));
pb.redirectError();
try {
System.out.println(pb.directory().getPath());
for(String s:pb.command()){
System.out.println(s);
}
Process p=pb.start();
return new BufferedReader(new InputStreamReader(p.getErrorStream()));
}
catch (final IOException e) {
throw new RuntimeException(e);
}
}
When I run the Python program from a terminal everything works as intended, with nothing in the error stream, and it prints [-1,1]. But if I run it from the program, I get this in the error stream:
Traceback (most recent call last):
File "solve.py", line 1, in <module>
from sympy.solvers import solve
ImportError: No module named sympy.solvers
Since specifying the full path of Python fixes your problem, you most likely have multiple installations of Python on your system. Rather than PYTHONPATH being different, I suspect it is actually PATH that is different. As a result, your command line uses the Python interpreter you intend, while Java uses another one.
To determine where this alternate install is, which -a python may be useful, but if not, examine PATH from inside your Java code and see if you can find Python in one of those directories.
Regardless, if you really need to specify the full Python path in Java, you should make this a configuration option. It will probably be different on different machines. Storing it in a file seems most prudent.
Your PYTHONPATH (or less likely your working directory) is different when running from your Java context.
You can
import sys
print sys.path
which may help you to ensure the path is the same for both.
Telling us more about how your environment is set up will help to get more specific answers.
eg. Maybe the Java is running via a web server or something?
Here are couple of ways to fix the path problem:
Make sure the directory containing sympy is in your PYTHONPATH environment variable
If you're really desperate, append the correct directory to sys.path
import sys
sys.append("/some/dir/with/sympy")
from sympy.solvers import solve
...
I installed Groovy.
And I am trying to run groovy scripts from a command prompt that I created using Java, like so:
Runtime.getRuntime().exec("groovy");
So if I type in "groovy" to the command line, this is what I get:
>>>groovy
Cannot run program "groovy": CreateProcess error=2, The system cannot find the file specified
Does anyone have an idea as to what might be going wrong? Should I just use Groovy's implementation of exec? Like:
def processBuilder=new ProcessBuilder("ls")
processBuilder.redirectErrorStream(true)
processBuilder.directory(new File("Your Working dir")) // <--
def process = processBuilder.start()
My guess is that it wouldn't matter whether using Java's implementation or Groovy's implementation.
So how do I run a groovy script?
The way originally described in the question above calling the groovy executable invokes a second Java runtime instance and class loader while the efficient way is to embed the Groovy script directly into the Java runtime as a Java class and invoke it.
Here are three ways to execute a Groovy script from Java:
1) Simplest way is using GroovyShell:
Here is an example Java main program and target Groovy script to invoke:
== TestShell.java ==
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
// call groovy expressions from Java code
Binding binding = new Binding();
binding.setVariable("input", "world");
GroovyShell shell = new GroovyShell(binding);
Object retVal = shell.evaluate(new File("hello.groovy"));
// prints "hello world"
System.out.println("x=" + binding.getVariable("x")); // 123
System.out.println("return=" + retVal); // okay
== hello.groovy ==
println "Hello $input"
x = 123 // script-scoped variables are available via the GroovyShell
return "ok"
2) Next is to use GroovyClassLoader to parse the script into a class then create an instance of it. This approach treats the Groovy script as a class and invokes methods on it as on any Java class.
GroovyClassLoader gcl = new GroovyClassLoader();
Class clazz = gcl.parseClass(new File("hello.groovy");
Object aScript = clazz.newInstance();
// probably cast the object to an interface and invoke methods on it
3) Finally, you can create GroovyScriptEngine and pass in objects as variables using binding. This runs the Groovy script as a script and passes in input using binding variables as opposed to calling explicit methods with arguments.
Note: This third option is for developers who want to embed groovy scripts into a server and have them reloaded on modification.
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
String[] roots = new String[] { "/my/groovy/script/path" };
GroovyScriptEngine gse = new GroovyScriptEngine(roots);
Binding binding = new Binding();
binding.setVariable("input", "world");
gse.run("hello.groovy", binding);
System.out.println(binding.getVariable("output"));
Note: You must include the groovy_all jar in your CLASSPATH for these approaches to work.
Reference: http://groovy.codehaus.org/Embedding+Groovy
I have a TCL file which uses Tcl's BWidget package that I've been using as a GUI for my program. I now want to be able to load up this GUI from a separate Java program. I've looked into Jacl and Swank, but they don't seem to do exactly what I want.
I've tried the following with Jacl but it's unable to evaluate the file. While debugging, I can see that it completes parsing my tcl file, but it throws an exception while parsing through the BWidget package tcl files. Here's my Java code:
Interp interp = new Interp();
try {
interp.evalFile("C:\\CTP\\Tcl\\LuxonCtp32.tcl");
} catch (TclException ex) {
int code = ex.getCompletionCode();
System.err.println("command returned bad error code: " + code);
} finally {
interp.dispose();
}
Any ideas on how I can accomplish what I want to do? Is it even possible?
Tcl itself can not display a GUI. It uses a plugin called Tk for that.
In the C reference implementation of Tcl you get Tk as well.
Tk has not been ported to Java, Tcl has.
You can not use Jacl to display Tk widgets, but TclBlend could do that, because TclBlend uses the C reference implementation of Tcl. That means that the user needs a working Tcl/Tk installation.
There are some problems with TclBlend and Tcl > 8.5 through, which result in a segfault.
IIRC you have to remove the conditional if around Tcl_FindNameOfExecutable in TclBlends C code (and compile it yourself).
Go to this site http://jtcl-project.github.io/jtcl/ and download now for the binary zip. Its a recent java tcl on github called Jtcl.
Unzip it and you will find a jar called jtcl-2.7.0.jar.
I am using Netbeans 8 my preference.
I add the jar into Project Library.
I create a java file called JTclHallo.java and this is the code.
package jtclhallo;
// import tcl.lang it belongs to jtcl-2.7.0 jar a must
import tcl.lang.*;
// Java wrapper to test JACL or JTCL.
public class JTclHallo {
public static void main(String []args) {
//Interp is a java class belonging to tcl.lang. Unrar the jtcl-2.7.0
Interp i = new Interp();
try {
//call your tcl file mine was swing.tcl from the E drive
i.eval("source E:/private/swing.tcl");
} catch (TclException e) {
System.out.println("Exception: " + e.getMessage());
}
}
}
For swing.tcl
package require java
set window [java::new javax.swing.JFrame]
$window setSize 600 400
$window setVisible true
I have a java library in jar form which can be used to extract data from files(excel,xml etc). As its in java, it can be used only in java applications. But i need the same library to be used for python projects as well.
I have tried py4j etc which takes the objects from jvm. But the library is not an executable and wont be 'run'. I have checked Jython but i need the library to be accessible from Python projects.
I have thought about using automated java to python translators, but i would take that as the last resort.
Please suggest some way i can accomplish this.
You can make a one class java program with a thread never ending until you send from Python a notification to do so.
This way the lib would be kept in memory and accessible from your Python program.
This class could be like this (add your needed lib import/init) :
public class TestPy {
private Thread thread;
public void die() {
synchronized (thread) {
thread.interrupt();
}
}
public TestPy() {
thread = new Thread(){
public void run() {
try {
while (!Thread.interrupted()) {
Thread.sleep(500);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
};
thread.start();
}
public static void main(String[] args) {
TestPy tp = new TestPy();
GatewayServer server = new GatewayServer(tp);
server.start();
}
}
You would have to launch the java program, use the lib, and then use the die() method to kill the java program in Python :
gateway = JavaGateway()
do your stuff here using the lib
tp = gateway.entry_point
tp.die()
You can write a simple command line Java program which calls the library and saves the results in a format you can read in Python, then you can call the program from Python using os.system.
Another option is to find Python libraries with equivalent functionality to the Java library: you can read excel, xml and other files in Python, that's not a problem.
I haven't learned how to create new instances of java class in a jar file by java constructors, but accidentally found that it's very easy to use any java static methods to access java objects in py2j.
step 1: download py4j zip file from https://pypi.python.org/pypi/py4j. "py4j0.10.0.jar" is in the zip file.
step 2: install py4j by
pip install 'D:\Downloads\py4j-0.10.0.zip'
step 3: add py4j0.10.0.jar as well as the_lib_you_use.jar (like owlapi-distribution-3.5.0.jar for the example below) to build path in your eclipse project
step 4: create AdditionApplication.java, and copy and paste the code of AdditionApplication.java at https://www.py4j.org/, and run Java application AdditionApplication.java
step 5: after running AdditoinApplication.java, test the example code in a python file:
if __name__ == '__main__':
pass
from py4j.java_gateway import JavaGateway
gateway = JavaGateway() # connect to the JVM
random = gateway.jvm.java.util.Random() # create a java.util.Random instance
number1 = random.nextInt(10) # call the Random.nextInt method
number2 = random.nextInt(10)
print(number1,number2)
(2, 7)
addition_app = gateway.entry_point # get the AdditionApplication instance
addition_app.addition(number1,number2) # call the addition method
Math = gateway.jvm.java.lang.Math
a = Math.max(4, 6);
print a
IRI = gateway.jvm.org.semanticweb.owlapi.model.IRI
abcIRI = IRI.create('fewf#fe')
print 'abcIRi = ' + str(abcIRI)
print 'abcIRI.getFragment() = ' + abcIRI.getFragment()
The result on console is :
(5, 0)
6
abcIRi = fewf#fe
abcIRI.getFragment() = fe