I need to run a Python script (write input and read output) inside my Java application that will eventually be uploaded onto the web. How do I do this such that it is compatible with the web? I've tried things like Jython and Runtime.exec() in Java and I think both require Python to be installed on the computer (correct me if I'm wrong) but I want the app to be run by anyone on the web.
The Python script imports win32com.client to operate on a COM object. It reads in a .csv file, runs the external software, then writes a .csv file using the methods RCSV(...), Run(...) and WCSV(...). Instead of a .csv file, I would like this data to be accessed from my Java app directly. This is my python script in full for reference:
import win32com.client
from win32com.client import VARIANT
import csv
# This will import VT_VARIANT
import pythoncom
#dictionary function designed to read .csv file
def RCSV(address):
input=[]
csv_reader = csv.DictReader(open(address, 'r'), delimiter=',', quotechar='"')
headers = csv_reader.fieldnames
for line in csv_reader:
for i in range(len(csv_reader.fieldnames)):
input.append(line[csv_reader.fieldnames[i]])
InVal=[]
for i in range(int(len(input)/len(headers))):
InVal.append([])
for i in range(len(InVal)):
for j in range(i*len(headers), (i+1)*len(headers)):
InVal[i].append(input[j])
return InVal
#dictionary function which writes a .csv file given its address
def WCSV(address, output, headers):
with open(address, 'w') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=headers, lineterminator = '\n')
writer.writeheader()
for i in range(len(output[0])):
writer.writerow({headers[x]: output[x][i] for x in range(len(headers))})
def Run(InType,InDesc,InVal,OutType,OutDesc):
FieldArray = VARIANT(pythoncom.VT_VARIANT | pythoncom.VT_ARRAY, InDesc)
AllValueArray=[None]*len(InVal)
for i in range(len(InVal)):
AllValueArray[i]=VARIANT(pythoncom.VT_VARIANT | pythoncom.VT_ARRAY, InVal[i])
object.ChangeParametersMultipleElement(InType, FieldArray, AllValueArray)
object.RunScriptCommand("SolvePowerFlow")
OutVal = object.GetParametersMultipleElement(OutType, OutDesc,'')
return OutVal
# This will establish the connection
object = win32com.client.Dispatch("pwrworld.SimulatorAuto")
filename= r"C:\Users\janusz\Desktop\NTU microgrid topology\ICESO Scaledown microgrid.pwb"
object.OpenCase(filename)
# Reading inputs from a .csv
ADIN='IN.csv'
InVal = RCSV(ADIN)
InType = "GEN"
InDesc = ["BusNum", "GenID", "GenMW"]
OutType = "BUS"
OutDesc = ["BUSNUM", "BUSNAME", "BUSPUVOLT", "BUSANGLE", "BUSKVVOLT"]
OutVal = Run(InType,InDesc,InVal,OutType,OutDesc)
ADOUT='OUT.csv'
WCSV(ADOUT,OutVal[1],OutDesc)
#This will close the connection
del object
object = None
Jython works without Python being installed on the host, because it is a 100% Java implementation of Python. That being said, only win32 clients can run win32 COM anything. So, that's never going to be compatible across any platform but win32 (and possibly win64 through wow).
Related
Im working with Graal VM, using combined languages like Java and Python. I have a problem when try to execute Python sintax to read/create files using context.eval().
I use this code using Graalpython in terminal:
out_file = File.new("cadena.txt", "w+")
out_file.puts("write your stuff here")
out_file.close
and works, but when I tried to run a code to read the file in context.eval() with Java:
codigoPython += "fichw = open('cadena.txt','r')";
codigoPython += "fichw.read() ";
codigoPython += "fichw.close() ";
Value filecontent = context.eval("python", codigoPython);
it throws me this error:
PermissionError: (1, Operation not permitted, cadena.txt, None, None)
I also tried running it using sudo and sudo su but it gives me the same error. Does anyone know why this happened?
Thanks
You need to give your context permission to do IO:
Context context = Context.newBuilder("python").allowIO(true).build();
For experimenting/prototyping it may be useful to allow everything:
Context context = Context.newBuilder("python").allowAllAccess(true).build();
I spent significant amount of time looking for this and explore many solutions.
This is related to this thread.
Calling Java from Python
In the end, after testing:
Pyjnius : Cannot install in Windows.
Py4J: can install on windows, but using Gateway is a bit heavy.
JPype: Python 3 installed in 5 mins, can load 50Mo JAR without any issues.
Good thing is the syntax is completely merged with Python syntax...
https://github.com/tcalmant/jpype-py3
Just Wondering, if any people has developed real world wrapping application of Java in Python (ie running on a production server) with big size JAR ?
To save time to many people, I post the module I used for JPype, this is working nicel to load JAR.
import jpype as jp; import numpy as np; import os as os
jarpath= r"D:\zjavajar\\"
mavenurl= r"http://mvnrepository.com/artifact/"
# StartJVM (add "-Xmx" option with 1024M if crash due to not enough memory )
def importJAR(path1="", path2="", path3="", path4=""):
classpath = path1
if path2 != "": classpath = os.pathsep.join((classpath, path2))
if path3 != "": classpath = os.pathsep.join((classpath, path3))
if path4 != "": classpath = os.pathsep.join((classpath, path4))
jp.startJVM(jp.getJVMPath(),"-ea", "-Djava.class.path=%s" % classpath)
def showLoadedClass(): #Code to see the JAR loaded.
classloader = jp.java.lang.ClassLoader.getSystemClassLoader(); vv= [];
for x in classloader.getURLs(): vv.append(x.toString());
return vv
def loadSingleton(class1): single= jp.JClass(class1); return Single.getInstance()
def java_print(x): jp.java.lang.System.out.println(x) #Print in Java Console
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);
}
I have a program using the Luaj 3.0 libraries and I found some lua scripts I want to include, but they all require lua file system and penlight and whenever I try to use those libraries, it gives an error.
Does anyone know how I am supposed to make use of those in Luaj?
Edit:
A little more information might help:
I have am Archlinux 64bit system with open-jdk8 Luaj, lua-filesystem, and lua-penlight installed. I found a set of libraries called Lua Java Utils which I want to include in my project. But it always gets this error:
#luaJavaUtils/import.lua:24 index expected, got nil
Line 24 for reference:
local function import_class (classname,packagename)
local res,class = pcall(luajava.bindClass,packagename)
if res then
_G[classname] = class
local mt = getmetatable(class)
mt.__call = call -- <----- Error Here
return class
end
end
It requires the penlight library which in turn requires lua filesystem which is why I installed the two. I found through testing that Lua filesystem wasn't loading by trying to run lfs.currentdir(). I tried globals.load("local lfs = require \"lfs\"").call(); but it also gave an error.
My Lfs library is located at /usr/lib/lua/5.2/lfs.so and penlight at /usr/share/lua/5.2/pl.
This Is an Issue in the Luaj 3.0 and Luaj 3.0 alpha 1.
The lua package.path is being ignored while requiring a module. Here's a workout for this.
You can override the require function:
local oldReq = require
function require(f)
local fi = io.open(f, "r")
local fs = f
if not fi then
fi = io.open(f .. ".lua", "r")
fs = f .. ".lua"
if not fi then
error("Invalid module " .. f)
return
end
end
local l = loadfile(fs)
if not l then
return oldReq(f)
end
return l()
end
I am trying to access a device through a COM object in a JAVA interface.
The particular call (as described by the manufacturer) is:
Name: ScanUSB
Parameters: [out] VARIANT* serialNumbers
Use: serialNumbers is a pointer to a VARIANT containing an array of BSTR.
The exact call doesn't matter, but I need to feed it a BSTR array through the Java interface. A VB demo for the COM interface simply does this with the commandlm_Co1.ScanUSB(snNum), with Dim snNum As Object = Nothing. The Items in snNum are then displayed in a dropdown menu.
I am trying to do this with JACOB, as I have had the most luck with communication. This is more or less what I am using:
import com.jacob.com.Variant;
import com.jacob.com.Dispatch;
public class JunoReader {
public JunoReader() {
Dispatch oOphirLink = new Dispatch("clsid:{--the id of the device here--}");
Variant snNum = new Variant();
Variant testing = Dispatch.call(oOphirLink,"ScanUSB", snNum);
println(testing);
}
}
When I run this, everything compiles properly and I can confirm that I am communicating with the device, but all I get back is the null Variant that I put in.
My question is: How can I feed a BSTR array to a COM object through a JAVA interface?
Thanks!
So, one month and zero free time later, I have an answer to my own question and that answer is "Use Python".
Python allows users to access COM objects with the comtypes module, effectively with a single command:
from comtypes.client import CreateObject
target_guid = CreateObject("{TARGET-COM-CLSID-HERE}")
This allows python to talk with whatever the COM object is, so there was none of the trouble that Java was giving me. The single line: target_guid.ScanUSB() produced (u'717610',), the serial number of the target device. Notice that Python has no trouble reading the Unicode produced by the COM object.
The second trick involves Python's COM server, generated with the win32com module. Because the COM servers can be registered to Windows, I don't have to worry about where the dll is located. All I need is the clsid. To build the python COM server, I followed the instructions for a "quick start to server side COM and Python". So what does it all look like?
Python code:
class OphirPyCOM:
_reg_clsid_ = "{PYTHON-COM-CLSID-HERE}"
_reg_desc_ = "Python COM server"
_reg_progid_ = "Python COM server"
_public_methods_ = [ 'Hello',
'ConnectOphir',
'ScanUSB', """More methods here"""
]
_public_attrs_ = ["""Some public attributes"""]
_readonly_attrs_ = ["""some read-only attributes"""]
def __init__(self):
"""some variables declared here"""
def Hello(self, who):
"""Verifies a connection"""
return "{PYTHON-COM-CLSID-HERE}" + str(who)
def ConnectOphir(self,clsid):
"""Connects to the target COM Object"""
from comtypes.client import CreateObject
self.target_guid = CreateObject(clsid)
return "OphirLMMeasurement object created."
def ScanUSB(self):
"""Communicates with the target device"""
self.connected_inst = self.target_guid.ScanUSB()
for i in range(0,len(self.connected_inst)):
self.inst_list.append(str(self.connected_inst[i]))
return self.inst_list
if __name__ == "__main__":
# use 'python com.py' to register the COM server
# use 'python com.py --unregister' to unregister it
print "Registering COM server..."
import win32com.server.register
win32com.server.register.UseCommandLine(OphirPyCOM)
We can run that with the command line and get it registered. Then we take the values over to Java with JACOB:
import com.jacob.com.Variant;
import com.jacob.com.Dispatch;
public class JunoReader {
String pyClsid = "{PYTHON-COM-CLSID-HERE}"; // This is where your python COM clsid goes
String opClsid = "{TARGET-COM-CLSID-HERE}"; // This is where your ultimate target clsid goes
public JunoReader() {
_pyClsid = "clsid:" + pyClsid
// This finds the COM object location:
Dispatch oOphirLink = new Dispatch(_pyClsid);
}
public String HandShake() {
String _talkBack = "is connected.";
Variant _handShake = Dispatch.call(oOphirLink,"Hello",_talkBack); // I am trying to look for the Juno...
return (_handShake.toString());
}
public String ConnectOphir() {
Variant _connectOphir = Dispatch.call(oOphirLink,"ConnectOphir", opClsid); // Connect to the target COM object
return (_connectOphir.toString());
}
public String ScanUSB() {
Variant _serialNumberList = Dispatch.call(oOphirLink,"ScanUSB"); // This scans the USB ports for devices
return (_serialNumberList.toString());
}
}
calling JunoReader.ScanUSB() produces: 717610, exactly like it is supposed to. Implementing the subsequent methods of the manufacturer dll allowed me to read data from this device into a Java applet. Problem solved.
A caveat: you may need to unregister the Python COM every time you change the change the source file and then re-register with a different clsid. Java was having a tough time establishing a connection to the updated code unless I used a new clsid every time I changed the file.
Why did I spend all this time typing this up? Because most of the advice related to reading native data types into Java involved some version of JNI and JNA. I spent weeks trying to get the tutorials to compile and didn't make any progress. On the other hand, I thought of this approach yesterday and can now communicate with my device through Java. The Python COM server provided a simple, straightforward way to interface Java with native applications. No UnsatisfiedLinkErrors, no Can't find librarys, no Classpath and JAVA_HOME issues. I didn't need to learn C or C++ and all the Python tutorials worked as described with no necessary modification.
To summarize, if you are having trouble reading native data types into Java, just set up a Python COM server and let Python's dynamic typing do the work for you.