Better way of opening a Document from Java? - java

I've been using the following code to open Office Documents, PDF, etc. on my windows machines using Java and it's working fine, except for some reason when a filename has embedded it within it multiple contiguous spaces like "File[SPACE][SPACE]Test.doc".
How can I make this work? I'm not averse to canning the whole piece of code... but I'd rather not replace it with a third party library that calls JNI.
public static void openDocument(String path) throws IOException {
// Make forward slashes backslashes (for windows)
// Double quote any path segments with spaces in them
path = path.replace("/", "\\").replaceAll(
"\\\\([^\\\\\\\\\"]* [^\\\\\\\\\"]*)", "\\\\\\\"$1\"");
String command = "C:\\Windows\\System32\\cmd.exe /c start " + path + "";
Runtime.getRuntime().exec(command);
}
EDIT: When I run it with the errant file windows complains about finding the file. But... when I run the command line directly from the command line it runs just fine.

If you are using Java 6 you can just use the open method of java.awt.Desktop to launch the file using the default application for the current platform.

Not sure if this will help you much... I use java 1.5+'s ProcessBuilder to launch external shell scripts in a java program. Basically I do the following: ( although this may not apply because you don't want to capture the commands output; you actually wanna fire up the document - but, maybe this will spark something that you can use )
List<String> command = new ArrayList<String>();
command.add(someExecutable);
command.add(someArguemnt0);
command.add(someArgument1);
command.add(someArgument2);
ProcessBuilder builder = new ProcessBuilder(command);
try {
final Process process = builder.start();
...
} catch (IOException ioe) {}

The issue may be the "start" command you are using, rather than your file name parsing. For example, this seems to work well on my WinXP machine (using JDK 1.5)
import java.io.IOException;
import java.io.File;
public class test {
public static void openDocument(String path) throws IOException {
path = "\"" + path + "\"";
File f = new File( path );
String command = "C:\\Windows\\System32\\cmd.exe /c " + f.getPath() + "";
Runtime.getRuntime().exec(command);
}
public static void main( String[] argv ) {
test thisApp = new test();
try {
thisApp.openDocument( "c:\\so\\My Doc.doc");
}
catch( IOException e ) {
e.printStackTrace();
}
}
}

Related

Open a file with its associated application and extra command line parameters

I would like to open a file with its associated application and a couple of command line parameters.
E.g. the_app.exe -someoption file.app, the_app.exe being the associated application for opening the .app files.
I was looking to the Desktop class with the hope to find either
a Desktop.getDesktop().open(file,options), or
a Desktop.getDesktop().getOpenFileHandler(file) and then build upon that with Process and ProcessBuilder
Neither do exist. Any other suggestion ?
In java you can use Runtime to run programs.
Runtime.getRuntime().exec("program");
You can also use "cmd" to run command-line commands.
Then, if option parameters weren't needed, you could simply use 'start' command.
//this will open notepad in my pc
Process pr = Runtime.getRuntime().exec("cmd /c start test.txt");
If you really need to pass commands you can use some auxiliar commands and build your own command-line:
With 'assoc' you retrieve the filetype
With 'ftype' you retrieve the application for a filetype
Then:
private static String command(Runtime rt, String command) throws IOException {
Process pr = rt.exec("cmd /c " + command);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(pr.getInputStream()))){
return reader.readLine();
}
}
public static void main(String[] args) throws IOException {
//without parameters is an easy job
Process pr = Runtime.getRuntime().exec("cmd /c start test.txt");
//with parameters things get messy
Runtime rt = Runtime.getRuntime();
String out1 = command(rt, "assoc .txt");
System.out.println(out1);
String assoc = out1.split("=")[1];
String out2 = command(rt, "ftype " + assoc);
System.out.println(out2);
String app = out2.split("=")[1].split(" ")[0]; //error prone, consider using regex
command(rt, app + " /P test.txt"); //This will print (/P) the file using notepad
}
Of course, none of these are portable at all.
For more info:
Running Command Line in Java
Best way to get file type association in Windows 10 from command line?
https://www.addictivetips.com/windows-tips/file-app-association-command-prompt-windows-10/
How to open file with default application in cmd?

Cannot run python script from java jar

