JavaFX program cannot run python script after it's deployed - java

I have written a JavaFX program that runs a python script that uses google console search api to get data. It works fine before I deploy it as a standalone program (.exe). After I install the program it runs fine until I click the button that runs the script; it does nothing.
After the button is clicked it succesfully checks if the python is installed or not, after that the problem occurs when it tries the run the script. I have specified the path like this, "./src/com/fortunamaritime/" and I think this might be the problem.
private static final String ANALYTICS_PATH ="./src/com/fortunamaritime/";
inside the method
//set up the command and parameter
String[] cmd = new String[3];
cmd[0] = "python";
cmd[1] = "analytics.py";
cmd[2] = "https://fortunamaritime.com.tr";
String command = cmd[0] + " " + cmd[1] + " " + cmd[2] + " " +
startDate + " " + endDate;
ProcessBuilder builder = new ProcessBuilder("cmd.exe", "/c", "cd " + new
File(ANALYTICS_PATH).getAbsolutePath() + " && " + command);
builder.redirectErrorStream(true);
Process p = builder.start();
I have redirected console output to a .txt file to see what was happening and there's only one line that said "system cannot find the path specified".
I have switched from absolute path to relative path and it produced this error:
The filename, directory name, or volume label syntax is incorrect.
I also printed this line to console
this.getClass().getResource("/com/fortunamaritime/analytics.py")
and the result is:
jar:file:/D:/IdeaProjects/FortunaWebTracker/out/artifacts/FortunaWebTracker/FortunaWebTracker.jar!/com/fortunamaritime/analytics.py
Is it possible to access the inside of the jar file and run the script from there via cmd through stripping parts of this URL?

Copy the script to a (maybe temporary) file and run it from that file.
You can get the script as InputStream with Main.class.getResourceAsStream(String resc) (relative to main) or Main.class.getClassLoader().getResourceAsStream(String resc) (relative to root)
You may use java.nio.file.Files#createTempFile(String,String and delete that file on exit.
You can also copy the script into the user home or relative to the exe (and don't delete it).
For example, you could do this:
File dir=new File("./resc");
if(!dir.exists()) {
dir.mkdirs();
}
File analyticsFile=new File(dir,"analytics.py");
if(!analyticsFile.exists()) {
/*
* alternative:
* this.getClass().getResourceAsStream("com/fortunamaritime/analytics.py")
*/
try(InputStream analyticsIn = this.getClass().getClassLoader().getResourceAsStream("analytics.py")){
Files.copy(analyticsIn, analyticsFile.toPath());
}
}
/*
* alternative: String command=String.join(" ",new String[]{"python",analyticsFile.getAbsolutePath(),"https://fortunamaritime.com.tr"});
*/
String command="python "+analyticsFile.getAbsolutePath()+" https://fortunamaritime.com.tr";
/*
* alternative (without error redirection): Process p=Runtime.getRuntime().exec(command);
*/
ProcessBuilder builder = new ProcessBuilder(command);
builder.redirectErrorStream(true);
Process p=builder.start();

I have solved my problem.
Here's the code sample first, explanation will follow;
String path = new
File(Main.class.getProtectionDomain().getCodeSource().getLocation()
.toURI()).getPath();
path=path.substring(0,path.length()-21);
// set up the command and parameter
String[] cmd = new String[3];
cmd[0] = "python";
cmd[1] = "analytics.py";
cmd[2] = "https://fortunamaritime.com.tr";
String command0="jar xf FortunaWebTracker.jar" +
"com/fortunamaritime/analytics.pycom/fortunamaritime/client_secrets.json" +
" com/fortunamaritime/webmasters.dat";
String command1 = cmd[0] + " " + cmd[1] + " " + cmd[2] + " " + startDate + " " +
endDate;
ProcessBuilder builder = new ProcessBuilder("cmd.exe", "/c", "cd " + path + " && " +
command0+"&&"+"cd "+path+"\\com\\fortunamaritime"+" && "+command1);
builder.redirectErrorStream(true);
Process p = builder.start();
My first mistake was to use absolute path, and the second problem arose when I got the path I wanted via:
File(Main.class.getProtectionDomain().getCodeSource().getLocation()
.toURI()).getPath();
//Jar's name is 21 characters long, minus that and I get the directory path
path=path.substring(0,path.length()-21);
The second problem was unpacking elements inside the jar so I used this command in cmd:
jar xf FortunaWebTracker.jar
com/fortunamaritime/analytics.py
com/fortunamaritime/client_secrets.json
com/fortunamaritime/webmasters.dat
then I could get it to work.

Related

How to run multiple cmd commands through Java?

I have a simple GUI which selects an executable and a batch file. Clicking "run" should launch a command line instance, then run the executable given the selected batch. However, hiccups seem to show up at different points. This is the relevant code snippet:
String[] commands = {"cmd.exe", "/c", "C:\\Xilinx\\14.7\\ISE_DS\\settings64.bat && cd /d ",
"\"" + simFile.getParent() + "\"", " && ping localhost && ",
"\"" + jTextField1.getText() + "\"", " -tclbatch \"" + jTextField2.getText() + "\""};
ProcessBuilder simBuilder = new ProcessBuilder(commands);
simBuilder.redirectErrorStream(true);
Process simulation = simBuilder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(simulation.getInputStream()));
String line;
while (true) {
line = reader.readLine();
if (line == null)
break;
System.out.println(line);
}
I chose to create a process through a ProcessBuilder rather than "Runtime.getRuntime().exec" because having the command and arguments as a String array is more readable and manageable. I took a look through the documentation of Runtime, Process, and ProcessBuilder. I also searched for similar questions, the following being the closest: Run cmd commands through Java. However, I'm still having issues getting all commands to run properly, if it all. First point: The program successfully executes the commands until "ping", which I placed to determine where the issue occurs. I get the cmd output in the console through the BufferedReader just fine. However, the next command, which should run the executable indicated by "jTextField1.getText()", gives an error of "The filename, directory name, or volume label syntax is incorrect" although I made sure the path is within escaped double quotes to account for spaces. Is it something in my syntax? Something to do with where the double ampersands are placed? Does every separate command with its argument need to be its own string in the array? I tried that and different things, but it always seems to result in an error.
You should check that your path names are correct, and try the cmd as one parameter value not as comma separated after cmd.exe /c. This will ensure the arguments are passed to CMD correctly as a single argument for the CMD shell to handle:
import java.nio.file.Files
System.out.println("Files.isDirectory(simFile.getParent())="+Files.isDirectory(simFile.getParent()));
System.out.println("Files.isExecutable(jTextField1.getText())="+Files.isExecutable(Path.of(jTextField1.getText())));
String cmd = "C:\\Xilinx\\14.7\\ISE_DS\\settings64.bat && cd /d "+
"\"" + simFile.getParent() + "\" && ping localhost && "+
"\"" + jTextField1.getText() + "\" -tclbatch \"" + jTextField2.getText() + "\"";
String[] commands = {"cmd.exe", "/c", cmd};

