Make a Java application run itself from the command prompt? - java

I have an executable Jar file and to keep it simple, I want to make it so that you can simply double click it on the desktop and it will run. I've tried this:
if(args.length == 0){
String path = Main.class.getProtectionDomain().getCodeSource().getLocation().getPath();
String decodedPath = URLDecoder.decode(path, "UTF-8");
Runtime rt = Runtime.getRuntime();
Process pr = rt.exec("java -jar \"" + decodedPath + "\" -arg");
System.out.println("java -jar \"" + decodedPath + "\" -arg");
}
To no avail. I assumed that if I told the program to check for the "-arg" argument and it wasn't there, then it would asssume the program was run from the executable, not being called from the command line. So is there a way to make the program open a command prompt and then run itself within it, killing the previous program?

As to "run on double click", this is OS dependent.
You can "run a jar" at the command line using:
java -jar the.jar
This requires that the jar has a META-INF/MANIFEST.MF and that this manifest file has a Main-Class entry, the argument being the class where your main() method is. For instance:
Main-Class: org.foobar.mypackage.Foo

What I have done for a similar problem is that I have made a separate GUI program in a JAR file with some JTextFields for input and a JButton for confirmation. When the button gets clicked, it calls the main method in my other class with those values in a String array to start that program and close the GUI form with frame.setVisible(false). I suggest doing something like that, but it's dependent on what type of program you're developing.

You could also just pass the necessary command-line flags directly into the JRE at runtime! I just figured this out a couple weeks ago, but you can access the java.library.path and change it to match necessary library paths through reflection by just putting this code in the front of your main method.
try{
System.setProperty("java.library.path", path);
Field fieldSysPath = ClassLoader.class.getDeclaredField( "sys_paths" );
fieldSysPath.setAccessible( true );
fieldSysPath.set( null, null );
}catch(Exception ex){
// just exit and tell user that there was an error or something similar
}
Anyway, I hope that this was helpful. You can also do many similar things by similar code.

Related

Java Process object fails to execute given command

I am trying to run a piece of Python code via a Java application. The command when put directly into Command Prompt cd'd to the working directory runs exactly as intended. However, my attempts to use the Runtime and ProcessBuilder classes in conjunction with the Process class has yielded no sign of correct function which would be the creation of a CSV file for every call of the code.
I am running this program using Intellij on Windows 10. I have added each directory I am using to my environmental PATH variable as well as attempting full paths in my commands and just file names. The only source of life I can find is that if I include a .waitFor() method a .isAlive() method will return true before the .waitFor() method is called.
I have searched through various similar questions and concluded that using a ProcessBuilder object is the best way to go and that the biggest issue is probably the structure of my command. However, I have made many iterations and have found nothing that changes the caught error to anything useful.
Here is the privacy augmented code that I have been running, I wrote out the command in full in the process builder as that is the last iteration I have attempted.
for (int y = 1; y < iterator; y++) {
try {
String command =
"C:\\Users\\myName\\AppData\\Local\\Programs\\Python\\Python37\\python C:\\Users\\myName\\IdeaProjects\\projectApplication\\script.py ";
String pythonInputPath = " C:\\Users\\myName\\IdeaProjects\\projectApplication\\bin\\output" + y + ".wav ";
ProcessBuilder pb = new ProcessBuilder(command+Arrays.toString(pythonCommandString).replaceAll("\\s","")+pythonInputPath+Integer.toString(y));
Process p = pb.start();
//Process checks
System.out.println(p.isAlive());
p.waitFor();
System.out.println(p.isAlive());
//Destroying process once complete to ensure smooth iterations
p.destroy();
} catch (Exception ex) {
System.out.println("Problems with python script execution: " + ex);
}
}
They python code takes in a WAV file (pythonInputPath) that is a product of earlier part of the application, an Integer[] that usually includes ~20 values (pythonCommandString), and a single iteration integer (y).
The first call to .isAlive() is true and the second is false as expected however the script normally creates a CSV that should be output to a bin file that exists in the working director and that fails to occur when running from Java. From other examples I expected using the Process builder as opposed to the Runtime stream to work, however, there is no difference in my implementation.
Do not concatenate the program with its arguments. Quoting Oracle ProcessBuilder docs
Each process builder manages these process attributes: a command, a
list of strings which signifies the external program file to be
invoked and its arguments, if any
and
ProcessBuilder pb = new ProcessBuilder("myCommand", "myArg1", "myArg2");
Just use the constructor you use, but pass each argument as a separate string, otherwise the OS will try to find an application that is named as a whole command line you gave, and obviously there is no such program

