In my XPages application I have to call curl commands
The code is simple:
public static InputStream executeCurlCommand(String finalCurlCommand) throws IOException
{
return Runtime.getRuntime().exec(finalCurlCommand).getInputStream();
}
However, it seems that the command isn't executed at all.
When I execute the following (in Linux terminal):
curl -k -i -X GET https://someHost/fws/api/esia/login?redirectUrl=https://redirectHost/sed/gecho/2019/gecho_apps_2019.nsf/Nav2.xsp
I get the output.
But whenever I execute it with Java nothing works. getInputStream() is empty.
At the same time, if I execute this code on my local Windows machine everything is fine. Same goes for the command executed from cmd.
But in XPages there are no exceptions, no errors, just nothing.
Is there a way to debug it?
UPD
If I change the command to something as simple as ls -la everything works fine :x
OK, guys, I figured it out. The thing was that I passed the full command to the shell. It doesn't work in UNUX.
So
You should NEVER use
public static InputStream executeCurlCommand(String finalCurlCommand) throws IOException
{
return Runtime.getRuntime().exec(finalCurlCommand).getInputStream();
}
In UNIX systems. Use ProcessBuilder instead
For example:
ProcessBuilder pb = new ProcessBuilder(
"curl",
"-k",
"-i",
"-X",
"GET",
"http://host.com");
pb.redirectErrorStream(true);
Process p = pb.start();
InputStream is = p.getInputStream();
Related
try{
//String[] cmd = new String[]{"/bin/sh", "send.sh"};
//Process pr = Runtime.getRuntime().exec(cmd); //nothing happens
//Process p = Runtime.getRuntime().exec("send.sh"); //File not found
//Process p = Runtime.getRuntime().exec("bash send.sh"); //nothing happens
// ProcessBuilder pb = new ProcessBuilder("bash","send.sh");
// Process p = pb.start(); //nothing happens
}
catch(Throwable t)
{
t.printStackTrace();
}
With this code I am trying to start a simple bash file which is located in the directory of the program. The code of the bash file works when I start it with shell or by simply executing it. The code of the bash file works.
I've tried every option but they are all not working. I've commented what happens in each case. I don't understand that it don't find the file because the bash file is located in the same directory.
You don't see an output for two reasons:
You don't wait for a process to finish
You don't redirect it's output to the same console that your Java process runs
And, probably, you need to use a command like /bin/bash -c path/to/your/file.sh. Note that -c flag.
IMHO, the best way to craft and execute external processes in Java is java.lang.ProcessBuilder.
Supposing that you have your sh file somewhere the in resources directory, here is an example main class:
public class App {
public static void main(String[] args) throws Exception {
final ProcessBuilder processBuilder = new ProcessBuilder("/bin/bash", "-c", App.class.getResource("/46964369.sh").getPath());
processBuilder.redirectInput(Redirect.INHERIT);
processBuilder.redirectOutput(Redirect.INHERIT);
processBuilder.redirectError(Redirect.INHERIT);
processBuilder.start().waitFor();
}
}
Note that I redirect process's streams with redirect* methods. Redirect.INHERIT redirects stream to the corresponding stream of the JVM instance. It works both for input and output streams. Finally, I am waiting for a process to finish with waitFor() method. In fact, you can do more, like capturing the output into a string, providing input from a string or running the process asynchronously, but this is a minimal example.
If you store your sh file in another place, you must update path-related logic.
Take a look at the complete example here. It's a Gradle project, and you can use ./gradlew run to execute it:
$ ./gradlew run
:compileJava
:processResources
:classes
:run
Hello, world!
BUILD SUCCESSFUL in 4s
3 actionable tasks: 3 executed
I have a seemingly trivial problem: I want to start a terminal from a java process and give the terminal one or two commands.
I have a simple example code, that works perfectly on windows with CMD. But I have not been able to achieve the same exact behavior on a Linux nor a Mac OS machine.
I am aware, that the command needs to be changed, but unfortunately I have not been able to pass a string of arguments to a terminal on Mac.
Here the working code for windows:
import java.lang.ProcessBuilder.Redirect;
public class ExecTest {
public static void main(String[] args){
String cmd = "cmd /c start cmd.exe /K \"echo hello && echo bye\"";
try {
Process p = Runtime.getRuntime().exec(cmd);
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
}
On lubuntu I have been able to create a terminal with this command:
lxterminal -l -e 'echo hello && echo bye && read'
But this only works if called by a terminal but not with the java process.
.
TLDR: What is the Linux and Mac equivalent of this command:
cmd /c start cmd.exe /K \"echo hello && echo bye\"
I suggest you use ProcessBuilder to benefit from easier output redirection and ability to consume it without using threads, and also pass the command as a String[] instead of flat String to be able to support the various wrapping approaches. If you prefer to stick with Runtime.exec(), it also supports String[], but the example below uses ProcessBuilder.
static int executeInTerminal(String command) throws IOException, InterruptedException {
final String[] wrappedCommand;
if (isWindows) {
wrappedCommand = new String[]{ "cmd", "/c", "start", "/wait", "cmd.exe", "/K", command };
}
else if (isLinux) {
wrappedCommand = new String[]{ "xterm", "-e", "bash", "-c", command};
}
else if (isMac) {
wrappedCommand = new String[]{"osascript",
"-e", "tell application \"Terminal\" to activate",
"-e", "tell application \"Terminal\" to do script \"" + command + ";exit\""};
}
else {
throw new RuntimeException("Unsupported OS ☹");
}
Process process = new ProcessBuilder(wrappedCommand)
.redirectErrorStream(true)
.start();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line); // Your superior logging approach here
}
}
return process.waitFor();
}
Tested on 3 operating systems. The 3 booleans is{Windows|Linux|Mac} are unexplained here as OS detection is another topic, so for the example I kept it simple and handled out of the method.
The ProcessBuilder is configured to redirect stderr to stdout, so that a single stream needs to be read. That stream is then read and logged, because you must consume stderr and stdout, in case the Terminal itself prints stuff (not the command you are running in the Terminal, this is about what the Terminal itself prints), if it prints too much you risk getting the process to block indefinitely, waiting for the buffer to be read. See this nice answer.
For macOS if the command you pass is always a single executable script, you could also use {"open", "-a", "Terminal", command}, but that will not work with echo hello && echo bye. Similarly you could use {"/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal", command}, which would get you a second instance of the app running, but with same limitations.
Final note: you can offer a reasonable basic implementation, but you'll probably need to make it configurable to allow alternative terminal applications (especially on Linux where it varies a lot).
String[] cmd = {"echo", "hello", "&&", "echo", "hi"};
Runtime.getRuntime().exec(cmd);
There are multiple ways to do this, but this should work for you. Executables on Mac should run automatically in Terminal.
Possibly similar to: How To Run Mac OS Terminal Commands From Java (Using Runtime?)
If anything they have a few methods for running scripts in the terminal.
I am running the below query through Java on a Postgres DB using psql:
psql.exe -U <user> -w -h <host> -d <db_name> -a -f <file> 2> "<path_to_file>\psql.log"
Initially, for quite some time the java program did create the file. Then I ran into another problem, that it was not overwriting the log file. So i used file.delete() function after every time this log file got created via java.
Now, Java is not even creating the log file for some reason. If I run the above manually in command prompt, it runs absolutely fine, but not via java code. I can see this command getting run in the java log, but it does not create the log file even when i have removed the file.delete() function
I researched a lot on it but could not find any solution. Any help would be highly appreciated.
its a long code..so i will tell you the relevant part.
I am calling a function from a thread. Code is below for that function:
public static void SaveACopyfileToServer(int auditid,String filepath,String fname,String tb_name,String plpgsql_path) throws Exception
{
Map<String, String> env = System.getenv();
String plpgsql = "\""+plpgsql_path+"\" -U "+env.get("PG_USER")+" -w -h "+env.get("PG_HOST")+" -d "+env.get("PG_DB")+" -a -f "+"\""+filepath+"copy_"+tb_name+auditid+".sql\" 2> \"C:\\ER\\ETL\\logs\\psql.log\"";
System.out.println(plpgsql);
Process p = Runtime.getRuntime().exec(plpgsql);
p.getOutputStream().close();
p.waitFor();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
Calendar cal10 = Calendar.getInstance();
System.out.println("Data loaded for "+tb_name+auditid+" at "+sdf.format(cal10.getTime()));
}
I have tried the following codes also:
String plpgsql = "\""+plpgsql_path+"\" -U "+env.get("PG_USER")+" -w -h "+env.get("PG_HOST")+" -d "+env.get("PG_DB")+" -a -f "+"\""+filepath+"copy_"+tb_name+auditid+".sql\" 2> \"C:\\ER\\ETL\\psql_" +auditid +".log\"";
ProcessBuilder pb = new ProcessBuilder("C:/Windows/System32/cmd.exe",plpgsql);
System.out.println(plpgsql);
Process p =pb.start();
p.getOutputStream().close();
p.waitFor();
and
String filename = filepath+"copy_"+tb_name+auditid+".sql";
String psqllog_file = "C:\\ER\\ETL\\logs\\psql_" +auditid +".log";
Process p = Runtime.getRuntime().exec(new String [] { plpgsql_path,
"-U",
env.get("PG_USER"),
"-w",
"-h",
env.get("PG_HOST"),
"-d",
env.get("PG_DB"),
"-a",
"-f",
filename,
"2>",
psqllog_file });
If i run it through a batch file, then I am getting and error that Windows cannot find -U using the below code in java
String plpgsql = "\""+plpgsql_path+"\" -U "+env.get("PG_USER")+" -w -h "+env.get("PG_HOST")+" -d "+env.get("PG_DB")+" -a -f "+"\""+filepath+"copy_"+tb_name+auditid+".sql\" 2> \"C:\\ER\\ETL\\psql_" +auditid +".log\"";
ProcessBuilder pb = new ProcessBuilder("C:/Windows/System32/cmd.exe","/c","start",plpgsql);
System.out.println(plpgsql);
Process p =pb.start();
p.getOutputStream().close();
p.waitFor();
I am stuck on this for a good amount of time now. Any help would be really really appreciated.
P.S: I am absolutely new to Stackoverflow, still trying to learn how to reply to comments to notify the other person, cz i think my replies are not being sent to the mailbox of others.
The following method starts the cmd in Windows and it takes a parameter of the command which need to be run.
I have tested this method using the following commands: net users and it worked fine and it printed the users accounts. but if I run the dir command I get the following error:
java.io.IOEXception:
Cannot run program "dir": CreateProcess error=2, The system cannot find the file specified (in java.lang.ProcessBuilder)
Code :
private String commandOutPut;
public void startCommandLine(String s) throws IOException{
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(s); // you might need the full path
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String commandOutPut;
while ((commandOutPut = br.readLine()) != null) {
this.commandOutPut = this.commandOutPut + "\n" + commandOutPut;
}
System.out.println(this.commandOutPut);
}
Well, obviously, your method does not start cmd. How did you get this notion?
The net command is a standalone command so it runs just fine, but the dir command is not standalone, it is an internal command of cmd.exe, so you cannot run it without launching cmd.exe to execute it.
To get it to work you will have to pass not dir but cmd.exe /c dir or something like that.
Don't know if this perception can help you. But, seems that "net users" are recognized as Windows command, since "Execute" dialog can run it.
But, for some reason, the "dir" command aren't. When try to run, Windows responds that command was not found.
Additionaly, I tried run Command with inline arguments too, but the arguments are simply ignored. (sorry for bad english)
My best guess is that this is because "net" is a real executable (there is a file WINDIR\System32\net.exe"), while "dir" is a builtin command of the command interpreter - it has no executable and is directly executed within cmd.exe.
Howevever you may get around this be invoking "dir" command inside the cmd process. The syntax - as per Microsoft docs - is:
cmd /c dir
There are also some related answers on the site:
How to execute cmd commands via Java
Run cmd commands through java
You can use the following code for this
import java.io.*;
public class demo
{
public static void main(String args[])
{
try
{
Process pro=Runtime.getRuntime().exec("cmd /c dir");
pro.waitFor();
BufferedReader redr=new BufferedReader(
new InputStreamReader(pro.getInputStream())
);
String ln;
while((ln = redr.readLine()) != null)
{
System.out.println(ln);
}
}
catch(Exception e) {}
System.out.println("Done");
}
}
I have a command like
cp -R Folder1/* Folder2/
or
rm -r /images/*.gif
It is not working to I try to run a sample program through Java
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(cmd);
return proc.waitFor();
What am i doing wrong?
When you run a process, Java creates three outputs, the exit code, the STDOUT, and the STDERR. A good program running an external process, will check all three.
You are just waiting for the process to terminate, and return the exit code.
An easy way to see what's happening is to 'inherit' the STDOUT/STDERR streams using a ProcessBuilder:
ProcessBuilder pbuilder = new ProcessBuilder("cp", "-R", "Folder1/*", "Folder2/");
pbuilder.inheritIO();
Process proc = pbuilder.start();
return proc.waitFor();
you will get a better idea of what went wrong.
Note also that I used separate String arguments for the command. This helps with ensuring the arguments are passed right to the underlying process.
Try like this:
List<String> cmd = new ArrayList<String>();
cmd.add("bash");
cmd.add("-c");
cmd.add(" rm -rf *.txt");
add the above list in ProcessBuilder then execute.