2

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).

ImJustACowLol
  • 826
  • 2
  • 9
  • 27
  • Is your `main` method ready to receive multiple file paths as arguments ? – Arnaud May 06 '16 at 08:47
  • @Berger Yes, main is ready to receive multiple file paths as arguments. I have made a testfile to see what the arguments are, and every time only one filepath is shown as a argument. – ImJustACowLol May 06 '16 at 08:49
  • @MadProgrammer I have looked into that aswell, but I am not sure how to implement that. Using a socket would require the different (attempted) application instances to communicate with each other, and to pass on their args to each other. – ImJustACowLol May 06 '16 at 08:55
  • Well, the idea would be to attempt to create a ServerSocket on particular port, if it fails, you would attempt to connect to it via a normal socket and communicate the information you need, once you're done, you'd exit.on the other side, if you can create a ServerSocket, you'd wait for stuff to be sent to you – MadProgrammer May 06 '16 at 09:01

2 Answers2

2

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.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
2

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.

ImJustACowLol
  • 826
  • 2
  • 9
  • 27