Calling python script from java web application with Processbuilder - java

I am working on a simple meteo station - I want to use raspberry pi 3b+ as a host, dht22 sensor and write a web application in Java (with spring boot, then deploy it to tomcat 8) and Python for retrieving sensor's data.
What I've done so far:
Python application for retrieving and displaying data. Works as expected, it just prints something like "22.5;37.4":
import Adafruit_DHT
DHT_SENSOR = Adafruit_DHT.DHT22
DHT_PIN = 4
humidity, temperature = Adafruit_DHT.read_retry(DHT_SENSOR, DHT_PIN)
if humidity is not None and temperature is not None:
print("{0:0.1f};{1:0.1f}".format(temperature, humidity))
else:
print("FAIL")
Then I've wrote a java application, put it into .jar and checked if I am able to get sensor's data. Not a rocket science, also works as expected when I use java -jar InputTest.jar on my raspberry pi:
public static void main(String[] args) {
try {
ProcessBuilder pb = new ProcessBuilder("python", "/home/pi/Desktop/input/dht_once.py");
Process process = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println("measured: " + line);
}
process.waitFor();
} catch (IOException ) {
System.out.println(" exception " + e.getLocalizedMessage());
}
}
Then I've created a spring boot application, put my java code inside (logic same as above), packed as a war, deployed to tomcat 8 and run it. It turned out nothing is being printed (of course I've changed code to log output to logfile, it works fine, I can see other logs inside). No issues in logs, it looks like reader never returns a line.
I believe application does not wait for a process to produce output, but I have no idea why. Important thing: it takes up to few seconds to produce sensor's output. I've also changed python script just for test purposes to return value immediately:
print("22.4;33.0")
and it results in successful read by java web application. But when it has to wait few seconds for the output it kills process (process.isAlive() is false right after while loop).
I've also tried to play with sleep() on current thread to force it to wait for python process but no success.
Do you guys have any idea what can be the reason for this behavior? Is there anything more I should check?
TLDR;
Java application which creates python process works fine until I run it as a web application - then it looks like it does not wait for a process' output

I haven't found a solution yet, however I've implemented workaround/cleaner solution.
I've decided to separate totally java and python code and created microservice for data retrieving. I use flask for rest webservice (followed this tutorial https://docs.dataplicity.com/docs/control-gpios-using-rest-api) and call it directly from java.
Since this does not resolve my initial question I do not mark it as an answer, however it might help someone.

Related

How can I run a PowerShell script that requires admin rights through Java

I am creating a simple app for our users that checks if a specific user is active on a PC and returns the result.
The app consists of two parts, one will be distributed to our seniors who can set their status (meeting, break etc.), the other will be used by our juniors to check if the senior is available for helping out at any given time.
It works by running a PowerShell script which pings a PC, checks the user and stores the result in a (accessible) hashtable on a network share.
Both apps work perfectly when used by a admin, but problems arise when a normal user tries the same, it just does not run the required PS scripts.
I am 99% sure that user rights are the culprit here but I am struggling to find a simple enough solution.
I have also setup a GPO (checked the PC-s via gpresult and rsop.msc) that should theoretically allow it:
Computer Configuration > Policies > Administrative Templates > Windows Components > Windows PowerShell -- Turn on script execution -- Allow all scripts. (Unsafe, but it is for testing purposes :))
The Java code snippet that runs the script looks like this:
run_script(String path) throws IOException {
String command = String.format("powershell.exe -executionpolicy bypass -file %s",path);
String result;
Process powerShellProcess = Runtime.getRuntime().exec(command);
powerShellProcess.getOutputStream().close();
BufferedReader brd= new BufferedReader(new InputStreamReader(
powerShellProcess.getInputStream()));
while ((result= brd.readLine()) != null) {
System.out.println(result);
}
brd.close();
BufferedReader err= new BufferedReader(new InputStreamReader(
powerShellProcess.getErrorStream()));
while ((line = err.readLine()) != null) {
System.out.println(result);
}
err.close();
}
Just to repeat the important part, everything runs fine when a admin runs the app, only normal users cannot get the expected result.
And finally, my question :)
Is it possible to run code like that without admin rights on a remote PC through a Java app and are there any alternatives for getting the result I want (ping PC, check user, return result without using PowerShell)?
Thank you in advance for any suggestions and advice!

Java Process object fails to execute given command