While working in IntelliJ everything worked but after I built jar it stopped. At first, it was just me forgetting to put it in jar build config, but now after making sure it is there, I still can't run it. These are ways I try:
InputStream script = mainView.class.getResourceAsStream("vizualize3D.py");
Process process = new ProcessBuilder("python3", "-").start() ;
Process p1 = Runtime.getRuntime().exec("python3 " + script);
Runtime rt = Runtime.getRuntime();
Process pr = rt.exec("python3 " + mainView.class.getResourceAsStream("vizualize3D.py"));
None of theme work despite having it in resources. I also tried to specify path to it in IntelliJ project and it works but only when run from IntelliJ after I start it from jar it doesn't.
Edit1:
For people that didn't understand py file is in jar file
None of the options involving you trying to execute "python3 "+script, and equivalents, will work. script is an InputStream, not a path on the file system, so simply concatenating it with a String will not give you anything meaningful. Additionally, since your script is not in its own file, and there's no simple way for the python interpreter to extract it, simply invoking it like this won't work.
What you can do, however, is to execute
python3 -
The - option here (at least on BSD-like systems) means "read from standard input, and interpret it as a script". Then, on the Java side, you can read the jar-packaged resource as a stream and pipe it to the python process's standard input.
For details on choosing the correct path for the resource, see How do I determine the correct path for FXML files, CSS files, Images, and other resources needed by my JavaFX Application?.
The following, in which the script is simply placed in the same package as the class, works for me:
PythonRunner.java:
package example.python;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
public class PythonRunner {
public static void main(String[] args) throws Exception {
String pythonInterpreter = "/usr/bin/python3" ; // default
if (args.length > 0) {
pythonInterpreter = args[0] ;
}
InputStream script = PythonRunner.class.getResourceAsStream("script.py");
Process pythonProcess = new ProcessBuilder(pythonInterpreter, "-")
.start();
// This thread reads the output from the process and
// processes it (in this case just dumps it to standard out)
new Thread(() -> {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(pythonProcess.getInputStream()))) {
for (String line ; (line = reader.readLine()) != null ;) {
System.out.println(line);
}
} catch (IOException exc) {
exc.printStackTrace();
}
}).start();
// read the script from the resource, and pipe it to the
// python process's standard input (which will be read because
// of the '-' option)
OutputStream stdin = pythonProcess.getOutputStream();
byte[] buffer = new byte[1024];
for (int read = 0 ; read >= 0 ; read = script.read(buffer)) {
stdin.write(buffer, 0, read);
}
stdin.close();
}
}
script.py:
import sys
for i in range(10):
print("Spam")
sys.exit(0)
MANIFEST.MF
Manifest-Version: 1.0
Main-Class: example.python.PythonRunner
Eclipse layout:
Jar contents and result of running:
$ jar tf runPython.jar
META-INF/MANIFEST.MF
example/python/PythonRunner.class
example/python/script.py
$ java -jar runPython.jar
Spam
Spam
Spam
Spam
Spam
Spam
Spam
Spam
Spam
Spam
$

Opening a file using java.awt.Desktop while redirecting it's stderr?