Limit on commands for a ProcessBuilder?

is there a limit of commands on a ProcessBuilder?
I have this array of commands:
protected String[] cmd = {
"dism /mount-wim /wimfile:boot.wim /index:2 /mountdir:mount",
"dism /image:mount /add-driver:\"driver\" /recurse",
"dism /unmount-wim /mountdir:mount /commit",
"dism /mount-wim /wimfile:install.wim /index:" + formPanel.getOsIndex() + " /mountdir:mount"
};
And this is my ProcessBuilder:
ProcessBuilder pb = new ProcessBuilder(
"cmd.exe", "/c", cmd[0] + " && " + cmd[1] + " && " + cmd[2] + " && " + cmd[3] + " && " + cmd[1] + " && " + cmd[2]
);
But when I run it it says '&& was unexpected at this time'. When I change the processbuilder to this:
ProcessBuilder pb = new ProcessBuilder(
"cmd.exe", "/c", cmd[0] + " && " + cmd[1] + " && " + cmd[2]
);
Then it works fine.
So my question is basically just if there's a sort of limit of how many commands a single processbuilder can pass?
Here's the whole segment of my SwingWorker method:
#Override
protected Integer doInBackground() {
try {
ProcessBuilder pb = new ProcessBuilder(
"cmd.exe", "/c", cmd[0] + " && " + cmd[1] + " && " + cmd[2] + " && " + cmd[3] + " && " + cmd[1] + " && " + cmd[2]
);
pb.directory(new File(formPanel.workspaceDir.toString()));
pb.redirectErrorStream(true);
Process p = pb.start();
String s;
BufferedReader stdout = new BufferedReader(new InputStreamReader(p.getInputStream()));
while((s = stdout.readLine()) != null && !isCancelled()) {
publish(s);
System.err.println(s);
}
if(!isCancelled()) {
status = p.waitFor();
}
p.getInputStream().close();
p.getOutputStream().close();
p.getErrorStream().close();
p.destroy();
} catch(IOException | InterruptedException ex) {
ex.printStackTrace(System.err);
}
return status;
}
I'm starting to wonder if there's something wrong with the actual code, not the commands.
I think the limit you have to take into account first is the limit of a command itselft (then ProcessBuilder) which is different if you're on Windows or Unix.
For Windows, according to "Command prompt (Cmd. exe) command-line string limitation" documentation :
On computers running Microsoft Windows XP or later, the maximum length
of the string that you can use at the command prompt is 8191
characters. On computers running Microsoft Windows 2000 or Windows NT
4.0, the maximum length of the string that you can use at the command prompt is 2047 characters.
This limitation applies to the command line, individual environment
variables (such as the PATH variable) that are inherited by other
processes, and all environment variable expansions. If you use Command
Prompt to run batch files, this limitation also applies to batch file
processing.
For Unix, I suggest you to refer to the following Stackoverflow question which is resolved now :
Maximum number of Bash arguments != max num cp arguments?
Also, you should take account of limit size of an array in Java which is described into the following Stackoverflow question :
Do Java arrays have a maximum size?
I think the whole command that you sent might be too long for cmd.exe as you use the executable there did you considered using Runtime.exec(); or something like this ?
List<String> commands = new ArrayList<>();
final ProcessBuilder builder = new ProcessBuilder();
commands.add("dism /mount-wim /wimfile:boot.wim /index:2 /mountdir:mount");
And so on, additionaly im not sure if you can have whitespaces here, or you need to add everything as a seperate command.
builder.command(commands);
builder.directory(new File(workingDir));
process = builder.start();

