1

I have a library embedded in a jar which must load an external implementation :

- MyFile
     - myjar.jar
     - Myfirstclass.java
     - MySecondclass.java

Knowing that :

My class dependent on my jar, my jar is executable and contains all the dependencies. It initializes the Reflection object in this way:

new Reflections(
                new ConfigurationBuilder()
                        .setUrls(ClasspathHelper.forJavaClassPath())

I compile my class this way:

javac -cp /path/to/myjar.jar *.java

I then get:

- MyFile
     - myjar.jar
     - Myfirstclass.java
     - MySecondclass.java
     - Myfirstclass.class
     - MySecondclass.class

In my jar, I'm trying to load these classes at runtime to initialize them. When I compile my jar with these two classes inside using Maven and run it, everything works fine.

Now I'm trying to run my jar like this:

java -cp . -jar /path/to/myjar.jar "myarg1" "myarg2"

But it does not work. Is my order wrong? The initialisation of the classpath? Or all my logic?

any help would be precious

EDIT : Following VGR's comment


I also tried this command which doesn't work:

 java -cp .:/path/to/my/jar.jar mypackage.myrunnerwithmainclass "myarg1" "myarg2" 

EDIT : Out of desperation I tried another approach with a ClassLoader


Here is my Runner class where I try to display the methods of the class :

package coderchallengeimpl;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;

public class Runner {

    private Runner() {

    }

    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException {
        Runner.run(args[0]);
    }


    public static void run(String filePath) throws MalformedURLException, ClassNotFoundException {
        List<URL> urls = filePathToUrl(filePath);
        URL[] array = new URL[urls.size()];
        urls.toArray(array);

        for (int i = 0; i < array.length; i++) {
            System.out.println(array[i]);
        }

        ClassLoader cl = new URLClassLoader(array);

        Class<?> classCoderChallengeImpl = Class.forName("impl.CoderChallengeImpl", true, cl);
        for (int i = 0; i < classCoderChallengeImpl.getMethods().length; i++) {
            System.out.println(classCoderChallengeImpl.getMethods()[i].getName());
        }


    }

    public static List<URL> filePathToUrl(String path) throws MalformedURLException {
        File root = new File(path);
        List<URL> urls = new ArrayList<>();
        int i = 0;
        for (File file : Objects.requireNonNull(root.listFiles())) {
            if (file.getName().endsWith(".class")) {
                urls.add(file.toURI().toURL());
                System.out.println(file.getAbsolutePath());
            }
        }
        return urls;
    }
}

Voici le code de impl.CoderChallengeImpl :

package impl;

import coderchallengeimpl.CallBack;
import coderchallengeimpl.CoderChallengeJava;

public class CoderChallengeImpl extends CoderChallengeJava {

    public CoderChallengeImpl(CallBack callBack, String path) {
        super(callBack, path);
    }

    public void testMethod(){
        this.addInputs("hello");
    }

    public void testMethod2(){
        this.addInputs("hello");
    }
}

Here is my console output:

/home/stan/Documents/Test/test/test_coderchallenge/java/CoderChallengeImpl.class
/home/stan/Documents/Test/test/test_coderchallenge/java/TestClassImpl.class
file:/home/stan/Documents/Test/test/test_coderchallenge/java/CoderChallengeImpl.class
file:/home/stan/Documents/Test/test/test_coderchallenge/java/TestClassImpl.class

java.lang.ClassNotFoundException: impl.CoderChallengeImpl

    at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:436)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:416)
    at coderchallengeimpl.Runner.run(Runner.java:31)
    at TestUnit.testRunner(TestUnit.java:16)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)


Process finished with exit code 255

Now if in the Runner I change the contents of the first parameter of the Class.forName line to java.lang.String it works fine I can see the list of methods of the String class.

  • The `-cp` and `-jar` options are mutually exclusive. `-jar` causes `-cp` to be ignored. By the way, it’s usually better to load external implementations using [ServiceLoader](https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/ServiceLoader.html) rather than with reflection. – VGR Jun 23 '21 at 15:25
  • Thank you for your reply. When I remove the -jar command I have the error: "could not find or load the main class". Are you saying that it is impossible to do it this way? I have some reasons to go through this solution but you are right that in most cases service loader is better. – Stanislas Durand Jun 23 '21 at 18:01
  • Did you specify a path like `.:/path/to/myjar.jar` to the `-cp` option? – VGR Jun 23 '21 at 18:55
  • I made this order : java -cp .:/path/to/my/jar.jar mypackage.myrunnerwithmainclass "myarg1" "myarg2" I also moved the jar to another folder. I get a null pointer when I search for my implementation – Stanislas Durand Jun 24 '21 at 03:22
  • See https://stackoverflow.com/questions/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it. – VGR Jun 24 '21 at 11:43
  • Never mind the null pointer. If my algorithm does not work, it is because it cannot find my implementation and if it is not viable I will not have fun adding controls. Currently if I compile my jar with my implementation in my jar it works. When I try to put my implementation in a file it doesn't work. I have the impression that the java classpath is not viable in this specific case – Stanislas Durand Jun 25 '21 at 09:13
  • Perhaps it would help to show the code which doesn’t work in your question. Show us how you know that it doesn’t work. – VGR Jun 25 '21 at 11:48

0 Answers0