In Java, is there a way to obtain the command line parameters even if main() didn't save them?

StackOverflow https://stackoverflow.com/questions/1058531

  •  21-08-2019
  •  | 
  •  

Question

We have a program with a main() that parses certain CLPs but does not save them anywhere. I then have my own plug-in code that needs access to the original CLPs (so I can transmit more parameters) for it. However, I cannot change main()

I saw that there is apparently a way to do this in C#, I'm looking for an equivalent Java solution on Linux.

UPDATE: Obviously, I'm aware of how main() works. Unfortunately, I cannot change the existing application or the way it is invoked (except for CLPs). I can only access via a sandboxed plugin code. My question is whether there is a way to get the command line (rather then the environment variables with -D) that the JVM was invoked with.

Was it helpful?

Solution

Apart from doing it in main in some way I think the only other option that you have would be to drop to the operating system level and execute some commands to get the arguments.

On linux the cmd line arguments for a running process are stored at /proc/pid/cmdline

So to get them you would have to find the process id. See here:

How can a Java program get its own process ID?

Then using this open /proc/pid/cmdline and parse it. The format of this file and an example in c is here:

http://www.unix.com/unix-advanced-expert-users/86740-retrieving-command-line-arguments-particular-pid.html

It might be best to wrap these two calls in one shell script that you call from java.

Please note that this will be extremely non portable and is a bit hacky. But if needs must...

OTHER TIPS

The solution is easy once you realize that Java's main method is just another static method that takes a String array as argument.

Create a new class that stores the CLPs, then calls the old class. Later on, you can access your CLPs using the new class:

import NotToBeChangedMainClass;

public MyMainClass {
  public static final String[] ARGUMENTS;
  public static void main(String ... args) {
    ARGUMENTS = args;
    NotToBeChangedMainClass.main(args);
  }

}

Finally, change whatever external caller (e.g. any Batch files) to use MyMainClass instead of NotToBeChangedMainClass. If you are using runable jars or something similar, this requires changing the appropriate configuration file.

Create your own main class. Save the arguments. Call the old main.

It might be easier to use System.getProperty and -Dkey=value on the command line (before main class name or -jar).

In case you don't have a choice any you absolutely have to retain all the existing class names with their exact name (as stated in your comment to my previous answer), then you have to go with AspectJ.

Let's consider we have this class:

public class UnmodifyableClassWithMain {
  public static void main(String[] args) {
    System.out.println("In main");
    new ClassUsingArgumentRegistry();
  }
}

First, you need to something that holds the command line arguments. I will use a simple class with a static field for simplicity:

public class ArgumentRegistry {
  public static String[] ARGS;
}

Then, you need you define an Aspect that intercepts calls to main and stores the arguments.

public aspect StoreArgumentsOfMain {

  /**
   * This pointcut intercepts all calls to methods called main with a string array as
   * argument.
   */
  pointcut mainMethod(String[] arguments): execution(void main(String[])) && args(arguments);

  /**
   * Before the original main method gets called, store the arguments in the registry.
   */
  before(String[] arguments): mainMethod(arguments) {
    System.out.println("Storing arguments");
    ArgumentRegistry.ARGS = arguments;
  }

}

For trying it out, I also created a ClassUsingArgumentRegistry:

public class ClassUsingArgumentRegistry {

  public ClassUsingArgumentRegistry() {
    System.out.println("Arguments: " + java.util.Arrays.toString(ArgumentRegistry.ARGS));
  }

}

That's it. If I enable AspectJ's compile time weaving and run the result using "java UnmodifyableClassWithMain Foo Bar Baz", I get the follwing output:

Storing arguments
In main
Arguments: [foo, bar, baz]

Note that this solution is very limited as Linux truncates the command line that it saves. As Java command lines often have very long classpaths, this is a very real problem.

Here is Java code implementing the answer given by Pablojim.

package test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Test {
  public static List<String> getLinuxCmdline(int maxBytesToRead) throws IOException {
    List<String> result = new ArrayList<>();
    String pid = new File("/proc/self").getCanonicalFile().getName();
    File cmdlineFile = new File("/proc/" + pid + "/cmdline");
    final int growBy = 1024;
    try (FileInputStream is = new FileInputStream(cmdlineFile);) {
      byte[] data = new byte[Math.min(growBy, maxBytesToRead)];
      int totalRead = 0; 
      while (totalRead < maxBytesToRead) {
        int read = is.read(data, totalRead, data.length - totalRead);
        if (read > 0) {
          totalRead += read;
          if (data.length == totalRead) {
            data = Arrays.copyOf(data, Math.min(data.length + growBy, maxBytesToRead));
          }
        } else {
          break;
        }
      }
      int start = 0;
      int scan = 0;
      while (scan < totalRead) {
        if (data[scan] == 0) {
          result.add(new String(Arrays.copyOfRange(data, start, scan)));
          start = scan + 1;
        }
        scan++;
      }
      if (scan - start > 0) result.add(new String(Arrays.copyOfRange(data, start, scan)));        }
    return result;
  }

  public static void main(String[] args) throws IOException {
    System.out.println(getLinuxCmdline(Integer.MAX_VALUE));
  }
}

Running this with arguments "foo bar" in Eclipse gives this for me:

[/usr/lib/jvm/java-8-oracle/bin/java, -Dfile.encoding=UTF-8, -classpath, /home/mab/workspace/test/bin, test.Test, foo, bar]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top