Link to home
Start Free TrialLog in
Avatar of numtech
numtech

asked on

Java 6 exec, escaping special characters within ProcessBuilder.command(String[])

Hello,

I use the commandline from my main java app to execute another 'mailer.jar' app.
One of the argument is the mail content that can contains any character including \n, \r, ", '...

I thought using
ProcessBuilder.command(String[])

Open in new window

instead of  
ProcessBuilder.command(String)

Open in new window

would prevent the ProcessBuilder.exec from having troubles with splitting the command line, but it does not work.

If my command is a String[] array containing:
String[] cmd=new String[]{"java",
"-jar",
"mailer.jar"
"smtp.test.org",
"test@test.org",
"test@test.org",
"my subject ...",
"my content with carriage returns and other strange characters"};

Open in new window


I will obtain as the String[] args of the main method of my mailer.jar
String[] args=new String[]{
"smtp.test.org",
"test@test.org",
"test@test.org",
"my subject ...",
"my content with",
"carriage returns and",
"other strange characters",
...};

Open in new window


What should I do?
I would prefer not to use Apache Commons Exec because we have developped our own low level lib to handle multi threading and asynchronous exec...

Thanks for your help!

Regards,

Renaud
Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

For the final array element, does a simple string work?
Avatar of numtech
numtech

ASKER

Hello,

Yes it works perfectly with simple String content in the cmd.
But if one of the String in the cmd contains any " or ' character, it would be splitted in the final array.

I did this, and it solved the problem under windows, but I think it is really not a good idea to go into this because I will have to test it on every plaform/version (and I am targetting Win, MacOS and Linux).
private String[] escapeCMD(String[] commandLine) {
        String[] escapedCmd = Arrays.copyOf(commandLine, commandLine.length);
        for (int i = 0; i < escapedCmd.length; i++) {
            escapedCmd[i] = escapedCmd[i].replace("\"", "\\\"").replace("'", "\\'");
        }
        return escapedCmd;
    }

Open in new window

Avatar of numtech

ASKER

What a weird work around when I am not supposed to know the underlying OS specifics!
Would it not be easier just to do something like the following?
Mailer.main(args);

Open in new window

Avatar of numtech

ASKER

Not an option, mailer.jar cannot be loaded inside the main JVM. We have designed this to obtain a low coupling between different components that will evolve in a independant way.
Our designt is:
boot.jar
update.jar
runtime.jar
mailer.jar
The command line prototype and exit code of each main method is defined as the low level interface between application components.
We tryed the classloader approch in the past but we gave up after a while because of an memory leak issue in the unloading process (and we do not want to re-invent OSGI...)
I'm afraid to say that it looks like what you did in http:#35769647 IS going to have to be tested for each of your target platforms then
> "my content with",
> "carriage returns and",
> "other strange characters",

they are separate arguments. Are you expecting them to be treated as one?
And how are you receiving the special characters?
Avatar of numtech

ASKER

To illustrate I can show you the real example
This is what is sent as the commandline arguments:

java

-jar

mailer.jar

zimbra.xxxx.fr

ryyyyy@xxxx.fr

ryyyyy@xxxx.fr

NUMAILER[BOOT] RollbackSuccesfull -- satId=40436aff506a72aae8552438d493238b22d67581, path=C:/Program Files/Xxxx/Backup4, companyId=2

The install add to be rolled back.;Rollback reason was: XxxxException : The process terminated in error after 12ms, IExecutionResult[exitCode=1, stdOut="", stdErr=""] >>>> STACK TRACE :[fr.xxxx.boot.RuntimeBooter.bootRuntime(RuntimeBooter.java:125), fr.xxxx.boot.RuntimeBooter.run(RuntimeBooter.java:51), java.lang.Thread.run(Unknown Source)]

 ..\log, 25]

Open in new window


This is what I receive as the main args:
Arg[0] : zimbra.xxxx.fr

Arg[1] : ryyyyy@xxxx.fr

Arg[2] : ryyyyy@xxxx.fr

Arg[3] : NUMAILER[BOOT] RollbackSuccesfull -- satId=40436aff506a72aae8552438d493238b22d67581, path=C:/Program Files/Xxxx/Backup4, companyId=2

Arg[4] : The install add to be rolled back.;Rollback reason was: XxxxException : The process terminated in error after 17ms, IExecutionResult[exitCode=1, stdOut=",

Arg[5] : stdErr=Invalid or corrupt jarfile runtime-4.1.1.jar ;]

Arg[6] : >>>>

Arg[7] : STACK

Arg[8] : TRACE

Arg[9] : :[fr.xxxx.boot.RuntimeBooter.bootRuntime(RuntimeBooter.java:125),

Arg[10] : fr.xxxx.boot.RuntimeBooter.run(RuntimeBooter.java:51),

Arg[11] : java.lang.Thread.run(Unknown

Arg[12] : Source)] ..\log 25

Open in new window

Have you tested it on the other platforms?
Runtime.exec() used to be terrible at handling the command line, one would have hoped they had fixed it up for ProcessBuilder
ASKER CERTIFIED SOLUTION
Avatar of numtech
numtech

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Issue is that you don't know what shell (if any) is involved.
Avatar of numtech

ASKER

If found myself the solution by exploring apache commons exec sources.