I am trying to run a piece of Python code via a Java application. The command when put directly into Command Prompt cd'd to the working directory runs exactly as intended. However, my attempts to use the Runtime and ProcessBuilder classes in conjunction with the Process class has yielded no sign of correct function which would be the creation of a CSV file for every call of the code.
I am running this program using Intellij on Windows 10. I have added each directory I am using to my environmental PATH variable as well as attempting full paths in my commands and just file names. The only source of life I can find is that if I include a .waitFor() method a .isAlive() method will return true before the .waitFor() method is called.
I have searched through various similar questions and concluded that using a ProcessBuilder object is the best way to go and that the biggest issue is probably the structure of my command. However, I have made many iterations and have found nothing that changes the caught error to anything useful.
Here is the privacy augmented code that I have been running, I wrote out the command in full in the process builder as that is the last iteration I have attempted.
for (int y = 1; y < iterator; y++) {
try {
String command =
"C:\\Users\\myName\\AppData\\Local\\Programs\\Python\\Python37\\python C:\\Users\\myName\\IdeaProjects\\projectApplication\\script.py ";
String pythonInputPath = " C:\\Users\\myName\\IdeaProjects\\projectApplication\\bin\\output" + y + ".wav ";
ProcessBuilder pb = new ProcessBuilder(command+Arrays.toString(pythonCommandString).replaceAll("\\s","")+pythonInputPath+Integer.toString(y));
Process p = pb.start();
//Process checks
System.out.println(p.isAlive());
p.waitFor();
System.out.println(p.isAlive());
//Destroying process once complete to ensure smooth iterations
p.destroy();
} catch (Exception ex) {
System.out.println("Problems with python script execution: " + ex);
}
}
They python code takes in a WAV file (pythonInputPath) that is a product of earlier part of the application, an Integer[] that usually includes ~20 values (pythonCommandString), and a single iteration integer (y).
The first call to .isAlive() is true and the second is false as expected however the script normally creates a CSV that should be output to a bin file that exists in the working director and that fails to occur when running from Java. From other examples I expected using the Process builder as opposed to the Runtime stream to work, however, there is no difference in my implementation.
Do not concatenate the program with its arguments. Quoting Oracle ProcessBuilder docs
Each process builder manages these process attributes: a command, a
list of strings which signifies the external program file to be
invoked and its arguments, if any
and
ProcessBuilder pb = new ProcessBuilder("myCommand", "myArg1", "myArg2");
Just use the constructor you use, but pass each argument as a separate string, otherwise the OS will try to find an application that is named as a whole command line you gave, and obviously there is no such program

How to access BLE on Raspberry Pi 3 using Java?

