I have one Java project (Project A) in which I load other jars. These jars (Project B) all have one class implements an application interface. In order to know that interface, project b has to have project A as library.
Now, when starting project A, it loads the jar of project B, looks for the application class, gets the constructor and trys to create an object, but this fails:
The constructor from B's class creates an object implementing the interface which is known to project B, ALTHOUGH this is the same interface as in project A, but it seems that these two are not seen as being equal.
How can I solve this double bind dependency?
Edit:
This is how I try to create an application in project A:
clazz = Class.forName(className, true, loader);
ctor = (Constructor<? extends Application>) clazz.getConstructor();
Application app = ctor.newInstance();
The exception occurs in the third line where it says app is of type SpecificApplication (which is defined in the loaded jar and implements the Application interface) and thus cannot be cast to Application.
Put the interface in another jar, which is referenced by projects A and B.
Related
I have a webapp project, which I am running through tomcat. The webapp project has a dependency of a core project. (The core project jar is present in WEB-INF/lib folder).
I have another sample project which has same core project dependency. I need to load a class and invoke a particular method present in the sample project jar via reflection.
I need to load core project's classes that are being used in the sample project as well as web app project via common class loader.
The problem is :- I am unable to load core project classes and sample project classes in a common class loader.
I have tried a lot from past two days like loading up classes using a custom class loader and system class loader, but I always end up getting either NoSuchMethodException or ClassCastException or ClassNotFoundException.
How should I handle this situation of having a common class loader?
Class which I need to load and invoke a method (Sample Project)
public class ObjectiveFunction implements ScriptInterface{//ScriptInterface is a interface present in core project
#Override
public void execute(Map<Integer, CustomObject> map, IModel inputModel, List<AbstractNode> nodes) {
for (Node node : nodes){// this line throws ClassCastException
// IModel is an interface in core project
// AbstractNode (abstract class), CustomObject and Node are present in core project. Different nodes extends AbstractNode
// Some logic
}
Class loading code present in core project which is invoked by webapp
project
URLClassLoader classLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(classLoader, url);
Class<?> classToLoad = classLoader.loadClass(className);
Method executeMethod = classToLoad.getDeclaredMethod("execute", Map.class, IModel.class, List.class);
Object instance = classToLoad.newInstance();
executeMethod .invoke(instance, value1, value2, value3); // This line throws NoSuchMethodException
I have a problem with loading an object which is instantiated by two different classloaders. Basically I implement a webapp with three different plugins, each with its own classloader. The project structure looks like this:
MyAppService
- ObjectInterface.java
MyAppImpl
- ObjectImplementation.java
MyClass
- MyClass.java
Its a maven based project. MyAppImpl and MyClass both have MyAppService as a dependency. Now I use this code to create an object of class ObjectImplementation in class ObjectImplementation.
ObjectInterface o = new ObjectImplementation();
I want to pass this object to a method in class Myclass, where I get the object with this code.
ObjectInterface o = (ObjectInterface) passedObject;
But I get the exception java.lang.ClassCastException: MyAppImpl.ObjectImplementation cannot be cast to MyAppService.ObjectInterface. I also tried dynamic class loading with the code
ObjectInterface o = (ObjectInterface) Class.forName("MyAppImpl.ObjectImplementation").newInstance();
but I get the exception java.lang.ClassNotFoundException: MyAppImpl.ObjectImplementation. Adding MyAppImpl as a dependency for MyClass is currently not an option. Does anyone know if there is a solution for my problem?
Please read about ClassLoaders and its hierarchy. Also read about delegation mechanism.
In your case, classes are loaded by leaf classloaders and there are different copies of the same class. If the particular class is not loaded by current class loader or any of its parent then you will get the java.lang.ClassCastException.
I have couple of maven projects let's say A & B. I have mvm installed them both before loading them inside eclipse.
One of the class in project A runs a class from project B. This class is trying to create a instance of a another class in B using the newInstance method.
Class clazz = Class.forName(className);
Constructor construct = clazz.getConstructor(new Class[] { String.class, ThreadFactory.class });
ResultClassFromB api = (ResultClassFromB) construct.newInstance(new Object[] {file, threadFactory });
I get InvocationTargetException for above code. On the surface, I understand eclipse can't find class file for ResultClassFromB. I've tried adding the parent folder of ResultClassFromB in the class path of project A but I still see the issue. How do I get past this?
info: file is mostly with comments.
I have many more classes in the project but for now please consider only A, B and C classes.
abstract public class A {...}
public class B extends A {...}
public class C extends A {...}
Then later I have a code, say in class D, like this
A a = new B();
//A a = new C();
//use a's methods
So my question now is how to easily configure in Eclipse building of two separate JARs. First one should have B.class included and C.class excluded and code as A a = new B(); The second one should have C.class included and B.class excluded and code as A a = new C();
I do not know many things about Ant and Maven. Do I need to use them in this case?
Maybe, something wrong with my design, if so, please let me know.
What you want to is mainly a code loading problem, not an Ant problem.
Just the two statements you presented for creating a new class instance:
A a = new B();
A a = new C();
The constructors are called using static code. Ant can not change the code, therefore the only way with Ant I see is generating a Factory class with Ant as part of the build process, depending if A or C is included into the JAR
But that would result in a project that can no longer be used directly in Eclipse as the original source code in the Eclipse project misses the factor class.
A IMHO better approach is dynamic class loading (may be combined with reflection). You can automatically search for classes that extend A or you add a configuration file/info to the JAR specifying which class to create (e.g. properties file).
Place the properties file in the src folder with the Java file and load it via this.getClass().getResource("myclass.config");
The config file can contain the class name that should used for creating a new instance.
The following code snipped assumes that B and C both have a public constructor that does not take any argument:
String classNameToLoad = ... // loaded from config file example "mypackage.B"
final Class<?> c = Class.forName(classNameToLoad);
final Class<? extends A> ac = c.asSubclass(A.class);
final Constructor<? extends A> a_ctor = ac.getConstructor();
final A a = a_ctor.newInstance();
In your eclipse you if you right click your project, you will have an option Export
Click on that, and select jar, then name the jar, select the classes you want in it.
Repeat this for the other jar.
check this http://help.eclipse.org/juno/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Ftasks%2Ftasks-33.htm
The easiest way to do this would be to have 3 separate Java projects in Eclipse. Based on your example, that would be
B code project
C code project
A common code
Eclipse allows you to reference the bin directory of another project from your project in the build path.
Then you can export (or use Ant) the B and C projects as separate projects.
how can I use resources from other maven modules? My goal is to provide a AbstractImportClass as well as the to be imported files in a specific maven module. And use this module within other modules extending this class.
Let's say ModuleA contains src/main/java/MyAbstractImportClass.java, and src/main/resources/MyImport.csv
I now want to use the abstract import class in ModuleB. Or rather, I will extend it, use the abstract-fileimport, and a few custom functions.
Then ModuleC also uses the abstracts' import and some custom functions.
The problem is: the import in abstract class goes with reader and InputStream. When I execute just ModuleA everything is fine.
But when I tried to include the module via maven pom, and then extend the module to call the import, then I get NullPointerException at the line where the reader is used.
So obvious I cannot use foreign module resources this way.
But how could I instead make use of this?
Update:
Module A:
src/main/java/path/to/MyClassA.java
src/main/resources/path/to/test.txt
abstract class MyClassA {
public static String TESTFILE = test.txt;
List<String> doImport(String filename) {
InputStream fileStream = resourceClass.getResourceAsStream(filename);
//some precessing
return list;
}
}
Module B:
src/main/java/path/to/MyClassB.java
class MyClassB implements MyClassA {
List<String> list = doImport(TESTFILE);
}
If I put MyClassB in same dir as A, then everything works fine.
If I build B in a own module I get NullPointer for InputStream, what means the file is not found.
I don't think your problem is related to Maven at all. Class.getResourceAsStream() resolves relative paths as relative to the class object that you call it on. Therefore, if you use that method in an abstract class, every subclass of it could be looking for the resource in a different place.
For example, given three classes:
Super:
package com.foo;
public class Super {
{ System.out.println(getClass().getResourceAsStream("test.properties")); }
}
Sub1, a subclass of Super:
package com.foo.bar;
import com.foo.Super;
public class Sub1 extends Super {}
Sub2, another subclass:
package com.foo.bar.baz;
import com.foo.Super;
public class Sub2 extends Super {}
If you create a Super, it'll look for the classpath resource "/com/foo/test.properties" because that's how the path "test.properties" resolves relative to the class com.foo.Super. If you create a Sub1, it'll look instead in "/com/foo/bar/test.properties", and for a Sub2 instance, it'll look in "/com/foo/bar/baz/test.properties".
You might want to use an absolute path to the resource instead of a relative one, or else have the subclasses specify paths relative to themselves. It depends on your design and what kind of abstraction you're trying to achieve.
It's not exactly clear what your code does. Could you provide sample of how you're reading resource? If you do it properly - by getting InputStream from resource file in classpath there should be no problem. You can start by checking that ModuleA.jar has your resource file inside.
You should check:
Module B depend on Module A in pom.xml
The passed in 'filename' parameter starts with a '/', that is to say, the 'filename' parameter is '/path/to/test.txt' other than 'path/to/test.txt'
You program should work if these two conditions is satisfield.