I have a rather unique situation where I know my Java web app will always be packaged with 1-and-only-1 concrete subclass of an AbstractWidget:
public abstract class AbstractWidget {
// ...
}
public class SimpleWidget extends AbstractWidget {
// ...
}
public class ComplexWidget extends AbstractWidget {
// ...
}
public class CrazyComplexWidget extends AbstractWidget {
// ...
}
// ...etc.
Again, I know at runtime that my WAR/WEB-INF/classes directory will always have 1-and-only-1 AbstractWidget impl packaged in it (no more, no less), be it ComplexWidget.class, SimpleWidget.class, etc.
I'm trying to construct code (that would actually run when the WAR starts up from inside its ServletContextListener impl) that would be able to scan the runtime classpath and obtain an instance (using public no-arg constructor) of the AbstractWidget.
Thus, if my WAR has:
myWar/
WEB-INF/
lib/
classes/
com/
myorg/
App (implements ServletContextListener)
... lots of other classes and packages
some/
arbitrary/
package/
SimpleWidget
Then, from inside App#contextInitialized(ServletContextEvent) I need code that will find SimpleWidget.class on the classpath and give me an instance of it:
public class App implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
// Scan classpath for the lone AbstractWidget impl somehow.
???
// Use public, no-arg ctor to instantiate the impl.
AbstractWidget widget = ???
// Now do stuff with widget...
}
}
I know you can use reflection methods like Class.isAssignableFrom(), but not sure if that is the correct way to go, and even if it is, how to use it for my given use case. Any ideas? Thanks in advance!
You might want to check out the reflections api. It has utilities for finding the subclass(es) of a given class. You can do something like this with it:
Reflections reflections = new Reflections();
Set<Class<? extends AbstractWidget>> subclasses;
subclasses = reflections.getSubTypesOf(AbstractWidget.class);
This will get you a Set containing all the subclasses of the AbstractWidget class on the classpath. http://code.google.com/p/reflections/
I know it is very old question but ClassGraph library can help you very easily get all classes in the classpath extending a given class, basically all subclasses of a given class.
Gradle dependency
compile group: 'io.github.classgraph', name: 'classgraph', version: '4.8.46'
This how you can easily you can scan classpath
ScanResult scanResult = new ClassGraph()
.whitelistPackages("com.myorg") //whatever package you want to scan
.verbose()
.enableAllInfo()
.scan();
System.out.println("Classes which extending '" + AbstractWidget.class.getSimpleName() + "' class");
ClassInfoList classInfoList = scanResult.getSubclasses(AbstractWidget.class.getName());
for (ClassInfo classInfo : classInfoList) {
System.out.println("\t" + classInfo.getName());
}
Output assuming you have SimpleWidget class in the classpath
Classes which extending 'AbstractWidget' class
com.myorg.some.arbitrary.package.SimpleWidget
I hope it helps. ClassGraph is really powerful library and can do much more this simple need. Its explain really well in this article https://readtorakesh.com/java-classpath-scanning-using-classgraph/
Related
Edit: A follow-up question based on this discussion was published in the following link.
Android: How to manage common codebase in multiple libraries used by the same application
I have two android aar library projects: LibA using ClassA, and LibB using ClassB. Both libs have the same base package. both libs use the same class named BaseClass, currently resides separately within each lib in package name 'common'. BaseClass contains one method named baseMethod.
This creates two libs using a class with the same name and a different implementation.
this is how the classes look like:
ClassA:
package mybasepackage.a;
import mybasepackage.common.BaseClass;
public class ClassA {
BaseClass baseClass;
public ClassA() {
this.baseClass= new BaseClass();
}
public String myPublicMethod(){
return this.baseClass.baseMethod();
}
}
ClassB:
package mybasepackage.b;
import mybasepackage.common.BaseClass;
public class ClassB {
BaseClass baseClass;
public ClassB() {
this.baseClass = new BaseClass();
}
public String myPublicMethod(){
return this.baseClass.baseMethod();
}
}
BaseClass In LibA:
package mybasepackage.common;
public class BaseClass{
public String baseMethod() {
return "Called from ClassA";
}
}
BaseClass in LibB:
package mybasepackage.common;
public class BaseClass{
public String baseMethod() {
return "Called from ClassB";
}
}
When I try to compile both libs in the same app, it throws a duplicated class error: "Program type already present: mybasepackage.common.BaseClass", this happens because the compiler cannot know which BaseClass to compile since it resides within both libs.
My goal is to allow both aar libs to compile successfully within the same app, while providing different implementations for the BaseClass. More formally, LibA and LibB should compile in the same application such as:
Calling new ClassA().baseMethod() will return "Called from ClassA".
Calling new ClassB().baseMethod() will return "Called from ClassB".
Pre condition: I cannot change the base package name in one of the libs because it essentially creates an unwanted duplication of BaseClass.
NOTE: I'm aware this may not be possible via the aar approach. If that is truly the case, I'm willing to consider other deployment architectures as long as I'll be able to compile these libs with the same common class using different implementations, as described in the question.
My goal is to allow both aar libs to compile successfully within the same app, while providing different implementations for the BaseClass
That is not possible, sorry.
I'm aware this may not be possible via the aar approach.
It has nothing to do with AARs. You cannot have two classes with the same fully-qualified class name in the same app, period. It does not matter where those duplicate classes come from.
I'm willing to consider other deployment architectures as long as I'll be able to compile these libs with the same common class using different implementations, as described in the question.
That is not possible, sorry. Again: it does not matter where the duplicate classes come from. You simply cannot have duplicate classes.
Given your precondition you just can't do that in this way. You cannot have 2 different libraries in java with the same package name, which is the main problem that throws your error (and not the name of the classes).
What you can do and maybe if possible is the best way to handle with that is to merge the two libraries into just one and add two subpackages inside and then just import them:
import mybasepackage.common.a_name.BaseClass; // class A
import mybasepackage.common.b_name.BaseClass; // class B
This will prevent the duplication error because they just have the same name but from different packages.
Another idea if this way doesn't fit your expectation is to change the architecture by implementing another abstraction layer in which you define your BaseClass as an abstract method:
package mybasepackage.common;
public class abstract BaseClass{
public String myPublicMethod();
}
and then you just implement the method inside ClassA and ClassB:
public class ClassA implements BaseClass{
public ClassA() {
super();
}
#Override
public String myPublicMethod(){
// logic for A
}
}
NB note that the above implementation of class A is just a stub and it is not supposed to work as it is. Adapt to your need.
In any case by the way you can't have two packages with same classes name.
Just build three artifacts, because two artifacts will always require an exclude on one of the dependencies set. When the two -liba and -libb libraries depend on a third -base, -core or -common library, there are no duplicate classes - and if you want to keep the package name, just make the package name depend on all of them, alike a meta-package:
mybasepackage
|
mybasepackage-liba -> mybasepackage-common
|
mybasepackage-libb -> mybasepackage-common
mybasepackage-common
I want to load dynamic library where classes inherit from an interface/abstract class on my core project, so I can load my classes at runtime and use it. How can i do that ?
Example:
Core: ITrigger (interface)
Library: {MyTriggerOne extends ITrigger} {MyTriggerTwo extends ITrigger}
If you want to load a class/library dynamically use Class.forName('class name') method to load.
I had the same requirement and I used the library Reflections.
Very simple code snippet:
public Set<Class<? extends ITrigger>> getITriggerClasses() {
final Reflections reflections = new Reflections("package.where.to.find.implementations");
return reflections.getSubTypesOf(ITrigger.class);
}
Then you can use the method Class::newInstance to create the ITrigger(s).
This is a very simple example, there are several options to initialize the Reflections class (not only with one package name).
Java's SPI(Service Provider Interface) libraries allow you to load classes dynamically based on the interfaces they implement, that can be done with the help of META-INF/services.
You can create a interface like
package com.test.dynamic;
public interface ITrigger {
String getData();
String setData();
}
you can use the ServiceLoader class to load the interface like below code
ServiceLoader<ITrigger> loader = ServiceLoader.load(ITrigger.class);
then you can perform all the operation on it.
If you have some other implementing classes on your classpath, they register themselves in META-INF/services.
you need to create a file in META-INF/services in your classpath with the following properties
The name of the file is the fully qualified class name of the
interface, in this case, it's com.test.dynamic.ITrigger
The file contains a newline-separated list of implementations, so
for the example implementation, it would contain one line:
com.test.dynamic.impl.SomeITriggerImplementation class.
I'm developing a grails app, and I need to modify a groovy class that is in a plugin, so I decided to override the class, so I have these method and class in my plugin:
def example = new a();
a.method();
class a {
void method() {
println "2";
}
}
all this was Inside the plugin, so I want to create another class in the same package in my project, to change the method, but how can I set my new class to run instead the plugin's? or is it impossible?
class a {
void method() {
println "4";
}
}
Yes, you just need to ensure that your class is on the classpath before the plugin's version.
Yes you can. It is called class shadowing. But I would advice against it most of the times. You only need to let the jvm load your class before the plugin class.
I have several classes which implement two interfaces. All of them implement the BaseInterface and some other interface which is specific to them.
I want to be able to use the loadClass method below to instantiate classes which are referred to in a .properties file and call the common method they all contain (because they implement BaseInterface).
public interface BaseInterface {
public void doBase();
}
public interface SpecificInterface extends BaseInterface {
public void doSpecific();
}
public class SpecificClass implements SpecificInterface {
public void doBase() { ... }
public void doSpecific() { ... }
}
public class LoadClass() {
private PropertiesLoader propertiesLoader = new PropertiesLoader();
public <C extends BaseInterface> C loadClass(String propertyName) {
Class<C> theClass;
// Load the class.
theClass = propertiesLoader.getPropertyAsClass(propertyName);
// Create an instance of the class.
C theInstance = theClass.newInstance();
// Call the common method.
theInstance.doBase();
return theInstance;
}
}
Unfortunately, when I run the code:
loadClassInstance.loadClass("SpecificClass");
I get the following exception:
Exception in thread "main" java.lang.ClassCastException:
SpecificClass cannot be cast to BaseInterface
at LoadClass.loadClass
Any ideas how I would solve this issue?
Many Thanks, Danny
Java's Service Provider Interface (SPI) libraries allow you to load classes with public parameterless constructors dynamically based on the interfaces they implement, and it's all done through the use of META-INF/services.
First, you'll need the interface:
package com.example;
public interface SomeService {
String getServiceId();
String getDisplayName();
}
Then when you need them, you can load them using Java's ServiceLoader class, which implements Iterable:
ServiceLoader<SomeService> loader = ServiceLoader.load(SomeService.class);
for (SomeService serv : loader) {
System.out.println(serv.getDisplayName());
}
Then when you have 1 or more implementing classes on your classpath, they register themselves in META-INF/services. So if you have the implementation:
package com.acme;
public class SomeImplementation implements SomeService {
// ...
public SomeImplementation() { ... }
// ...
}
Note that this class needs a default no-args constructor, this is not optional.
You register it with the class loader by creating a file in META-INF/services in your classpath (such as in the root of your jar) with the following properties:
The name of the file is the fully qualified class name of the interface, in this case, it's com.example.SomeService
The file contains a newline-separated list of implementations, so for the example implementation, it would contain one line: com.acme.SomeImplementation.
And there you go, that's it. How you build your project will determine where you put the META-INF/services stuff. Maven, Ant, etc. all have ways of handling this. I recommend asking another question about your specific build process if you have any trouble adding these files to your build.
If you replace your code with below it works. I doubt that PropertiesLoader is doing something that is not supposed to be done.
Class<?> theClass;
// Load the class.
theClass = Class.forName("SpecificClass");
// Create an instance of the class.
C theInstance = (C) theClass.newInstance();
BaseInterface base = loadClass();//There is no problem in casting
Java program normally is loaded by system classloader. The classes which are referred to in a .properties file are loaded by a user-defined classloader. Classes loaded by different classloaders are considered different even if have same name and are loaded from same classfile. In your case, the interface BaseInterface loaded by system classloader is different from the BaseInterface loaded by
PropertiesLoader.
To fix this, PropertiesLoader should delegate loading of BaseInterface to system classloader. Typical way to do so is to use system classloader as a parent classloader for PropertiesLoader.
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.