I can use java.awt.Desktop.getDesktop.open(someFile) to open a file with the system default application. However, if that application has any output, it will show in the console. Since I'm making a command line utility, this is ugly/unnecessary. Is there any way to redirect it's standard error stream?
If this cannot be done, what about finding the path to the executable which Desktop would use to open the file? After gathering that information, it would be simple enough to use a Process/ProcessBuilder.
In principle it's not necessary know the path to the default program to open a file if you will use ProcessBuilder. You can delegate this task to the Operating System through cmd.exe on Windows and xdg-open on Linux (see Xdg-open) as follows:
public static void main(String[] args) {
String filePath = args[0];
String operatingSystem = System.getProperty("os.name");
try{
if(operatingSystem.toLowerCase().contains("windows")){
ProcessBuilder pb = new ProcessBuilder("cmd.exe","/c",filePath);
Process p = pb.start();
} else if (operatingSystem.toLowerCase().contains("linux")){
ProcessBuilder pb = new ProcessBuilder("xdg-open",filePath);
Process p = pb.start();
} else {
throw new UnsupportedOperationException(String.format("Not supported for %1$1s", operatingSystem));
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
Note: I'm not using ProcessBuilder#redirectError method because it's just an example.
Disclaimer: as I said in my comments it is an ugly/limited workaround to this matter, but it works.

Opening exe with parameters in Java, don't understand parameters

I'm aware how to open an exe program with parameters in Java from finding the answer online. However my specific shortcut is a bit complicated for me to understand.
I'm trying to open a shortcut which has the following target:
C:\Windows\System32\javaw.exe -cp jts.jar;total.2012.jar -Dsun.java2d.noddraw=true -Dswing.boldMetal=false -Dsun.locale.formatasdefault=true -Xmx768M -XX:MaxPermSize=128M jclient/LoginFrame C:\Jts
In my program I've split up the location and what I think are the parameters. However when I run the program I get the error 'Could not create Java Virtual Machine, Program will Exit'. Can someone with a better understanding of whats going on explain what I might be doing wrong or point me in a direction where I can read up?
String location = "C:\\Windows\\System32\\javaw.exe";
String p1="-cp jts.jar;total.2012.jar";
String p2="-Dsun.java2d.noddraw=true";
String p3="-Dswing.boldMetal=false";
String p4="-Dsun.locale.formatasdefault=true";
String p5="-Xmx768M";
String p6="-XX:MaxPermSize=128M";
String p7="jclient/LoginFrame" ;
String p8 = "C:\\Jts";
try {
Process p = new ProcessBuilder(location,p1,p2,p3,p4,p5,p6,p7,p8).start();
} catch (IOException ex) {
Logger.getLogger(Openprogramtest.class.getName()).log(Level.SEVERE, null, ex);
}
Each String you pass to ProcessBuilder is a separate argument (except the first one, which is the command).
Think of it like the args[] which are passed to your main method. Each String would be a separate element in the array.
I suspect that p1 is been interpreted as a single argument, when it should actually be two...
Try separating this argument into two separate parameters
String location = "C:\\Windows\\System32\\javaw.exe";
String p1="-cp";
String p2="jts.jar;total.2012.jar";
String p3="-Dsun.java2d.noddraw=true";
String p4="-Dswing.boldMetal=false";
String p5="-Dsun.locale.formatasdefault=true";
String p6="-Xmx768M";
String p7="-XX:MaxPermSize=128M";
String p8="jclient/LoginFrame" ;
String p9 = "C:\\Jts";
Amendment
Look at the -cp parameter, it appears that the class path elements are relative to the location that the command is executed. This suggests that you need to use the ProcessBuilder#directory(File) to specify the location that the command should executed from.
For example, if you program is installed in C:\Program Files\MyAwesomeApp, but you run it from the context of C:\Desktop, then Java won't be able to find the Jar files it needs, generally raising a ClassNotFound exception.
Instead, you need to tell ProcessBuilder that you want the command to executed from within the C:\Program Files\MyAwesomeApp context.
For example...
ProcessBuilder pb = new ProcessBuilder(...);
pb.directory(new File("C:\Program Files\MyAwesomeApp"));
// Other settings...
Process p = pb.start();
Updated from running example
Just to make the point. I built myself a little Java program that simple printed a simple message to the standard out.
When I run this, it works as expected...
try {
String params[] = new String[]{
"C:\\Windows\\System32\\javaw.exe",
"-cp",
"C:\\...\\TestSimpleProcessBuilder\\build\\classes",
"-Dsun.java2d.noddraw=true",
"-Dswing.boldMetal=false",
"-Dsun.locale.formatasdefault=true",
"-Xmx768M",
"-XX:MaxPermSize=128M",
"testsimpleprocessbuilder/HelloWorld",
"Boo"
};
ProcessBuilder pb = new ProcessBuilder(params);
pb.redirectErrorStream();
Process p = pb.start();
InputStream is = p.getInputStream();
int in = -1;
while ((in = is.read()) != -1) {
System.out.print((char) in);
}
is = p.getErrorStream();
in = -1;
while ((in = is.read()) != -1) {
System.out.print((char) in);
}
System.out.println("p exited with " + p.exitValue());
} catch (IOException ex) {
Logger.getLogger(TestSimpleProcessBuilder.class.getName()).log(Level.SEVERE, null, ex);
}
When I change the arguments from
"-cp",
"C:\\...\\TestSimpleProcessBuilder\\build\\classes",
to
"-cp C:\\...\\TestSimpleProcessBuilder\\build\\classes",
It fails with...
And outputs
Unrecognized option: -cp
C:\DevWork\personal\java\projects\wip\StackOverflow\TestSimpleProcessBuilder\build\classes
And if you're wondering, this is the little test program I wrote that gets run...
package testsimpleprocessbuilder;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello world - world says " + (args.length > 0 ? args[0] : "Nothing"));
}
}

Runtime.exec on argument containing multiple spaces

How can I make the following run?
public class ExecTest {
public static void main(String[] args) {
try {
//Notice the multiple spaces in the argument
String[] cmd = {"explorer.exe", "/select,\"C:\\New Folder\\file.txt\""};
//btw this works
//String cmd = "explorer.exe /select,\"C:\\New Folder\\file.txt\"";
//and surprisingly this doesn't work
//String[] cmd = {"explorer.exe", "/select,\"C:\\New Folder\\file.txt\""};
//Update: and (as crazy as it seems) the following also worked
//String[] cmd = {"explorer.exe", "/select,\"C:\\New", "Folder\\file.txt\""};
Runtime.getRuntime().exec(cmd);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Using Java 6. Tested under Vista x64. By the way, taking the string that gets executed (you'll have to use the String version of exec to get it) and using it in the Search field of Vista's start menu will run as expected.
Ok, this is not simply an update but also an answer so I'm filing it as one. According to all information I could find, the following should theoretically do it:
String[] cmd = {"explorer.exe", "/select,\"C:\New", "", "", "", "", "", "", "Folder\file.txt\""};
The multiple spaces have been broken into empty strings and the array version of exec is used.
Using the above array, I debugged the loop in lines 50-75 of java.lang.ProcessImpl where a string is finally constructed. The resulting string was:
explorer.exe /select,"C:\New Folder\file.txt"
This is what is passed as the 1st argument to ProcessImpl's native create method (line 118 same class), which as it seems fails to run properly this command.
So I guess it all ends here... sadly.
Thnx prunge for pointing out the java bug.
Thnx everyone for their time and interest!
A miracle, it works!
Don't ask me why, but when i, after quite a while of nerve-wrecking research in the internets, was close to give up and use a temporary batch file as a workaround, i forgot to add the /select, parameter to the command, and, who would have thought, the following works on my Win 7 32Bit System.
String param = "\"C:\\Users\\ME\\AppData\\Local\\Microsoft\\Windows\\Temporary Internet Files\\\"";
try {
String[]commands = new String[]{"explorer.exe", param};
Process child = Runtime.getRuntime().exec(commands);
} catch (IOException e1) {
System.out.println("...");
}
General Solution:
The solution of the bug-database mentioned by prunge in his post (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6511002) worked fine for me.
Reason:
Apparently the problem lies with the commenting of some characters done by java which it does before actually executing the command string.
You have to do the commenting yourself by tokenizing your command string, to prevent the faulty java one to spring into action and mess everything up.
How to fix:
So, in my case i had to do the following (tokenizing my command string, so that no spaces are left inside the string):
String param[] = {
"explorer.exe",
"/select,C:\\Users\\ME\\AppData\\Local\\Microsoft\\Windows\\Temporary",
"Internet",
"Files\\"};
try {
Process child = Runtime.getRuntime().exec(param);
} catch (IOException e1) {
System.out.println("...");
}
As you can see i basically started a new String wherever a space occured, so "Temporary Internet Files" became "Temporary","Internet","Files".
Always use Runtime.exec(String[]), not Runtime.exec(String) unless the command line is extremely simple.
Use new File(pathName).canExecute() first to check whether it's executable or not
EDIT:
public static void runAll(String... cmd)
{
for(String s : cmd)
{
try
{
Runtime.getRuntime().exec(cmd);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
and then you can use it like: runAll("explorer.exe", "taskmgr.exe");
The characters ,-& and double spaces, all combined are a nightmare!
All the answers exposed here failed for "\\NAS\media\Music\Artistes\E\Earth, Wind & Fire\1992 - The eternal dance - Vol. 1 (1971-1975) (double space between 'Vol. 1' and '(1971').
I have no other choice than writing a temporary batch file:
void openFolderOf( Album album ) {
try {
final String path = album._playList.getParent();
final File batch = File.createTempFile( getClass().getSimpleName(), ".bat" );
try( PrintStream ps = new PrintStream( batch )) {
ps.println( "explorer.exe \"" + path + '"' );
}
Runtime.getRuntime().exec( batch.getAbsolutePath());
}
catch( final Throwable t ) {
t.printStackTrace();
}
}
Note: on cmd.exe, the line explorer "\\NAS..." works well but not with Runtime.exec() nor ProcessBuilder.
Could be a Java bug. See:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6511002
Did a bit of debugging out of curiosity, I think things are becoming unstuck in java.lang.ProcessImpl (see the constructor). Noticed that when it got to actually calling the underlying Windows API the string had turned into
explorer.exe "/select,"c:\New Folder\test.txt""
So that might explain why, as for workarounds see the bug database link.
For your specific case of needing the reveal/select command, I get around the windows quote nightmare by using cmd /c start:
String[] cmd = {"cmd", "/c", "start explorer.exe /select," + path};
Where path is the absolute path from a File object.
A better way to do it would be using ProcessBuilder object:
Process p;
p = new ProcessBuilder("/Applications/Sublime Text.app/Contents/MacOS/sublime_text", homeDir + _CURL_POST_PUT_CMDS).start();
int exitValue = p.waitFor();
if (exitValue != 0){
System.out.println("Error to open " + homeDir + _CURL_POST_PUT_CMDS);
}
Simple way to resolve this problem for files is java.awt.Desktop Since 1.6
Example:
Desktop.getDesktop().open(new File(fullFileName));

Categories