How to change the Directory from command prompt using Java code

public void VerifyFiles(File dir) throws IOException{
File[] files = dir.listFiles();
for (File file : files) {
//Check if directory
if (file.isDirectory()) {
//Recursively call file list function on the new directory
VerifyFiles(file);
} else {
try {
ProcessBuilder builder = new ProcessBuilder(
"cmd.exe", "/c", "cd \" C:\\Users\\e843778\\Documents\\NetBeansProjects\\EncryptedFiles\" && C:\\Program Files\\PGP Corporation\\PGP Desktop");
builder.redirectErrorStream(true);
Process p = builder.start();
Runtime rt = Runtime.getRuntime();
String query2 = "cmd /c pgpnetshare -v " + "\"" + file + "\"";
Process proc = rt.exec(query2);
proc.waitFor();
System.out.println("Executing for: " + file);
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line = "";
String s1 ="";
while ((line = in.readLine()) != null)
{
System.out.println(line);
if(line.contains("All files and folders are encrypted"))
s1+=line;
}
System.out.println(s1);
}
I have to run a pgpnetshare command for all the files in a given directory using command prompt. For that, first i need to change my current directory to other directory. But i was not able to change the directory with the below code. I have checked all the previous answered questions in Stackoverflow.com. But none of them helped me.Please verify the code and please let me know if it needs any corrections. Thanks in advance!!.
That should work:
Process process=Runtime.getRuntime().exec(query2,
null, new File("directory path"));
From the documentation:
Process exec(String[] cmdarray, String[] envp, File dir)
Executes the specified command and arguments in a separate process with the specified environment and working directory.
You can always create a .bat file and execute that instead.
you should use using file.getAbsolutePath()
like
String query2 = "cmd /c pgpnetshare -v " + "\"" + file.getAbsolutePath() + "\"";

Java runtime exec with scp command

This is a part of my code to copy files from local to a remote machine
try {
Process cpyFileLocal = Runtime.getRuntime().exec("scp " + rFile+"*.csv" + " root#" + host + ":" + lFile);
InputStream stderr = cpyFileLocal.getErrorStream();
InputStreamReader isr = new InputStreamReader(stderr);
BufferedReader br = new BufferedReader(isr);
String line = null;
System.out.println("<ERROR>");
while ((line = br.readLine()) != null) {
System.out.println(line);
}
System.out.println("</ERROR>");
int exitVal = cpyFileLocal.waitFor();
System.out.println("Process exitValue: " + exitVal);
System.out.println("...." + cpyFileLocal.exitValue());
System.out.println("SCP COMMAND "+"scp "+rFile+"*.csv" +" root#"+host+":"+lFile);
System.out.println("Sending complete...");
} catch (Exception ex) {
ex.printStackTrace();
}
the output is...
<ERROR>
/opt/jrms/rmsweb/transfer/cn00/outgoing/*.csv: No such file or directory
</ERROR>
Process exitValue: 1
....1
SCP COMMAND scp /opt/jrms/rmsweb/transfer/cn00/outgoing/*.csv root#10.50.1.29:/opt/jrms/transfer/incoming/
but when I run the command in terminal on the local machine, it works fine
and when I run ll the files are there
-rwxr-xr-x 1 freddie freddie 140 Apr 22 09:13 gc00cn00150420092629.csv*
-rwxr-xr-x 1 freddie freddie 105 Apr 22 09:13 gc00cn00150420122656.csv*
Any help please
If you are using java 7 and above you should use ProcessBuilder instead of Runtime.getRuntime().exec() and in the ProcessBuilder you can specipied the execution directory:
ProcessBuilder pb = new ProcessBuilder("scp", rFile+"*.csv", "root#" + host + ":" + lFile);
Map<String, String> env = pb.environment();
env.put("VAR1", "myValue");
env.remove("OTHERVAR");
env.put("VAR2", env.get("VAR1") + "suffix");
pb.directory("directory where the csv files located");
Process p = pb.start();
When you run command with in bash with wildcards like * in it, bash will expand that command and in your case, replaces *.csv with list of files terminating with .csv, but in your java program, this won't happen.
According to this answer, you can do following:
Use file.listFiles() to get the list of files
Use file.getName().contains(string) to filter them if needed
Iterate over the array and perform scp or do it with whole list
or with thanks to #James Anderson comment add sh before scp in your command.
According to this, you should try:
Process cpyFileLocal = Runtime.getRuntime().exec(new String[] {"/bin/sh","-c", "scp " + rFile+"*.csv" + " root#" + host + ":" + lFile});
I tested with /bin/sh and /bin/bash, both copied the files over successfully

Java - Exec console

I want to create a full cross-plateform console in Java.
The problem I have is when I use the cd command, the path is reset. For example, if I do cd bin, then cd ../, I will execute the first one from the directory of my app and the second one exactly from the same directory.
If I want to go to a specific folder and execute a program I have to do something like that:
cd C:\mydir & cd bin & start.exe
What I want to do is to split this cmd in different parts:
cd C:\mydir then cd bin then start.exe
How can I do that? Is there a way to store the current cd path and use it then?
Here is the code I use:
String[] cmd_exec = new String[] {"cmd", "/c", cmd};
Process child = Runtime.getRuntime().exec(cmd_exec);
BufferedReader in = new BufferedReader(new InputStreamReader(child.getInputStream()));
StringBuffer buffer = new StringBuffer();
buffer.append("> " + cmd + "\n");
String line;
while ((line = in.readLine()) != null)
{
buffer.append(line + "\n");
}
in.close();
child.destroy();
return buffer.toString();
It executes the command, then return the content of the console. (This is for windows for the moment).
If you want to run a command from a specific directory, use ProcessBuilder instead of Runtime.exec. You can set the working directory using the directory method before you start the process. Don't try to use the cd command - you're not running a shell, so it doesn't make sense.
If you do cd you don't want to execute it. You just want to check if the relative path exists and then change a
File currentDir
to that directory. So I would propose you split your commands up in three: cd, dir/ls and other stuff. cd changes the dir, as I mentioned, by using a File currentDir, dir should just get folders and files of currentDir and list them and then the rest you should just execute as you do know.
Remember you can split a command string by "".split("&"); this way you can do "cd C:\mydir & cd bin & start.exe".split("&"); => {"cd C:\mydir", "cd bin", "start.exe"} and then you can execute them in order.
Good luck.
Thanks to Mads, I was able to do the trick:
Here is the code I used:
if (cmd.indexOf("cd ") >= 0)
{
String req_cmd = cmd.substring(0, 3);
String req_path = cmd.substring(3);
if (req_path.startsWith(File.separator) || req_path.substring(1, 2).equals(":"))
path = req_path;
else
if (new File(path + cmd.substring(3)).exists())
path += cmd.substring(3);
else return "[Error] Directory doesn't exist.\n";
if (!path.endsWith(File.separator)) path += File.separator;
cmd = req_cmd + path;
}
else cmd = "cd " + path + " & " + cmd;
Then you can execute the command calling:
Runtime.getRuntime().exec(new String[] {"cmd", "/c", cmd});
Don't forget to add this attribute in your class:
private static String path = System.getProperty("user.dir") + File.separator;

Categories

Resources