Update 3
Progress! I wrote a CMD script which takes three parameters:
- the absolute path to
sqlite3cipher.exe
- the absolute path to
testdb
- 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.exe
resides inC:\Program Files\Apache Software Foundation\bin\
an empty file
testdb
resides inC:\Program Files\Apache Software Foundation\pages\dbdir\
a file containing SQL statements
statements.sql
resides in the same folder astestdb
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'sInputStream
andOutputStream
doesn't work:Access denied
I'm not sure what to do next, so I sincerely hope someone can help me.