Java: showInputDialog and ProcessBuilder

I just started coding in Java and i'm trying to make a program to run chkdsk when i click on a JButton. I will put a bit of the code here so you guys can help me:
String disk = JOptionPane.showInputDialog(Janela, "Especifique a letra do disco (Exemplo: C:)", "CHKDSK /F", JOptionPane.QUESTION_MESSAGE);
if (disk.length() == 2 && disk.endsWith(":")) {
try {
String disk2 = ("fsutil dirty set " + disk)
ProcessBuilder chkdskf = new ProcessBuilder("cmd.exe", "/C", "start", disk2);
Process chkdskff = chkdskf.start();
}
catch (IOException fnfex2) {
System.out.println ("Erro no CHKDSK /F");
}
}
else {
JOptionPane.showMessageDialog(Janela, "Erro!", "Erro", JOptionPane.ERROR_MESSAGE);
}
So, it shows a InputDialog so you can put a drive letter (like C: or D:), and after that it checks if the string is the way i want. (Has two characters and ends with a ":"). Then, it starts a new cmd window with the command to make chkdsk run on the next reboot. However, it doesn't work. The CMD window that is opened when the code is executed has the title of "fsutil dirty set C:", but nothing happens, no command is executed. Any help is appreciated, and sorry for my poor english.
From the command line help for start
STATE ["title"] [/D path] ... [command/program] [parameters]
So, based on that, it would mean that start is taking fsutil dirty set C: as the title - the reason is because of the way the parameters work in ProcessBuilder.
Each element in the array is a separate argument been sent to the command, this is really helpful as it means you don't need to worry about quotes or other escaping requirements
So, based on all of this, you should construct your ProcessBuilder more like...
ProcessBuilder chkdskf = new ProcessBuilder("cmd.exe", "/C", "start", "Make it so", "fsutil", "dirty", "set", disk);
I don't know the way you are implemented. But I have an idea for your refer. Let's create a .bat file, write the execute statement to this file, then call to run this .bat file like this:
Runtime.getRuntime().exec("cmd /c start " + "command.bat");

Run external app by userinput

So I'm creating a Java program and I want to make it so that you can ask it to open a program.
But, here's the catch, I want the program it opens to be taken from the user input, right now I'm trying to change this
try{Process p = Runtime.getRuntime().exec("notepad.exe");}
catch(Exception e1){}
Into something that opens a program that you asked it to open.
Here's an example of what I want:
User: Can you open chrome?
Program: Of course, here you go!
chrome opens
Could anyone tell me how I would be able to do this?
You can do it in two ways:
1.By Using Runtime:
Runtime.getRuntime().exec(...)
So, for example, on Windows,
Runtime.getRuntime().exec("C:\application.exe -arg1 -arg2");
2.By Using ProcessBuilder:
ProcessBuilder b = new ProcessBuilder("C:\application.exe", "-arg1", "-arg2");
or alternatively
List<String> params = java.util.Arrays.asList("C:\application.exe", "-arg1", "-arg2");
ProcessBuilder b = new ProcessBuilder(params);
or
ProcessBuilder b = new ProcessBuilder("C:\application.exe -arg1 -arg2");
The difference between the two is :
Runtime.getRuntime().exec(...)
takes a single string and passes it directly to a shell or cmd.exe process. The ProcessBuilder constructors, on the other hand, take a varargs array of strings or a List of strings, where each string in the array or list is assumed to be an individual argument.
So,Runtime.getRuntime.exec() will pass the line C:\application.exe -arg1 -arg2 to cmd.exe, which runs a application.exe program with the two given arguments. However, ProcessBuilder method will fail, unless there happens to be a program whose name is application.exe -arg1 -arg2 in C:.
You can try it with like. Pass whole path of where you install chrome.
try{
Process p = Runtime.getRuntime().exec("C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe");
}
catch(Exception e1){
}
When using exec, it is essentially the same as if you were using the command line on windows. Open Command Prompt, type open, and see if it gives details as to how it opens files. If not, find the opener. Usually when dealing with command line operations, there are multiple parameters that are required for opening files/applications. An example of this would be for opening the "TextEdit.app" application on a mac.
Process p = Runtime.getRuntime().exec("open -a TextEdit.app");
Terminal(for mac) would open the app using the -a flag, meaning "application." You could open a file doing:
Process p = Runtime.getRuntime().exec("open filename.file_ext -a TextEdit.app");
The second one will tell the computer to find the application named <app_name>.app and open the file filename.file_ext
I know this is not going to work for a windows machine, but it's only to show how to use the command line operations for opening files and applications. It should be similar for windows though.
Hope this helps