The Raspberry Pi 3 includes BLE support. I confirmed it works by
sudo hcitool lescan
which returned the MAC and BLE 'complete local name' for neighboring advertisers.
How does one access this programmatically, in Java?
To use BLE on Raspberry Pi 3 you have to update bluez (the bluetooth core in Raspbian linux) then use the bluez D-Bus interface to interact with it.
I'm looking for writing my own java lib, but it's very difficult because there are few documentation about D-Bus in java and about bluez.
For bluez there are only the sample code in the last distribution.
For now i have write a simple script that update the bluez version to the latest:
https://gist.github.com/tongo/94338cebf4e6367d353439bca8d0a376
I have also found a blog post that talk about d-bus,java and bluez:
http://smartspacestuff.blogspot.it/2016/02/i-got-figurin-out-dbus-bluez.html
It was useful, but not very clear for me.
I hope this can help.
If you found other documentation post it.
I don't think there is a clear or easy answer available at this time. Bluetooth integration requires native components that are not part of a standard JDK.
The most common library used for using Bluetooth with Java on Linux is BlueCove. BlueCove provides extra native libraries for working with Bluetooth on linux: BlueCove-GPL or BlueCove-bluez(experimental). However, you will likely need to compile one of these yourself for use on your RPi. Methods for doing do will be dependent on your distribution and will require some significant knowledge of linux, compiling native code, etc. A quick google search shows some working examples of this for previous RPi versions. It's unclear if it will work with the BLE on RPi 3 though.
Another might be to try using "Camel Bluetooth Component", which is wrapper over Bluecove and expects libbluetooth-dev and blueman to be installed. But again, not clear if it will work with RPi 3.
If unable to get a true integration working, another option might be to simply make external Process calls out from Java to the command line Bluetooth utilities that you know already work. It depends on your use-case if this an option, but I suspect could be sufficient for many BLE specific use cases.
You can make this very simple by simply executing a command through Runtime and reading the output with a BufferedReader.
Executing the command:
Process p;
p = Runtime.getRuntime().exec(command);
p.waitFor();
Full code:
package your.package.rpicommand;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class ExecuteShellCommand {
public static void main(String[] args) {
ExecuteShellCommand obj = new ExecuteShellCommand();
String domainName = "google.com";
//in mac oxs
String command = "ping -c 3 " + domainName;
//in windows
//String command = "ping -n 3 " + domainName;
String output = obj.executeCommand(command);
System.out.println(output);
}
private String executeCommand(String command) {
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec(command);
p.waitFor();
BufferedReader reader =
new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null) {
output.append(line + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
}
I wish I could of coded this for you but there is already plenty of example on the internet about this.
Although this is one way of doing this, you should use BlueCove or some library to scan through the Bluetooth devices.
Source: https://www.mkyong.com/java/how-to-execute-shell-command-from-java/

Read and write to cmd in windows

I used system("java .....")to run a java app in cmd with VC++ code.
The java app will run a server in the cmd,it will output info in the console.And I can also enter commands to it just like run "dir" commands in cmd.
Now I want get all the output in my program and use C++ code to write commands sent to the java app.
But I found that the system() won't return until I stop the java app.It's reasonable.And how to avoid it?Use Thread ?
And the biggest problem is I don't know how to get the output and write commands,can anyone give me a method?
Thanks a lot!
P.S. The java app's code can't be changed.
--------------------------------------I have made progress--------------------
int main()
{
char psBuffer[256];
FILE* output = _popen("java xxxx.jar", "rt" );
if(output == NULL)
return 0;
while(fgets(psBuffer, 256, output))
{
printf(psBuffer);
}
if (feof( output))
{
printf( "\nProcess returned %d\n", _pclose( output ) );
}
else
{
printf( "Error: Failed to read the pipe to the end.\n");
}
system("pause");
return 0;
}
When I use "dir".It works perfect!But when I use java,psBuffer is always nothing,and the output of java app is normally.Is it pipe cannot redirect java's output?
I change my code and make some java command run perfect:
FILE* output = _popen("java -version 2>&1", "rt" );
But when it run that .jar,It failed.I read the .jar's code and find the output is create by java.util.logging.Logger.info().I'm not familiar with java. How dose the info() work in cmd?
Thanks many!
Finally, I found last code above is work correctly.But origin output of java app haven't been redirect .It will display normally,but buffer is correctly received the output I want.Anyway I solved my problems.Thanks everyone!!!
The MSDN article Creating a Child Process with Redirected Input and Output explains how you can do it. It is quite a lot of code to go through, but will allow you to do what you want, and give you full control over it.
On the other hand, using _popen is much easier, but you don't have as much control. Depends on your exact needs as to how much code you'll be writing :).

Runtime.getRuntime().exec strange behavior

I am trying to call a perl script from java runtime. It worked fine on my windows7 laptop with the following code,
try {
String cmdString= "c:\\perl64\\bin\\perl.exe c:\\perl64\\eg\\userinput.pl \""+arg1+"\" \""+arg2+"\"";
Process p = Runtime.getRuntime().exec(cmdString);
} catch(IOException e) {
System.out.println(e);
}
The perl script runs and produces what I expect (update database).
When I move the whole thing over to a remote CentOS server, it doesn't work anymore. The script is the same and the java code is,
try {
String cmdString= "/opt/lampp/bin/perl /home/support/scripts/userinput.pl \""+arg1+"\" \""+arg2+"\" > /tmp/userinput.log";
log(cmdString);
Process p = Runtime.getRuntime().exec(cmdString);
} catch(IOException e) {
System.out.println(e);
}
I added redirect to /tmp/userinput.log after I see the script is not working. But there is no log file created at all. I also added log to make sure this part of the java code did get executed, and indeed it did. I also tried to add "/bin/bash " in front of the comString and it didn't make a difference. However, when I run the cmdString directly on the remote server from command line, it works without problem.
Now, when I changed the cmdString to "touch /tmp/userinput.log", it does create the empty log file.
So I know the Runtime.getRuntime().exec(cmdString) command ran, and the cmdString works when entered on command line, and a simple "touch" command would work with this setup. But I am totally lost why the actual cmdString that calls the perl script doesn't work, and there is no message whatsoever to tell me what is wrong.
Can someone please help?
Frist, separate each parameter for the command and use the version of exec which takes a String[] (you won't have to worry about quoting issues). also, shell redirection won't work since java isn't executing a shell.

Categories

Resources