3

Update 3

Progress! I wrote a CMD script which takes three parameters:

  1. the absolute path to sqlite3cipher.exe
  2. the absolute path to testdb
  3. the absolute path to statements.sql

All the script does is execute the command

path\to\sqlite3cipher.exe path\to\testdb < path\to\statements.sql

This script is being called from within my Java code. And it works like a charm! At least with all the files residing in a folder in my home directory. Having the files in a subfolder of C:\Program Files doesn't work so there might be an issue with privileges which I have to work out with the administrator.


Update 2

I've created a dummy SQL file in my home directory. The code should now read the SQL file from and write the database file to my home directory.

Again omitting cmd /c in the command reads and writes the first line from the SQL file but stops after that (execution of the whole program comes to a halt). Executing the command including cmd /c reads and writes every single line of the SQL file but once EOF is reached (in which case nothing will be written of course) I again get the Access denied message.


Update 1

Taking skarist's comment into account the following command (omitting cmd /c as it isn't mandatory for executing exe-files after all)

String cmdForRuntime = "\"" + prog.getAbsolutePath() + "\" \"" + fDb.getAbsolutePath() + "\"";

which spelled out looks like

"C:\Program Files\Apache Software Foundation\bin\sqlite3cipher.exe" "C:\Program Files\Apache Software Foundation\pages\dbdir\testdb"

doesn't produce any output. Also the code stops executing, so I assume there's something wrong with the I/O?


Problem

In order to encode a database I want to execute sqlite3cipher.exe on the command line in Java.

I ran into quite some diverging results for various approaches. At first the setup (notice that this is a testing environment where right now nothing can be changed regarding where files are located):

  • sqlite3cipher.exeresides in C:\Program Files\Apache Software Foundation\bin\

  • an empty file testdbresides in C:\Program Files\Apache Software Foundation\pages\dbdir\

  • a file containing SQL statements statements.sql resides in the same folder as testdb does

Executing sqlite3cipher.exe -help on the command line prints the usage. Here is an exerpt:

C:\Program Files\Apache Software Foundation\bin\sqlite3cipher.exe [OPTIONS] FILENAME [SQL] 
FILENAME is the name of an SQLite database. A new database is created if the file does not previously exist.

Executing "C:\Program Files\Apache Software Foundation\bin\sqlite3cipher.exe" "C:\Program Files\Apache Software Foundation\pages\dbdir\testdb" < "C:\Program Files\Apache Software Foundation\pages\dbdir\statements.sql" on the command line has the desired effect: testdb now is an encrypted database according to the SQL statements that were put in through the statements.sql.

Now in Java my code looks like this:

String cmdForRuntime = "cmd /C \"" + prog.getAbsolutePath() + "\" \"" + fDb.getAbsolutePath() + "\" < \"" + fSqlScript.getAbsolutePath() + "\"";
Process process = Runtime.getRuntime().exec( cmdForRuntime );

BufferedReader stdIn = new BufferedReader(new InputStreamReader(process.getInputStream()));
String s;

while ((s = stdIn.readLine()) != null) {
    logger.info("OUTPUT FROM PROG: " + s);
}

if (process.waitFor() == 0) {
    logger.info("Process finished");
}
else {
    logger.error("Error!");

    // Get input streams
    BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));

    System.out.println("Standard error: ");
    while ((s = stdError.readLine()) != null) {
        System.out.println(s);
    }
}

where prog , fDb and fSqlScript are of type File and represent sqlite3cipher.exe, testdb and statements.sql respectively.

I'm not well versed with Process.execute() but what I gathered from reading about it is that cmd /c as part of the command is required on Windows (the test server is running Windows 8) in order to start the command interpreter.

On execution this code returns Access denied on the command line (via the process's streams). Executing this exact command (including cmd /c) on the command line directly returns the same result: Access denied. Even if I start the command line as administrator.

Leaving cmd /c out of the command (because it had worked without on the command line before), I get the message that there are too many options. On further investigation, I learned that this problem is caused by the < operator, used in the command to redirect the input. The exec() method won't recognize < as an operator and it was suggested to use the process's OutputStream. So I changed my code to look like this:

String cmdForRuntime = "cmd /c \"" + prog.getAbsolutePath() + "\" \"" + fDb.getAbsolutePath() + "\"";
Process process = Runtime.getRuntime().exec( cmdForRuntime );

BufferedWriter bufferedwriter = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
BufferedReader br = new BufferedReader(new FileReader(fSqlScript.getAbsolutePath()));

BufferedReader stdIn = new BufferedReader(new InputStreamReader(process.getInputStream()));
String s;
String fileInput = br.readLine();

while (fileInput != null) {

    bufferedwriter.write(fileInput);

    while ((s = stdIn.readLine()) != null) {
        logger.info("OUTPUT FROM PROG: " + s);
    }

    fileInput = br.readLine();
}

if (process.waitFor() == 0) {
    logger.info("Process finished");
}
else {
    logger.error("Error!");

    // Get input streams
    BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));

    System.out.println("Standard error: ");
    while ((s = stdError.readLine()) != null) {
        System.out.println(s);
    }
}

As I understand it is important to read what the external program prints out so it won't block or stop working. That's why I read a line from statements.sql (through the BufferedReader br) and write it to the process's OutputStream. Then I read what the program will print out (logging that output). If there is nothing (left) to read I read the next line from the statements.sql. This will be repeated until there is nothing left to read from said file.