Command prompt doesn't open with Runtime.getRuntime().exec

I've created a GUI (swing) that executes a batch file that contains a command prompt .exe file execution with specific parameters.
When I run the batch file manually (by double clicking it), everything is as expected.
The problem is: the command prompt window doesn't open to show progress, moreover, it doesn't really start to work (only initiated) until I exit the GUI (forking?). When it starts to work, is works somewhere in the background and seen only in the task manager.
Only a blank command prompt window is opened.
From digging little bit around, I've constructed this command that gives me same result as above:
Runtime.getRuntime().exec("cmd.exe /c start \"Encoding\" cmd.exe /c start md \"" + Gui.outputDirField.getText() + "\\encoderOutput\" & cd \"" + Gui.outputDirField.getText() + "\\encoderOutput\" & \"" + Gui._batFile + "\" & pause");
Could you please assist?
Sorry if it sounds stupid..
this way works for me:
new Thread() {
#Override public void run() {
try {
Runtime.getRuntime().exec("cmd.exe /c start " + Gui._batFile);
} catch (IOException e1) {
e1.printStackTrace();
}
}
}.run();
trashgod may be on to something. We ran into issues with paths with spaces. This is from the release notes for jre 7u21
Changes to Runtime.exec
On Windows platform, the decoding of command strings specified to Runtime.exec(String), Runtime.exec(String,String[]) and Runtime.exec(String,String[],File) methods, has been improved to follow the specification more closely. This may cause problems for applications that are using one or more of these methods with commands that contain spaces in the program name, or are invoking these methods with commands that are not quoted correctly.
For example, Runtime.getRuntime().exec("C:\\My Programs\\foo.exe bar") is an attempt to launch the program "C:\\My" with the arguments "Programs\\foo.exe" and "bar". This command is likely to fail with an exception to indicate "C:\My" cannot be found.
The example Runtime.getRuntime().exec("\"C:\\My Programs\\foo.exe\" bar") is an attempt to launch the program "\"C:\\My". This command will fail with an exception to indicate the program has an embedded quote.
Applications that need to launch programs with spaces in the program name should consider using the variants of Runtime.exec that allow the command and arguments to be specified in an array.
Alternatively, the preferred way to create operating systems processes since JDK 5.0 is using java.lang.ProcessBuilder. The ProcessBuilder class has a much more complete API for setting the environment, working directory and redirecting streams for the process.
Does your bat file requiere user interaction or why are you putting a pause on your command? If so, the Runtime.exec just runs the file with no window, why would you want a Window? >ou can get a Process object as a result from the exec, from this object you can get an InputStream (and if needed, an OutputStream) so you can print your output or interact with the process.

Launching java classes via windows drag-and-drop

