2

I want to execute one java program from my current java project. It has multiple jar dependencies that should be added in classpath before executing it. First I tried executing using normal java command -

  String classDir = "";
  for (int i = 0; i < compilerConfiguration.getClasspathEntries().size(); i++) {
        classDir = classDir + compilerConfiguration.getClasspathEntries().get(i) + ";";
  }
  runProcess("java -cp " + classDir + " topLevelProject.com.test.project.App");

  private static void runProcess(String command) throws Exception {
    Process pro = Runtime.getRuntime().exec(command);
    printLines(command + " stdout:", pro.getInputStream());
    printLines(command + " stderr:", pro.getErrorStream());
    pro.waitFor();
    System.out.println(command + " exitValue() " + pro.exitValue());
  } 

But as there are multiple classpath entries , it gives me error -

  java.io.IOException: Cannot run program "java": CreateProcess error=206, The filename or extension is too long

classDir contents are somewhat like this -

E:\test\maven\com.test.project\target\classes;C:\Users\dd\.m2\repository\p2\osgi\bundle\com.t.cep.studio.cli\5.3.0.164\com.t.cep.studio.cli-5.3.0.164.jar[+com/t/cep/studio/cli/studiotools/*;?**/*];C:\Users\dd\.m2\repository\p2\osgi\bundle\org.eclipse.core.runtime\3.11.1.v20150903-1804\org.eclipse.core.runtime-3.11.1.v20150903-1804.jar[~org/eclipse/core/internal/preferences/legacy/*;~org/eclipse/core/internal/runtime/*;+org/eclipse/core/runtime/*;?**/*];

Alternatively , I tried to set classpath dynamically before executing java command :

  try {
        for (int i = 0; i < compilerConfiguration.getClasspathEntries().size(); i++) {
            String filePath = "file://" + compilerConfiguration.getClasspathEntries().get(i);
            URL[] url = { new URL(filePath) };
            ClassLoader currentThreadClassLoader = Thread.currentThread().getContextClassLoader();
            URLClassLoader urlClassLoader = new URLClassLoader(url, currentThreadClassLoader);
            Thread.currentThread().setContextClassLoader(urlClassLoader);
        }
        runProcess("java  topLevelProject.com.test.project.App");
    } catch (Exception e) {
        e.printStackTrace();
    }  

But it is not setting classpath as expected. Any other workaround?

Disha
  • 822
  • 1
  • 10
  • 39
  • Your second method doesn't actually set up a classpath anywhere. As for the first one - does any of the paths to the files contain a space? – RealSkeptic Sep 08 '16 at 07:03
  • no.. there is no space..there are around 141 entries in 'compilerConfiguration.getClasspathEntries()'. Am I missing anything in second method? – Disha Sep 08 '16 at 07:08
  • Yeah, you're missing the fact that the command is running in a separate JVM and thus has nothing to do with the class loader of the current thread. You have to pass the `-cp` (or a `CLASSPATH` environment variable). Can you somehow share the exact string in `classDir`? – RealSkeptic Sep 08 '16 at 07:23
  • Hmm. It seems to have a lot of special characters. Have you tried to put it inside double quotes in the command? – RealSkeptic Sep 08 '16 at 07:33
  • yes.. just tried it.. gives me same error – Disha Sep 08 '16 at 07:40
  • What is `compilerConfiguration` and why is it being used? Also does the invoked Java class exist in the same project? – M A Sep 10 '16 at 16:42
  • actually I am using tycho-compiler-plugin to compile my code , So I am overriding source code of tycho-compiler-plugin and after compiling my java project , I want to execute it. So its a part of tycho-compiler-plugins code .. https://github.com/eclipse/tycho/blob/master/tycho-compiler-plugin/src/main/java/copied/org/apache/maven/plugin/AbstractCompilerMojo.java – Disha Sep 10 '16 at 16:57
  • Are you able to launch your program manually from the command line providing that long-long classpath? – Leon Sep 11 '16 at 06:28
  • @Leon No.. I am not able to run it as on command line it does not accept arguments after some length – Disha Sep 11 '16 at 12:40
  • 1
    Have you checked this question: http://stackoverflow.com/questions/201816/how-to-set-a-long-java-classpath-in-windows ? – Leon Sep 11 '16 at 12:48
  • @Leon Yes I have gone through it.. in this case I will have to create Manifest.mf at runtime as I dont have list of jars predefined.. so I was not sure how to do it? – Disha Sep 12 '16 at 05:28
  • Check this [link] (http://stackoverflow.com/questions/252893/how-do-you-change-the-classpath-within-java) – John Harris Sep 13 '16 at 17:00

3 Answers3

3

In addition to @Szmeby answer, if you don't know how to use file with classpath inside, you may try to create a "pathing jar".

"Pathing jar" contains only Manifest.mf file which includes next entry:

Class-Path: some.jar another.jar others.jar

You can also use wildcards to reduce length.

Lostboy
  • 458
  • 3
  • 16
1

I think it is mainly an OS problem caused by the command line length limitation, not a java one. I had the same issue when I was playing around with jdeps, it also needed a huge classpath. Eventually I exported the classpath into a plain text file and inlined that file content as a command argument.

Assuming the name of the text file containing the classpath string is: cp.txt

Its content (partly):

/home/anon/.m2/repository/com/app/generator/2.0.jar:/home/anon/.m2/repository/com/app/model/2.0.jar:/home/anon/.m2/repository/com/generator-helpers/2.0.jar:/home/anon/.m2/repository/org/eclipse/emf/org.eclipse.emf.ecore/2.10.1-v20140901-1043/org.eclipse.emf.ecore-2.10.1-v20140901-1043.jar:/home/anon/.m2/repository/org/eclipse/emf/org.eclipse.emf.common/2.10.1-v20140901-1043/org.eclipse.emf.common-2.10.1-v20140901-1043.jar:/home/anon/.m2/repository/org/eclipse/emf/org.eclipse.emf.ecore.xmi/2.10.1-v20140901-1043/org.eclipse.emf.ecore.xmi-2.10.1-v20140901-1043.jar:/home/anon/.m2/repository/commons-io/commons-io/2.1/commons-io-2.1.jar:etc...

Then you should execute your command like this:

runProcess("java -cp $(< cp.txt) topLevelProject.com.test.project.App");

It can consume a classpath string of any size, however it is a linux-only solution. I do not know how to inline file content in a Windows command prompt. Well, at least I hope it gives you some idea to move on.

Szmeby
  • 148
  • 3
  • 8
  • thank you.. It helped me to move ahead.. as it was platform specific , I used pathing-jar for my project – Disha Sep 14 '16 at 05:51
1

Another solution to launching a Java application from Java is to use a class loader. This has the following advantages:

  • You can guarentee that the new program runs with the same Java version as your program, thus making multiple installations a non-issue.
  • The new program can more easily communicate with your program if you want it to (although this is not at all required if you choose this route).
  • It is more platform independent in that any setup with a strange command line configuration is a non-issue.

To use this solution, simply load all of the necessary jars with a URLClassLoader, and then call the main method via reflection:

URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{
    new URL("file://path/to/jar/1.jar"),
    new URL("file://path/to/jar/2.jar"),
    new URL("file://path/to/jar/3.jar"),
    new URL("file://path/to/jar/4.jar"),
    new URL("file://path/to/jar/5.jar")
});

Class<?> clazz = urlClassLoader.loadClass("topLevelProject.com.test.project.App");
clazz.getMethod("main", String[].class).invoke(null, new String[]{"Command", "Line", "Arguments", "Here"});
john01dav
  • 1,842
  • 1
  • 21
  • 40