4

I want to execute a simple Unix command from my Java servlet: what I need to do is a simple echo write to file like this one:

echo HELLO > myfile.txt

What I'm doing in my servlet is:

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ServletAutorecovery extends HttpServlet {
    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            ProcessBuilder pb = new ProcessBuilder("/usr/bin/bash", "-c", "echo HELLO > ../webapps/test/myfile.txt");
            pb.start();
        } finally { 
        out.close();
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        processRequest(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        processRequest(request, response);
    }
}

My problem is: this code section is not giving me any errors, but nothing happens. After I executed my servlet, the file has not being created, and of course, nothing is written in it.

What am I doing wrong?

EDIT1: added full path to pb command.

EDIT2: bash is in the path /usr/bin/bash, 100% sure of it.

EDIT3: added SSCCE.

abierto
  • 1,447
  • 7
  • 29
  • 57
  • 1
    where do you expect the myfile.txt to be ? where do you look/search ? try to write the file to a path you know. for example /tmp/myfile.txt – MrSimpleMind Feb 05 '13 at 08:47
  • I am actually writing it in my project folder, `../webapps/test/myfile.txt`. – abierto Feb 05 '13 at 08:50
  • Did you try with Runtime ? Runtime.getRuntime().exec("echo HELLO > myfile.txt"); – MrSimpleMind Feb 05 '13 at 08:52
  • Tried this: ProcessBuilder pb = new ProcessBuilder("bash", "-c", "echo HELLO > myfile.txt"); – MrSimpleMind Feb 05 '13 at 08:55
  • I usually use `ProcessBuilder` but in a different way: in the third parameter I normally add my script path, for example `scripts\myscript.sh`, and in this particular case I think it's dull to create a script for a command like this one... – abierto Feb 05 '13 at 08:58
  • What are you doing with the process's standard error stream and exit status? Do either of those give any indication of the problem? – Tom Anderson Feb 05 '13 at 09:01
  • 1
    For better help sooner, post an [SSCCE](http://sscce.org/). – Andrew Thompson Feb 05 '13 at 09:14
  • @TomAnderson I actually don't know how to manage the process' standard error stream and exit status, so I'm not able to answer to your question... – abierto Feb 05 '13 at 09:39
  • @abierto: Ian Roberts's answer sends standard error to Java's standard output, so that's a start. You can get the exit status from the return value of `waitFor`. – Tom Anderson Feb 05 '13 at 14:45
  • What working code did you come up with finally because I tried everything here http://stackoverflow.com/questions/24883364/giving-file-permissions-and-running-processbuilder-in-java-servlets I'm still stuck. – Akshay Hazari Jul 22 '14 at 11:08

3 Answers3

8

First, are you sure bash is definitely at /usr/bin? Second, you probably need to tell the ProcessBuilder what directory it should use as the cwd when running the process, otherwise it will try and create myfile.txt in whatever is the current directory of the servlet container, typically somewhere you don't have write access. And thirdly, when you run a process from java the output of the process is passed back to java via input streams on the process object, it doesn't go straight to stdout, so you need to read the streams to see the result

ProcessBuilder pb = new ProcessBuilder("/usr/bin/bash", "-c", "echo HELLO > myfile.txt");
pb.directory(...);
pb.redirectErrorStream(true);
Process p = pb.start();
IOUtils.copy(p.getInputStream(), System.out);
p.waitFor();
Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
  • Sorry, but in `pb.directory(...)` what directory should I write? The output one, where I want `myfile.txt` to be in? I also have problem using `IOUtils`, even if I added `import java.lang.Object;`. – abierto Feb 05 '13 at 09:05
  • .. 4) You might as well break `"echo HELLO > myfile.txt"` into separate arguments as well. 5) I'd heard that pipes do not work in the same way when used via Java. – Andrew Thompson Feb 05 '13 at 09:13
  • @abierto the directory that you want to be the current dir of the bash process, which is what the relative path in your redirection will be resolved against. And IOUtils is part of Apache commons-io, I use it as a convenient way to copy an input stream to an output stream but you can do it by hand if you prefer. – Ian Roberts Feb 05 '13 at 10:04
  • @AndrewThompson in this case no, you do want the whole echo-and-redirect as one argument because it's intended to be interpreted by `bash`, not by Java. Without bash in there you can't do redirects using `>` but in Java 7 at least you can send output directly to a file using the new methods added to ProcessBuilder in 7. – Ian Roberts Feb 05 '13 at 10:07
  • @IanRoberts What if I'm using Tomcat then? Is it a problem? – abierto Feb 05 '13 at 10:18
  • @abierto no, no problem. You can get a suitable location using `servletContext.getRealPath` (if you want to use a directory in the unpacked webapp) or `(File)servletContext.getAttribute(ServletContext.TEMPDIR)` to access the private temporary directory that the container provides to every web application. – Ian Roberts Feb 05 '13 at 10:37
3
String echo = "echo 'hello' > myfile.txt";
ProcessBuilder pb = new ProcessBuilder("/usr/bin/bash", "-c", echo);
pb.start();
MrSimpleMind
  • 7,890
  • 3
  • 40
  • 45
0

Check your error handling; you're probably swallowing an exception somewhere because there is no bash in /usr/bin, so you're getting a "file not found" exception (or similar).

Try "/bin/bash" instead. The rest should work.

Also note that relative paths won't work after you deploy you app since it will be relative to the process running the Java VM which isn't what you expect, want or could use. Ask your ServletContext for a path with getRealPath()

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820