If I execute this code, my logger will log the following:

INFO  Executing command: cmd /c "C:\Program Files\Apache Software Foundation\bin\sqlite3cipher.exe" "C:\Program Files\Apache Software Foundation\pages\dbdir\testdb"
ERROR Error!
Standard error: 
Access denied

In this case, the code at least terminates resulting in an empty file testdb. In another approach I left cmd in the command but skipped the /c resulting in following output:

INFO  Executing command: cmd "C:\Program Files\Apache Software Foundation\bin\sqlite3cipher.exe" "C:\Program Files\Apache Software Foundation\pages\dbdir\testdb"
INFO  OUTPUT FROM PROG: Microsoft Windows [Version 6.3.9600]
INFO  OUTPUT FROM PROG: (c) 2013 Microsoft Corporation. Alle Rechte vorbehalten.
INFO  OUTPUT FROM PROG: 

The output is the same you get when you type cmd on the command line. And that's where execution stops.


tl;dr

  • execution on command line directly works like a charm
  • exact same command executed from Java doesn't work
  • command prepended by cmd /c (as it apparently should be) and usage of the process's InputStream and OutputStream doesn't work: Access denied

I'm not sure what to do next, so I sincerely hope someone can help me.

Community
  • 1
  • 1
Christian
  • 707
  • 1
  • 7
  • 15
  • Why do you think you need the "cmd /C". You don't need the command interperted to execute .exe files. Check this link here: http://stackoverflow.com/questions/13991007/execute-external-program-in-java – skarist Jul 10 '14 at 11:48
  • I've updated the post. I've omitted `cmd /c` in the command resulting with the execution to stop. No output is read. – Christian Jul 10 '14 at 12:36
  • seems like `UAC` issue! is ur program running in `Elevated` mode? – Parimal Raj Jul 10 '14 at 12:36
  • I've started Tomcat (who then executes said code) in elevated mode. Once with `cmd /c` in the command (again `Access denied`), once without (no output at all and code execution stops). – Christian Jul 10 '14 at 12:54
  • Any antivirus running? Try to disable. Can you try in safe mode (I assumed you are using windows) – hutingung Jul 10 '14 at 14:40
  • As far as I can see there is no anti virus software running. Unfortunately there is no way for me to boot the machine in safe mode. – Christian Jul 10 '14 at 15:09
  • Your output logs appear to be inconsistent? In your first one it references locations in `C:\Program Files`, whereas in your second it's `C:\Programme`. Also in your first one there is a space where there probably shouldn't be `C:\ Program Files\Apache Software Foundation\pages\dbdir\testdb` – Woodham Jul 10 '14 at 15:34
  • I'm sorry for that. I'm working on a German version of Windows so `Program Files` would be `Programme`. Just translated it for this post, so everyone would be on the same page that this is a system folder. What you are actually referring to are in fact my weak copy and editing skills. I've updated the code accordingly. – Christian Jul 11 '14 at 05:38

2 Answers2

1

So I solved the problem with the workaround I mentioned in update 3.

My Java code now looks like this (besides the cmdForRuntime variable it should be pretty much the same code as in the beginning, when I asked this question):

try {
        File fDb = new File(this.inputDirectory + File.separator + dbName);

        String cmdForRuntime =  "\"" + cmdScript.getAbsolutePath() + "\"" + " " +       // the script to be executed 
                                "\"" + prog.getAbsolutePath() + "\"" + " " +            // sqlite3cipher.exe
                                "\"" + fDb.getAbsolutePath() + "\"" + " " +             // testdb
                                "\"" + fSqlScript.getAbsolutePath() + "\"";             // statements.sql

        logger.info("Executing command: " + cmdForRuntime );

        Process process = Runtime.getRuntime().exec( cmdForRuntime );

        BufferedReader stdIn = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String s;

        while ((s = stdIn.readLine()) != null) {
            logger.info("OUTPUT FROM PROG: " + s);
        }

        if (process.waitFor() == 0) {
            logger.info("Process finished");
        }
        else {
            logger.error("!!!! error !!!!");
            // Get input streams
            BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));

            System.out.println("Standard error: ");
            while ((s = stdError.readLine()) != null) {
                System.out.println(s);
            }
        }
    }
    catch(Exception ioe) {
        ioe.printStackTrace();
    }       

cmdForRuntime will read something like this (I will omit the paths, the only add to the confusion):

"SQLiteCipher.cmd" "sqlite3cipher.exe" "testdb" "statements.sql"

The content of the script looks like this:

%1 %2 < %3

This will invoke the sqlite3cipher.exe with the parameter testdb and a redirected input coming from statements.sql.

Critical is that the location of the script as well as the location of the exe is not a system folder.

If all these criteria are met everything works like a charm.

Christian
  • 707
  • 1
  • 7
  • 15
0

You are having User Account Control issue.

It seems like the program is running in non-elevated mode, to make changes to system directories the application need to run in elevated mode.

Running you program in elevated mode should solve the problem.

Parimal Raj
  • 20,189
  • 9
  • 73
  • 110
  • I'm afraid not. I've started Tomcat in elevated mode (`Run as administrator`) with the exact same outcome. – Christian Jul 10 '14 at 13:18