My Goal
I am attempting to make a Java program in which a user can select any .class or .jar file from their computer. My program will then pop up a JInternalFrame with a JEditorPane in it as the console, capturing any console output from the user's program. When the user's program closes (calls System.exit(int status);), my program must not close along with it. My program might also have such features as a button to immediately stop the user's program and others an IDE would. My program need not compile Java code, only run .class and .jar files.
My Experience
I have made a small test version of this program wherein I got two specific files from a package and had the user click one of two buttons, each representing one of the two programs. A press of a button calls the following method:
private void run(Class runnable)
{
java.lang.reflect.Method[] m = runnable.getMethods();
boolean hasMain = false;
for (int i = 0; i < m.length; i++)
{
if (m[i].getName().equals("main") && m[i].getParameterTypes()[0].isArray() && m[i].getParameterTypes()[0].getName().contains("java.lang.String"))
try
{
Object invoke = m[i].invoke(null, (Object)globalArgs);
hasMain = true;
hub.setExtendedState(Hub.ICONIFIED);
numPrograms++;
}
catch (Throwable t)
{
java.util.logging.Logger.getLogger(Hub.class.getName()).log(java.util.logging.Level.SEVERE, null, t);
javax.swing.JOptionPane.showMessageDialog(null, "Could not run " + runnable.getName(), "Error in invocation", javax.swing.JOptionPane.ERROR_MESSAGE);
}
finally
{
break;
}
}
if (!hasMain)
javax.swing.JOptionPane.showMessageDialog(null, runnable.getName()
+ " does not have a public static main method that\nreturns void and takes in an array of Strings",
"No main method", javax.swing.JOptionPane.ERROR_MESSAGE);
}
This method successfully calls either program's main method and runs a copy of said program. However, when any of the programs this hub has started calls the System.exit(int status) command, the hub closes, too. Also, I haven't the slightest clue as to how to capture console output.
My Questions
Does anyone have any experience or advice they would be willing to share to help me make a fully-functional program that can...
Open and run a compiled Java file (remember that .jar files may have more than one class with main(String[] args) method)
Catch System.exit(int status); so that the hub program handles the internal program's exiting
Catch new java.io.PrintStream().println(Object o) and similar calls and place their output in a JEditorPane
Make a button that, when pressed, stops the internal program from running
Possibly make all JFrames the internal program uses into JInternalFrames and place them in a JDesktopPane
If you don't want the other program (which you call through it's main method) to be able to shut down the JVM you're running in, you have, as I see it, three options:
1. Using a SecurityManager
Set up the SecurityManager so that it prevents the System.exit call:
public class Test {
public static void main(String args[]) {
SecurityManager sm = System.getSecurityManager();
System.setSecurityManager(new SecurityManager() {
#Override
public void checkExit(int status) {
throw new SecurityException("Client program exited.");
}
});
try {
System.out.println("hello");
System.exit(0);
System.out.println("world");
} catch (SecurityException se) {
System.out.println(se.getMessage());
}
}
}
Prints:
hello
Client program exited.
This is probably the nicest solution. This is the way application servers prevent an arbitrary servlet from terminating the entire server.
2. Separate JVM
Run the other program in a separate JVM, using for instance ProcessBuilder
import java.io.*;
public class Test {
public static void main(String args[]) throws IOException {
ProcessBuilder pb = new ProcessBuilder("java", "other.Program");
pb.redirectErrorStream();
Process p = pb.start();
InputStream is = p.getInputStream();
int ch;
while ((ch = is.read()) != -1)
System.out.print((char) ch);
is.close();
System.out.println("Client program done.");
}
}
3. Use shutdown hooks instead
Don't disallow the termination of the JVM, but instead add shutdown-hooks that cleans up the "hub" and exits gracefully. (This option probably only makes sense if your running one "external" program at a time.)
import java.io.*;
public class Test {
public static void main(String args[]) throws IOException {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
System.out.println("Uninitializing hub...");
System.out.println("Exiting gracefully.");
}
});
// Run client program
System.out.println("Running... running... running...");
System.exit(0);
}
}
Prints:
Running... running... running...
Uninitializing hub...
Exiting gracefully.
Related
I've got a tough question, for which I will first sketch a background to make things more understandable.
Background
I have made an audioplayer in Java which can be launched with command line args, and also without. The application's .jar (made with Netbeans) is wrapped in a .exe file (made with Launch4j) so that you can open for example a mp3 file with the .exe, and then the .jar inside adopts the filepath in it's String[] args.
The problem with this approach (for now) is that if you select multiple mp3 files at once and you open them at the same time, they all get opened in seperate windows of the audioplayer. What I want however, is that all the files get opened in one single instance of the application.
What I then attempted is to let Launch4j allow only one instance of the .jar/.exe in the hopes that all the selected files would be opened in one application, this did unfortinately not work.
What I see as a solution
So I want to be able to select multiple .mp3 files in windows, and that all their filepaths get passed on as a command line arg to one single instance of the application. Or a different approach that has the same result. Does anyone know how to realize this in the actual application?
Many thanks in advance. I will try to keep looking for potential solutions/ideas as well.
--Edits--
The main method is ready to receive multiple files. I have implemented a piece of code that saves all the command line args of the application to a .txt file, and when I allow only one single instance with the Launch4j .exe file, there only appears to be one single argument in the .txt file when I try to open multiple mp3 files.
If I allow the .exe to have multiple instances, then I simply have the .jar application being launched multiple times (one time for each file I try to open).
I used java RMI (Remote Method Invokation) to make a single-instance application.
An RMI attempts to listen on a socket with a user-defined port number.
When starting the jar.
If noone serves that port, then this instance is the RMI server. Establish a GUI Window. Call an open with the main's args.
If there is already a serving application, send by RMI an open with the main's args. Then exit normally, return from main.
Code: Untested as you probably want to arrange things differently.
public interface OpenRMI extends Remote {
void open(String[] args) throws RemoteException;
}
public class SingleInstanceApp implements OpenRMI {
private static final String RMI_ENTRY = "ImJustACowLolAudioPlayer";
public static void main(String[] args) throws RemoteException,
AccessException, NotBoundException {
System.out.println("main " + Arrays.toString(args));
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
Registry registry = LocateRegistry.getRegistry();
OpenRMI openRMI;
try {
System.out.println("bind with new OpenRMI");
SingleInstanceApp app = new SingleInstanceApp();
openRMI = (OpenRMI) UnicastRemoteObject.exportObject(app, 0);
registry.bind(RMI_ENTRY, openRMI);
System.out.println("Player bound");
app.create(); // Server.
} catch (AlreadyBoundException e2) {
System.out.println("lookup as someone else bound before us");
openRMI = (OpenRMI) registry.lookup(RMI_ENTRY); // Client.
}
openRMI.open(args);
}
private void create() {
new Thread(true) { // Daemon thread, or start GUI
#Override
public void run() {
System.out.println("create " + this);
for (int i = 0; i < 10; ++i) {
Thread.sleep(1000L);
}
shutdown();
}
}
}
private void shutdown() throws RemoteException,
NotBoundException, AccessException {
System.out.println("close " + this);
Registry registry = LocateRegistry.getRegistry();
registry.unbind(RMI_ENTRY);
}
#Override
public void open(String[] args) throws RemoteException {
System.out.println("open " + this + ": " + Arrays.toString(args));
}
}
I would expect some more decent classes.
I fixed it, after some hours of programming and taking breaks inbetween
package argsbuilder;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
public class ArgsBuilder
{
public static void main(String[] args)
{
checkIfRunning(args);
}
private static void checkIfRunning(String[] args)
{
buildFile(args);
ProcessBuilder pb = new ProcessBuilder("core.exe"); //core.exe is a .exe wrapper with the .jar audioplayer in it
try
{
Process p = pb.start();
}catch (IOException f){System.out.println(f);}
}
private static void buildFile(String[] args)
{
try
{
boolean notdone = true;
int i=0;
File f;
while(notdone)
{
f = new File("arg" + i + ".txt");
if(f.exists())
{
i++;
}
else
{
PrintStream out = new PrintStream(new FileOutputStream(new File("Folder Location" + "arg" + i + ".txt")));
System.setOut(out);
System.out.println(args[0]);
notdone = false;
}
}
}catch(Exception g){System.out.println(g);}
}}
What the above does
The above application checks if there are other argument files, and if there are it will keep generating a new name untill the name is free. It then prints the argument to that file. After it has printed the argument, it launches the audioplayer. In the audioplayer the following happens:
import javafx.embed.swing.JFXPanel;
import java.net.InetAddress;
import java.net.ServerSocket;
public class YourApp {
public static void main(String[] args)
{
try
{
socket = new ServerSocket(PORT,0,InetAddress.getByAddress(new byte[] {127,0,0,1}));
//Everything you need to launch the application in the try
}catch(Exception g){//Nothing in the catch}
}}
What the above does
It tries to claim a serversocket for itself. If there already is one then it does not proceed to launch the application. That way only one instance will be running at a time. (at PORT you just fill in a random integer).
Combining those 2, you can read the textfiles created by the first application and interpret them as arguments in the second application.
So how does it interpret them as arguments?
Well, I already had a timer fixed into the program, and I tell the audioplayer to look for the very first arg file (arg0.txt) in a specified folder. If it finds it it adds it to an arraylist, along with all arg+i.txt files.
It might not be the fastest way, but it surely works well.
I need to make a program, which can be executed in single instance. I tried to create a temporary file and delete it before exit program.
public static boolean isLocked() {
File f = new File("lock.txt");
return f.exists();
}
public static void lock() {
String fname = "lock.txt";
File f = new File(fname);
try {
f.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void unlock() {
File f = new File("lock.txt");
f.delete();
}
In frame
private void initialize() {
lock();
}
private void setFrameHandler() {
frame.addWindowListener(new java.awt.event.WindowAdapter() {
#Override
public void windowClosing(java.awt.event.WindowEvent windowEvent) {
unlock();
}
});
}
Problem occurs if program is finished with emergency (e.g. electricity cuts). File does not remove, and running a new instance is impossible.
How to make a reliable single-instance verification?
You could check for another instance of the program at startup using the GetProcesses method as described here
But that only works depending on the scenario you have (might not see all processes of other users)
Another thing you could do is simply checking, if a specific file is locked via File.Open
File.Open ("path.lock", FileMode.OpenOrCreate, FileAccess.ReadWrite);
As long as you keep the resulting FileStream open in your program no other program can open the file in that mode either. This is basically how Unix lock files work too. Of course you have to catch an IOException (hinting you to a locked file).
Disclaimer: I did not try that code out so please check if I gave you the right parameters.
Edit: You could also check out this Code-Project article on how to do it with the win32 API
Another attempt using windows messaging has been done here
A simple approach to this on a single machine is to write a 'PID file', which is literally a file containing the operating system's ID of the process currently running. You create this when you start your "critical" work, and remove it on successful completion.
Since it is unlikely that the process would be started again with the same PID, you can simply check to see if the PID file already exists, and if so, if that process is still running.
currently, i'm trying to write a programm, which should execute a seperate Java-program multiple times, but with different parameters. This executed Java-program calls a Thread-Class. Within this class, a connection to a (Game)Server is established. Once connected, the Threads sends a command to turn the connected player around every 10 milliseconds. I have 2 "solutions" for this:
The easy (working) one:
public class GeneralAgentTest {
public static void main(String [] args){
Thread thread = new Thread(new HexagonRunner("127.0.0.1",6000,"UnitedTestors",-30,-15));
thread.start();
}
}
This is working correctly, but not actually my goal. I need to start several of this Threads (new Thread(new HexagonRunner("127.0.0.1",6000,"UnitedTestors",-30,-15)); ) and each of this threads must be handled by a seperate process.
To do this, i wrote some code with an ProcessBuilder. This is within one class.
Second not correctly working one:
public void execute(Class class1, int a, String str, String team, String x,
String y) {
ProcessBuilder builder;
String javaHome = System.getProperty("java.home");
String javaBin = javaHome + File.separator + "bin" + File.separator
+ "java";
String classpath = System.getProperty("java.class.path");
String className = class1.getCanonicalName();
builder = new ProcessBuilder(javaBin, "-cp", classpath,
className, ip, port, team, str, x, y);
try {
process[a] = builder.start();
} catch (Exception e) {
e.printStackTrace();
System.out.println("failed");
}
public void loadPlayers() {
process = new Process[4];
for (int i = 0; i < 4; i++) {
try {
execute(processes.StartProcesses.class, i,
playerCombo[i].getSelectedItem().toString(),
playerTeam[i].getText(), playerStartX[i].getText(),
playerStartY[i].getText());
} catch (Exception e) {
System.out.println("Failed to create processes for players");
e.printStackTrace();
}
}
}
These are the functions i wrote, to execute the class(es) who is/are starting the thread(s).
Following class is executed:
public class StartProcesses{
public static void main(String[] args) {
Thread t = null;
t = new Thread(new HexagonRunner("127.0.0.1",6000,"UnitedTestors",-30,-15));
t.start();
JOptionPane.showMessageDialog(null, "should've started");
}
}
In my second try, the parameters which are given to the StartProcesses Class are containing some information like IP-Adresses, Portnumbers, Playerpositons and stuff like this. Anyway i was trying to execute the class with "hard" information, just to be sure it is working like in my first codepart.
The connections to the server are correctly established in both attempts, but in the first one the thread keeps working. In my second try it seems like the thread is dead after the connection is established. The process is still alive, since the connection to the server is still there.
This is a bit of code, but what i want to tell, is that the Thread is working correctly when executed manually, but it is not working correctly if i try to start the class automatically with the use of ProcessBuilders.
I really really hope you guys could understand what i am trying to say. Hopefully someone has a working solution for me.
Cheers.
EDIT: Add Code for HexagonRunner:
public class HexagonRunner extends GeneralAgent {
// Bunch of Methods
// Important Method:
#Override
protected void simulationCycle() {
turnAgent(40);
}
}
The simulationCycle() method, is the method that is going to be go through over and over again.
Since the class HexagonRunner is inherited from the class GeneralAgent, i'm going to post the relevant stuff of this class here as well:
public class GeneralAgent implements Runnable, UdpListener{
// Attributes, getters, setters, methods..
#Override
public final void run() {
// giving the worker threads the signal to start
mServerConnection.start();
mParser.start();
// waiting for the first information to be parsed, so that the
// simulation loop can run
try{
System.out.println("GeneralAgent-Run: waiting for latch");
mLogger.info("Run method: waiting for CountDownLatch");
mFirstDone.await();
}
catch(InterruptedException e){
System.out.println("GeneralAgent-Run: InterruptedException");
mLogger.info("Run method error: " + e.getMessage());
}
mLogger.info("Run method: CountDownLatch now 0, continue");
// setting the initial position
moveToPostion(mXStartCoord, mYStartCoord);
// the simulation loop
while (true){
simulationCycle();
// Shutdown if the game is over
if (mGameworld.getTime() >= 6000){ // later 6000+
System.out.println("Yeah, 50 runs completed -> Shutdown");
mLogger.info("General Agent - Time > 50, programm should terminate");
shutdown();
break;
}
// waiting for the threads to signal that they are
// ready (e.g. have new server information)
// for another round of the simulation loop
mPhaser.arriveAndAwaitAdvance();
}
}
I hope things get clearer now. I still have no idea where my code fails.
You could build somethin a lot simpler by using Executors. It' part of the comcurrent package introduced in Java 1.5. It basically works as follows:
// create a pool with 10 threads
ExecutorService executorService = Executors.newFixedThreadPool(10);
//loop as long as you need to detach your threads
for (int i = 0; i < 4; i++) {
// this actually contains the thread bit, will be executed in parallel
executorService.execute(new Runnable() {
public void run() {
// this is where your code is
new HexagonRunner("127.0.0.1",6000,"UnitedTestors",-30,-15)
}
});
}
// clean up when you're done to prevent leaks
executorService.shutdown();
That's it, much simple and you don't need to spawn different JVMs through ProcessBuilder, which is a lot slower.
I am invoking the main class of a .class contained in the same folder from my Java code. That main is void, and I want to check whether it was successful or not by checking its exit status:
...
String[] arguments = new String[]{"a", "b"};
AnotherClass.main(arguments);
...
Any idea on how, being that AnotherClass.main(arguments); does not return anything?
Thank you.
Even better idea -- instead of trying to call a main method, instead create a method in your other class called testMain() which does the same thing as main except it returns the exit code instead of exiting.
System.exit() called in that main method will shut down your entire program. You want to run the other main in another thread and find the exit code for that thread.
Build the other program (name the jar AnotherClass.jar), add the jar to your path and try this:
int exitCode;
try {
Process process = Runtime.getRuntime().exec("java AnotherClass.jar",new String[]{"arg1","arg2"});
exitCode = process.waitFor();
System.out.println(exitCode);
} catch (IOException e) {
System.out.println("IO Error: " + e);
} catch (InterruptedException e) {
System.out.println("Inturrupted: " + e);
}
EDIT: The following will not work at all; System.exit() kills all threads.
This code will run another main method in a new thread, and when it exits it will not harm your current program.
Thread anotherProgram = new Thread(new Runnable() {
#Override
public void run() {
AnotherClass.main(new String[]{"foo","bar"});
}
});
anotherProgram.start();
If you mean the exit code parameter to System.exit(int status), you cannot access it, as when it is called by some part of the main method (or subsequently executed code), your application is exited, and so there is nothing left running to get at the code.
The only way to check the exit code of an application is using the OS, e.g. the $? environment variable in unix tells you the exit code of the last application to terminate in that session.
Edit: Here's an example using ProcessBuilder to do so:
Process p = new ProcessBuilder(System.getProperty("java.home")+"/bin/java", "fully.qualified.package.to.AnotherClass").start();
Integer exitCode = null;
while (exitCode == null) {
try {
exitCode = p.waitFor();
} catch (InterruptedException e) {
// handle e
}
}
There is a console Java application which is supposed to run until it is stopped by Ctrl+C or closing the console window. How that application can be programmed to execute a clean up code before exit?
You could use a Shutdown Hook.
Basically you need to create a Thread which will perform your shutdown actions, and then add it as a shutdown hook. For example:
class ShutdownHook extends Thread
{
public void run()
{
// perform shutdown actions
}
}
// Then, somewhere in your code
Runtime.getRuntime().addShutdownHook(new ShutdownHook())
A Shutdown hook is the way to go, but be aware that there is no guarantee that the code is actually executed. JVM crashes, power failures, or a simple "kill -9" on your JVM can prevent the code from cleaning up. Therefore you must ensure that your program stays in a consistent state even if it has been aborted abruptly.
Personally, I simply use a database for all state-storage. Its transactions model makes sure that the persistent storage is in a sane state no matter what happens. They spend years making that code fool-proof, so why should I waste my time on problems already solved.
Program to delete temp file bat.bat when program is exited:
public class Backup {
public static void createBackup(String s)
{
try{
String fileName ="C:\\bat"+ ".bat";
FileWriter writer=new FileWriter(fileName);
String batquery="cd C:\\Program Files\\MySQL\\MySQL Server 5.0\\bin"
+ "\nmysqldump -uroot -proot bankdb > \""+s+".sql\""
+"\nexit";
writer.append(batquery);
writer.close();
}
catch(Exception e){e.getMessage();}
try{
Process p =Runtime.getRuntime().exec("cmd /c start C:\\bat.bat");
}
catch(Exception e){e.getMessage();}
ShutDownHook sdh=new ShutDownHook();
Runtime.getRuntime().addShutdownHook(sdh);
}
}
class ShutDownHook extends Thread
{
public void run()
{
try
{
File f=new File("c:/bat.bat");
f.delete();
}
catch(Exception e){e.getMessage();}
}
}
The code written inside a Threads run() method will execute when the runtime object terminates...
class ShutdownHookclass extends Thread {
public void run() {
// perform shutdown actions
}
}
//could be written anywhere in your code
Runtime.getRuntime().addShutdownHook(new ShutdownHookclass())