Load dynamic library java that inherit interface - java

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.

Related

Java, Load classes from JAR to use them later

I have a problem loading class from JAR file.
I can load them with URLClassLoader using a JarFile etc etc like in this answer; but if later in code I try to instantiate them with reflection:
Object obj = Class.forName(className).newInstance()
I get a ClassNotFoundException.
Can I dinamically load a class to use them later, when I need them, just like classes in ClassPath?
Thank you!
You need to provide class loader to Class.forName method - as otherwise it will look in this same class loader as your class is in.
Object obj = Class.forName("name", true, loader).newInstance() .
But you can't just load a class and then use it in your code like MyLoadedType - as here java does not know where to look for that class, unless you will ensure that your code and loaded code is in this same class loader - you can do this by running all your code from custom class loader that allows for adding new sources in runtime. (URLClassLoader allows for this but method is protected - so you need to extend it and make it public, in java 8 system class loader is also URLClassLoader - but this was changed in java 9) .
But you can operate on that code using reflections like I showed you.

Java : Interface in a class is not accessible while importing

I am using the maven dependency hive-hcatalog-core in my program
and this jar present in the project maven dependencies, with the interface (as in the image-top).
The interface ICacheableMetaStoreClient , though present the class, is NOT resolvable from import org.apache.hive.hcatalog.common. (image -bottom)
consequently, while doing a spark-submit, I am getting the exception :
com.google.common.util.concurrent.UncheckedExecutionException:
java.lang.IllegalArgumentException: interface
org.apache.hive.hcatalog.common.HiveClientCache$ICacheableMetaStoreClient
is not visible from class loader
What do I need to do for this to be visible from the program class path.
Lets look at the code:
class HiveClientCache {..}
The HiveClientCache has only package level visibility and it along with ICacheableMetaStoreClientwont wont be available for import outside of that package (this includes in your code).
Now lets look at ICacheableMetaStoreClient:
#InterfaceAudience.Private
public interface ICacheableMetaStoreClient extends IMetaStoreClient {....}
The interface is public but has annotation on it that makes the Hive/Hadoop additional preprocessing to check object type and throw IllegalArgumentException.
Here is the JavaDoc for InterfaceAudience:
Annotation to inform users of a package, class or method's intended
audience. Currently the audience can be InterfaceAudience.Public,
InterfaceAudience.LimitedPrivate or InterfaceAudience.Private. All
public classes must have InterfaceAudience annotation.
Public classes that are not marked with this annotation must be considered by default as InterfaceAudience.Private.
External applications must only use classes that are marked InterfaceAudience.Public. Avoid using non public classes as these
classes could be removed or change in incompatible ways.
Hadoop projects must only use classes that are marked InterfaceAudience.LimitedPrivate or InterfaceAudience.Public
Methods may have a different annotation that it is more restrictive compared to the audience classification of the class. Example: A class
might be InterfaceAudience.Public, but a method may be
InterfaceAudience.LimitedPrivate

Getting all classes with Javassist using a pattern

How can I get all classes using a pattern like "com.stackoverflow.*" with Javassist?
i found only 2 methods :
1/ Find a class by full name
CtClass ClassPool.getDefault().getCtClass("com.stackoverflow.user.name")
2/ Find a list of classes with fullnames :
CtClass[] ClassPool.getDefault().get(String [] arg0)
You could use some library like :
https://github.com/ronmamo/reflections
I don't think you can do that just with JRE classes.
Example from the doc :
Reflections reflections = new Reflections("my.project.prefix");
Set<Class<? extends SomeType>> subTypes =
reflections.getSubTypesOf(SomeType.class);
Set<Class<?>> annotated =
reflections.getTypesAnnotatedWith(SomeAnnotation.class);
Michael Laffargue's suggestion is the best way to go. The Reflections library uses javassist under the covers. Basically, javassist provides a means of reading raw byte code from class or jar files and extracting the class meta-data from it without actually class loading it, where as Reflections provides a richer API around locating (via classpath specifications) and filtering the set of classes you're looking for.
You can do the same thing yourself using javassist only, but you will be recreating some portion of the Reflections library. You could look at Reflections' source code to see how it works, but very generally speaking, it looks like this:
Locate the classpath you want to scan. This will usually be a group of directories with a tree of class files, or a group of Jar files, but could also include more complex structures like WARs or EARs (which Reflections supports quite nicely).
Add the root of the file system where the class files live, or the JAR file reference to your ClassPool instance.
Using a file system iteration or a JarInputStream, iterate through each class file or JarEntry. You can filter out any files or entries that do not match "com/stackoverflow/**.class"
For the remaining, using the name of the file or entry, derrive the class name and load it from the javassist class pool.
Use the loaded CtClass to apply any further search criteria.
Now you have your class reference list, release the ClassPool for garbage collection.

Scan Java classpath for lone subclass?

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/

Maven use resources from other modules?

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.

Categories

Resources