0

I've seen a couple answers to similar questions but unfortunately none have been able to answer mine.
I have a web application that offers a computation platform and in order to extend it I have written an interface, so that the application would check a specific folder for newly added jar files to load classes implementing the interface at runtime.
The problem I'm facing is that even though the class loading is successful am failing to cast the newly created object to my existing interface "ClassCastException"

ClassLoader sysClassLoader = ClassLoader.getSystemClassLoader();
URL directory = new URL("file:/Users/Abel/Desktop/Output/strategies/Classes/myJar.jar");
ClassLoader custom = new URLClassLoader(new URL[] {directory}, sysClassLoader);

Class strategyClass = custom.loadClass(className);
Object strategyObject = (Object) strategyClass.newInstance();
Strategy strategyInstance= (Strategy) strategyObject;

Strategy of course is the interface implementes the code (above) and returns the generated Strategy object so I would rather not use reflection in order to invoke the method contained in the loaded class and I apprehend that this would LAO end up with the same problem since the parameters of the method would be considered incompatible too.
From what I've read the class loader considers the existing interface and the one loaded along the class implementing it as two different types hence the exception.
Is there any work around?

Saurabh Jhunjhunwala
  • 2,832
  • 3
  • 29
  • 57
Abel
  • 150
  • 1
  • 9
  • 2
    Might the Strategy interface erroneously be in the myJar.jar too? – Joop Eggen Sep 09 '15 at 11:28
  • 1
    Which ClassLoader loads the Strategy interface? The custom classloader should probably have that classloader as parent, and not the system classloader. – JB Nizet Sep 09 '15 at 11:30
  • @joop yes in order to implement i created a separate eclipse project which contains a Strategy interface and the class implementing it. so yes the jar contains both. – Abel Sep 09 '15 at 11:39
  • @JB How to find which class loader is the one loading the interface? – Abel Sep 09 '15 at 11:42
  • Then the classloader of implementation class might first find the interface class in the jar, so have an API lib jar with the Strategy interface, the web app, and your implementation jar project. – Joop Eggen Sep 09 '15 at 11:42

3 Answers3

3

I know it's kinda weird to answer your own question but i'm gonna do it anyway in case someone has the same problem.

this is what i did step by step.

  • create a Jar file containing my interface as well as the model classes it needed.
  • I then installed the jar file into my local repository, since i'm using maven for dependency management, therefore this step is not required.
  • Next i created an implementation of my interface in a separate project where i used the aforementioned Jar file as a dependency.
  • I then added the Jar file of the additional strategy in the application folder which path is used for the class loading.
  • and then i changed my code to this:

    ClassLoader sysClassLoader = Strategie.class.getClassLoader();
    URL directory = new URL("file:" + path + "Strategies/Classes/" + strategie + ".jar");
    ClassLoader custom = new URLClassLoader(new URL[] {directory}, sysClassLoader);
    
    Class strategieClass = custom.loadClass(className);
    Object strategieObject = strategieClass.newInstance();
    Strategie strategieInstance= (Strategie) strategieObject;
    

changing the class loader avoids getting a classDef not found Exception.

Abel
  • 150
  • 1
  • 9
0

ClassCastException suggest that (any) class with that name is found (not null).

Maybe comes (dynamic loaded binary class) from different compilation that compilation of hosting (calling) sources? If such situation exist, class has the same name, but it is DIFFERENT class.

Or maybe loaders (main and dynamic) are preloaded with different sources (jars, classpath etc) im not sure in this part of answer.

Sample from real life: two webapp developed on diferent versions of the same framework (on Tomcat/jeety) can have conflict with classes with same names, but independent (if container don't give separation of loaders)

Jacek Cz
  • 1,872
  • 1
  • 15
  • 22
0

Try below code. The catch is your jar should be loaded via CustomClassLoader instead of default system class loader.

// During initialization in some method like initializeStrategy()

            CustomClassLoader customLoader =
                    new CustomClassLoader(getClass().getClassLoader(), new String[] {
                    <CLASSPATH_OF_IMPLEMENTOR_JAR>}
            try {
               specification = (SomeInterface) (customLoader.loadClass(<Implementor>).
                                newInstance());

            } catch (Exception ce) {
                ce.printStackTrace();
            }

//After uploading new jar, trigger an event via (JSP or servlet). Add this code block in some method e.g. reloadNewStrategy();

try {
           if (specification; != null) {
                specification;.unload();
            }
            CustomClassLoader customLoader =
                    new CustomClassLoader(getClass().getClassLoader(), new String[] {
                    <CLASSPATH_OF_IMPLEMENTOR_JAR>});
            specification =
                    (SomeInterface) (customLoader.loadClass(<Implementor>).newInstance());
        } catch (Exception err) {
            err.printStackTrace();
        }
}

Here specification is your Strategy Interface Implementor is strategy implementation object.

Ravindra babu
  • 37,698
  • 11
  • 250
  • 211