I have a java class file with a main method. In Windows, I would like to be able to drag files onto a desktop icon/short/etc that would call supply the filenames to my main method. Basically, I want to allow users to drag-and-drop files at program execution instead of having type them on the command line.
Any thoughts?
To build on daub815's answer, in Windows, you can use a batch file to pass
arguments to another command. In this case, we'll use the java launcher to
launch your class with the main method.
I did a quick Google search on how to do write a batch file to take multiple arguments,
and found a page with a batch file to pass arguments to another command. Adapting from
the example, here is what you can do:
#ECHO OFF
:Loop
IF "%1" == "" GOTO Done
java YourClass %1
SHIFT
GOTO Loop
:Done
Save the above file as a batch file (with a ".bat" extension), and then you can drag-and-drop
files onto it, and it will be passed as arguments.
Also, you can call the batch file from the command line and pass arguments as well.
Edit: It appears that the batch file will not work with quoted arguments which contain spaces. Using a workaround presented in the site I've linked to will split the spaces contained in the quoted full path of the file into separate arguments, so that won't work either. If anyone has a good idea how to fix this, please either edit this entry, or post another answer. I will make this a community wiki.
PhiLho's answer works perfectly if you pack the classes in an executable JAR file (it's how you're meant to do it anyway) and make a .reg file that looks like the one below. Then just double-click that .reg file to merge it into the registry and you're good to go. This lets you both double-click a JAR file to run it, and starting it by Drag & Drop.
Do remember to change the path to where your Java executable is installed.
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\.jar]
#="jarfile"
[HKEY_CLASSES_ROOT\jarfile\DefaultIcon]
#="C:\\Java\\jdk1.7.0\\bin\\java.exe,1"
[HKEY_CLASSES_ROOT\jarfile\shell\open]
#="Run Java Program"
[HKEY_CLASSES_ROOT\jarfile\shell\open\command]
#="\"C:\\Java\\jdk1.7.0\\bin\\java.exe\" -jar \"%1\" %*"
[HKEY_CLASSES_ROOT\jarfile\shellex\DropHandler]
#="{86C86720-42A0-1069-A2E8-08002B30309D}"
OK, I made it work... The base knowledge is to use DropHandler UUID in the registry. I made a base setting, as follow:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\.class]
#="JavaClass"
[HKEY_CLASSES_ROOT\JavaClass\DefaultIcon]
#="C:\\Java\\jdk1.6.0_05\\bin\\java.exe,1"
[HKEY_CLASSES_ROOT\JavaClass\shell\open]
#="Run Java class"
[HKEY_CLASSES_ROOT\JavaClass\shell\open\command]
#="\"C:\\Java\\jdk1.6.0_05\\bin\\java.exe\" \"%1\" %*"
[HKEY_CLASSES_ROOT\JavaClass\shellex\DropHandler]
#="{86C86720-42A0-1069-A2E8-08002B30309D}"
and... it didn't work!
I just forgot that java.exe wants a class name, not a file name! But I see no way to do that in the registry.
Fortunately, there is a workaround, which still need a script file if we want to be generic, to work on any/all class files (with static main function, of course!). Not batch, I avoid them when I can. I chose to use WSH, as it should be available on any modern Windows system. I also chose to make a JS script, it could have been a VB script as well.
So I made the following script (LaunchJavaClass.js):
if (WScript.Arguments.count() == 0)
{
WScript.StdOut.Write("No parameters");
WScript.Quit(1);
}
var className = WScript.Arguments.Item(0);
//~ WScript.StdOut.Write(className + "\n");
var m = className.match(/^(.*)\\(.+?)\.class$/);
if (m == null)
{
WScript.StdOut.Write("Not a class file");
WScript.Quit(1);
}
var classPath = m[1];
className = m[2];
//~ WScript.StdOut.Write(classPath + " >>> " + className + "\n");
var params = new Array();
for (i = 1; i < WScript.Arguments.count(); i++)
{
params[params.length] = WScript.Arguments.Item(i);
}
var cmd = "cmd /c cd /D " + classPath +
" & C:/Java/jdk1.6.0_05/bin/java.exe " +
className + " " + params.join(" ");
//~ WScript.StdOut.Write(cmd + "\n");
var shell = WScript.CreateObject("WScript.Shell");
//~ var exec = shell.Exec(cmd); // Can be used to get stdout
shell.Run(cmd, 0);
I left some output, not useful in this context, but usable for debugging (run with cscript).
Of course, the path to the JRE must be adjusted.
And I changed the command in the registry, as follow:
[HKEY_CLASSES_ROOT\JavaClass\shell\open\command]
#="\wscript -b "D:\\_PhiLhoSoft\\WSH\\LaunchJavaClass.js\" %1 %*"
Of course, adjust path, and keep the above other lines.
Now, if I drag'n'drop some files to a .class file, it gets the short file paths as arguments of the main() function.
import java.io.*;
class TestDnD
{
public static void main(String[] args)
{
Writer output = null;
try
{
output = new BufferedWriter(new FileWriter(new File("LogFile.txt")));
for (String arg : args)
{
output.write(arg + "\n");
}
}
catch (IOException ioe)
{
ioe.printStackTrace();
return;
}
finally
{
try { output.close(); } catch (IOException e) {}
}
}
}
I think the first version of the .reg file can be used for something else, eg. to drag'n'drop on .jar files (adapting it, of course).
This technique has limited use: we rarely make one-class programs in Java! But it looked like a good and interesting challenge, so I didn't resist to solve it. Note: you can add stuff like -Djava.ext.dirs="some path;another path" if you ever need to use external libraries (in jar files).
Adding onto Adiel A. If you create a batch file, which launches your a Java window using Swing. You would have the user drop the files onto that window. You could then be able to root through those dropped files.
So there's no way to have windows itself pass the args into main() via drag and drop?

Categories

Resources