I am currently working on a project that allows users to enter an IP Address and a file name, then run Logcat to save the file to the user entered file name.
The UI
The Application will allows users to start writing logs, and stop writing logs with a start and stop button. On the bottom there will be a status textfield that updates the user with the status of the ADB Command.
The command I am using to retrieve the Logs is adb logcat > (filename).txt
Currently, I have the logging working perfectly fine at the push of the start button.
Logging
onStartButtonClick Method
#FXML
protected void onStartButtonClick() {
ipAddress = deviceIP.getText();
nameFile = fileName.getText();
if(nameFile == null || nameFile.equals("")){
createDialog("Please Enter Valid File Name");
return;
}
if(ipAddress == null || ipAddress.equals("")){
createDialog("Please Enter Valid IP");
return;
}
runCommands("adb connect "+ipAddress, "Connecting to IP Address");
runCommands("adb logcat > "+nameFile+".txt", "Logging IP: "+ipAddress);
}
My On start button click method passes the user entered data to a method I have that actually executes the commands.
runCommands Method
#FXML
public void runCommands(String command, String status){
ProcessBuilder builder = new ProcessBuilder(
"cmd.exe", "/c", command);
try {
builder.redirectErrorStream(true);
p = builder.start();
r = new BufferedReader(new InputStreamReader(p.getInputStream()));
spinner.setOpacity(100);
loggingStatus.setText(status);
startButton.setDisable(true);
stopButton.setDisable(false);
Thread t = new Thread(() -> {
threadRunning = true;
while(threadRunning){
try {
line = r.readLine();
}
catch (Exception e){
System.err.println(e);
}
if (line == null) {
break;
}
}
});
t.start();
}
catch (Exception e){
System.err.println(e);
onStopButtonClick();
}
}
The run command method makes a new process builder in cmd and executes the command I pass into it, using threading it goes through the output of the cmd line. I make a new thread that runs through the lines until I press the stop button to make the thread running variable false.
onStopButtonClick Method
#FXML
protected void onStopButtonClick(){
threadRunning = false;
p.destroy();
loggingStatus.setText("File has been saved!");
startButton.setDisable(false);
stopButton.setDisable(true);
spinner.setOpacity(0);
}
Stopping
When the stop button is pressed this code gets executed, ending the thread and destroying the process, along with some other screen changes.
The issue I'm having is that everything works exactly as intended, except the fact that when I press stop, the logging continues, and the logging ONLY stops when I close the application which is less than ideal functionality. I am confident threading is working to some extent since some of the code is being executed in the onStopButtonClick method, but the logging still continues despite that.
If anything it seems like I wrote the threading wrong, not sure where I messed up or if this is even achievable at all.
Appreciate any help, Thanks. :)
Related
I've been trying to make an app to act as a ssh / scp client to make transferring files easier from my laptop to my desktop and currently I have been able to get the output of a ls command and get the file tree for the host and remote user, however this requires input in the terminal I am running the app with.
Is it possible to be able to embed a terminal window into a gui as I have went about making an interpreter however things like tab completion and running, for example, python3 don't work. I am also hoping to have full use of a terminal and be able to run commands like vim rather than just print the output of commands which is what I currently have.
My code for executing the commands is:
public void processCmd(String command) {
if (command.equals("exit")) {
System.exit(0);
} else if (command != null && !command.isEmpty()) {
execCommand(command);
}
}
public void execCommand(String input) {
String result = null;
try {
Runtime r = Runtime.getRuntime();
Process p = r.exec(input);
BufferedReader in =
new BufferedReader(new InputStreamReader(p.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null) {
System.out.println(inputLine);
result += inputLine;
}
in.close();
} catch (IOException e) {
System.out.println("Error");
}
}
The main problem is that when anything like python or vim is run, the app will hang and not do anything, python won't even show up in terminal, however, if I run vim, it will change the terminal screen (not in the app) to mostly vim but without the bottom bar.
I am a total newbie in JSPs/Tomcat and to a large extent in Java as well. Here's what I have to do -- when a user clicks a button/visits a URL, I want to launch a Java program (which takes some command line arguments).
I can very easily do
Runtime.exec("C:\\Python27\\python.exe test.py")
OR
Runtime.exec("java -cp %CLASSPATH%;C:\\devprojects HelloWorld"
and this works fine. Where HelloWorld.class just prints "HelloWorld".
However, when I attempt a java program which takes command line arguments, the GET request just hangs doing nothing. I don't know what logs to look for or what could be wrong here. After having spent TWO days on trying various things, I am just about to give up now.
Runtime.exec("java -cp %CLASSPATH%;C:\\devprojects Run --username Blah --password Foo");
What user does Tomcat end up running this java program as? Can I make it to be Administrator? This is on Windows 2008, does UAC interfere with things?
I cannot modify the Run.class here, I HAVE to run it as is and with command line parameters.
Please advise.
One idea: you are relying on the default tokenization of your command line as one complete String, and it is not parsing the last one as you expect. Instead you should use the form of this method that takes a String[], after you have chopped up the command line yourself:
http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#exec(java.lang.String[])
Or, it is waiting for input from you, or waiting for you to read its output. This could explain the hang. Search the internet for the dangers of streams and Runtime.exec().
Consider ProcessBuilder instead.
Remember also that you have to be sure that the executed file dont run "forever", and
if you need to pass some arguments, you could use this:
static String startProcess(String command, String dir) throws IOException {
StringBuffer ret = new StringBuffer();
String[] comm = new String[3];
comm[0] = COMMAND_INTERPRETER[0];
comm[1] = COMMAND_INTERPRETER[1];
comm[2] = command;
long start = System.currentTimeMillis();
try {
//Start process
Process ls_proc = Runtime.getRuntime().exec(comm, null, new File(dir));
//Get input and error streams
BufferedInputStream ls_in = new BufferedInputStream(ls_proc.getInputStream());
BufferedInputStream ls_err = new BufferedInputStream(ls_proc.getErrorStream());
boolean end = false;
while (!end) {
int c = 0;
while ((ls_err.available() > 0) && (++c <= 1000)) {
ret.append(conv2Html(ls_err.read()));
}
c = 0;
while ((ls_in.available() > 0) && (++c <= 1000)) {
ret.append(conv2Html(ls_in.read()));
}
try {
ls_proc.exitValue();
//if the process has not finished, an exception is thrown
//else
while (ls_err.available() > 0)
ret.append(conv2Html(ls_err.read()));
while (ls_in.available() > 0)
ret.append(conv2Html(ls_in.read()));
end = true;
}
catch (IllegalThreadStateException ex) {
//Process is running
}
//The process is not allowed to run longer than given time.
if (System.currentTimeMillis() - start > MAX_PROCESS_RUNNING_TIME)
//this is very important
{
ls_proc.destroy();
end = true;
ret.append("!!!! Process has timed out, destroyed !!!!!");
}
try {
Thread.sleep(50);
}
catch (InterruptedException ie) {}
}
}
catch (IOException e) {
ret.append("Error: " + e);
}
return ret.toString();
}
I am trying to run a batch file with Runtime.exec() and then output its InputStream into a JTextArea. What I have works, but only partially. What happens is the batch file runs, but if it executes a command other than something like "echo" that command immediately terminates and the next line executes. For example, let's say I try to run a simple batch file like this:
#echo off
echo hello. waiting 5 seconds.
timeout /t 5 /nobreak > NUL
echo finished. goodbye.
The batch file executes, and the JTextArea says
hello. waiting 5 seconds.
finished. goodbye.
but it doesn't wait for 5 seconds in the middle.
I can't figure out why it's doing this. Here's what I use to run the batch file and read its InputStream.
private class ScriptRunner implements Runnable {
private final GUI.InfoGUI gui; // the name of my GUI class
private final String script;
public ScriptRunner(final GUI.InfoGUI gui, final File script) {
this.gui = gui;
this.script = script.getAbsolutePath();
}
#Override
public void run() {
try {
final Process p = Runtime.getRuntime().exec(script);
StreamReader output = new StreamReader(p.getInputStream(), gui);
Thread t = new Thread(output);
t.start();
int exit = p.waitFor();
output.setComplete(true);
while (t.isAlive()) {
sleep(500);
}
System.out.println("Processed finished with exit code " + exit);
} catch (final Exception e) {
e.printStackTrace();
}
}
}
private class StreamReader implements Runnable {
private final InputStream is;
private final GUI.InfoGUI gui;
private boolean complete = false;
public StreamReader(InputStream is, GUI.InfoGUI gui) {
this.is = is;
this.gui = gui;
}
#Override
public void run() {
BufferedReader in = new BufferedReader(new InputStreamReader(is));
try {
while (!complete || in.ready()) {
while (in.ready()) {
gui.setTextAreaText(in.readLine() + "\n");
}
sleep(250);
}
} catch (final Exception e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (final Exception e) {
e.printStackTrace();
}
}
public void setComplete(final boolean complete) {
this.complete = complete;
}
}
public void sleep(final long ms) {
try {
Thread.sleep(ms);
} catch (final InterruptedException ie) {
}
}
I know my code is pretty messy, and I'm sure it contains grammatical errors.
Thanks for anything you can do to help!
You're creating a Process but you're not reading from its standard error stream. The process might be writing messages to its standard error to tell you that there's a problem, but if you're not reading its standard error, you won't be able to read these messages.
You have two options here:
Since you already have a class that reads from a stream (StreamReader), wire up another one of these to the process's standard error stream (p.getErrorStream()) and run it in another Thread. You'll also need to call setComplete on the error StreamReader when the call to p.waitFor() returns, and wait for the Thread running it to die.
Replace your use of Runtime.getRuntime().exec() with a ProcessBuilder. This class is new in Java 5 and provides an alternative way to run external processes. In my opinion its most significant improvement over Runtime.getRuntime().exec() is the ability to redirect the process's standard error into its standard output, so you only have one stream to read from.
I would strongly recommend going for the second option and choosing to redirect the process's standard error into its standard output.
I took your code and replaced the line
final Process p = Runtime.getRuntime().exec(script);
with
final ProcessBuilder pb = new ProcessBuilder(script);
pb.redirectErrorStream(true);
final Process p = pb.start();
Also, I don't have your GUI code to hand, so I wrote the output of the process to System.out instead.
When I ran your code, I got the following output:
hello. waiting 5 seconds.
ERROR: Input redirection is not supported, exiting the process immediately.
finished. goodbye.
Processed finished with exit code 0
Had you seen that error message, you might have twigged that something was up with the timeout command.
Incidentally, I noticed in one of your comments that none of the commands suggested by ughzan worked. I replaced the timeout line with ping -n 5 127.0.0.1 > NUL and the script ran as expected. I couldn't reproduce a problem with this.
The problem is definitely in timeout.exe. If you add echo %errorlevel% after line with timeout, you will see that it returns 1 if running from java. And 0 if running in usual way. Probably, it requires some specific console functionality (i.e. cursor positioning) that is suppressed when running from java process.
Is there anything I can do to get this to work while running from Java
If you don't need ability to run any batch file then consider to replace timeout with ping. Otherwise... I've tried to run batch file with JNA trough Kernel32.CreateProcess and timeout runs fine. But then you need to implement reading of process output trough native calls also.
I hope someone will suggest better way.
The ready method only tells if the stream can guarantee that something can be read immediately, without blocking. You can't really trust it because always returning false is a valid implementation. Streams with buffers may return true only when they have something buffered. So I suspect your problem is here:
while (!complete || in.ready()) {
while (in.ready()) {
gui.setTextAreaText(in.readLine() + "\n");
}
sleep(250);
}
It should rather read something like this:
String line;
while (!complete || (line=in.readLine()) != null) {
gui.setTextAreaText(line + "\n");
}
It's probably because your "timeout ..." command returned with an error.
Three ways to test it:
Check if the "timeout ..." command works in the Windows command prompt.
Replace "timeout ..." in the script with "ping -n 5 127.0.0.1 > NUL" (it essentially does the same thing)
Remove everything but "timeout /t 5 /nobreak > NUL" from your script. The process should return with an error (1) if the timeout failed because it is the last command executed.
I have developed a exe of my Java application, and run it from a thumb drive. It will take some time to execute. But my end user thinks it is not running and clicks for the second time. I need to stop this. I need to stop continuous click on the exe, while it is running. I used the shell script to check whether the exe is running or not. And displays the message, that the exe is already running. And stops the further process. I need this to be happened while exe is running for the second time. I couldn't figure this out. Is there any way to disable the exe from click, while it is running. Or how can I use the check whether it is running or not.
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.InputStreamReader;
public class VBSUtils {
private VBSUtils() { }
public static boolean isRunning(String process) {
boolean found = false;
try {
File file = File.createTempFile("realhowto",".vbs");
file.deleteOnExit();
FileWriter fw = new java.io.FileWriter(file);
String vbs = "Set WshShell = WScript.CreateObject(\"WScript.Shell\")\n"
+ "Set locator = CreateObject(\"WbemScripting.SWbemLocator\")\n"
+ "Set service = locator.ConnectServer()\n"
+ "Set processes = service.ExecQuery _\n"
+ " (\"select * from Win32_Process where name='" + process +"'\")\n"
+ "For Each process in processes\n"
+ "wscript.echo process.Name \n"
+ "Next\n"
+ "Set WSHShell = Nothing\n";
fw.write(vbs);
fw.close();
Process p = Runtime.getRuntime().exec("cscript //NoLogo " + file.getPath());
BufferedReader input =
new BufferedReader
(new InputStreamReader(p.getInputStream()));
String line;
line = input.readLine();
if (line != null) {
if (line.equals(process)) {
found = true;
}
}
input.close();
}
catch(Exception e){
e.printStackTrace();
}
return found;
}
}
And In my main class, I called VBUtils.
boolean result = VBSUtils.isRunning("myexe.exe");
if(result)
{
msgBox("myexe is running. Please wait");
}
else
{
// my part of execution.
}
If I call like this, the exe got terminated. Both the first and second execution.
The easiest way would be to present the user with some kind of visual feedback, so he knows that the application is running. (e.g. a console window with messages, a wait dialog, ...)
You need to use some sort of flag to indicate that your app is running. Many apps use a temp .pid file. When your app starts it checks for a .pid file, if there is one then it exits with an error message. If there is no .pid file then it creates one and runs as normal.
First of all java has a provision for a very early splash screen:
http://download.oracle.com/javase/6/docs/api/java/awt/SplashScreen.html
Furthermore per RMI you can on startup connect to any possibly running instance and stop and transmit command line ("open files ..."). RMI allows you to play remote server.
In the process of building a P2P app for a class, I start a number of peers from an ant build file before starting one in "interactive" mode.
The ant docs on the task states that one can interact with a forked app since ant-1.6.3
The code to read input is:
while (true) {
System.out.println("> ");
BufferedReader br = new BufferedReader(new InputStreamReader(
System.in));
String cmd = "";
try {
cmd = br.readLine();
System.out.println(cmd + "hier");
if (cmd == null)
continue;
if (cmd.equals("hello")) {
System.out.println("Port: ");
int bsPort = new Integer(br.readLine());
System.out.println("IP (blank for localhost): ");
String bsIp = br.readLine();
if (bsIp.equals(""))
bsIp = "127.0.0.1";
bootstrap(bsIp, bsPort);
}
else if (cmd.equals("plist")) {
plist();
}
else if (cmd.equals("nlist")) {
nlist();
}
else {
System.out.println("Command was read as: " + cmd);
}
} catch (IOException ioe) {
System.out.println("IO error trying to read your command!");
System.exit(1);
}
The relevant ant task is:
The ant task can apparently not be shown by SO, but it's basically a java fork="false" block.
Of note is that I run the root and subsequent peers in parallel -> daemons blocks to get them to run in the background. Those are forked.
I have tried with fork="true" and fork="false" (finding a link that said to set it to false) - both to no avail.
I have found one link saying that I can use a TimedBufferedReader, but am a bit wary to do so only to please ant...
Seems the daemon tasks hijacked System.in. Setting an inputstring="" argument on those did the prevented them from doing so, and let me interact with the interactive client.
Just to let others know, if one outputs via System.out.print instead of System.out.println, one will not see the output before after having given the input...