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.
Related
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 have several data processing algorithms that can be assembled into a pipeline to transform data. The code is split into two components: A pre-processing component that does data loading-related tasks, and a processing pipeline component.
I currently have the two parts compiled and packaged into two separate jars. The idea is that the same pre-processing jar can be shipped to all customers, but the pipeline jar can be exchanged depending on customer requirements. I would like to keep the code simple and minimize configuration, so that rules out the use of OSGi or CDI frameworks.
I've gotten some hints by looking at SLF4J's implementation. That project is split into two parts: A core API, and a bunch of implementations that wrap different logging APIs. The core API makes calls to dummy classes (which exist in the core project simply to allow compilation) that are meant to be overridden by the same classes found in the logging projects. At build time, the compiled dummy classes are deleted from the core API before packaging into jar. At run time, the core jar and a logging jar are required to be included in the class path, and the missing class files in the core jar will be filled in by the files from the logging jar. This works fine, but it feels a little hacky to me. I'm wondering if there is a better design, or if this is the best that be done without using CDI frameworks.
Sounds like the strategy software design pattern.
https://en.wikipedia.org/wiki/Strategy_pattern
Take a look at the ServiceLoader.
Example Suppose we have a service type com.example.CodecSet which is
intended to represent sets of encoder/decoder pairs for some protocol.
In this case it is an abstract class with two abstract methods:
public abstract Encoder getEncoder(String encodingName);
public abstract Decoder getDecoder(String encodingName);
Each method returns an appropriate object or null if the provider does
not support the given encoding. Typical providers support more than one
encoding. If com.example.impl.StandardCodecs is an implementation of
the CodecSet service then its jar file also contains a file named
META-INF/services/com.example.CodecSet
This file contains the single line:
com.example.impl.StandardCodecs # Standard codecs
The CodecSet class creates and saves a single service instance at
initialization:
private static ServiceLoader<CodecSet> codecSetLoader
= ServiceLoader.load(CodecSet.class);
To locate an encoder for a given encoding name it defines a static factory method which iterates
through the known and available providers, returning only when it has
located a suitable encoder or has run out of providers.
public static Encoder getEncoder(String encodingName) {
for (CodecSet cp : codecSetLoader) {
Encoder enc = cp.getEncoder(encodingName);
if (enc != null)
return enc;
}
return null;
}
A getDecoder method is defined similarly.
You already understand the gist of how to use it:
Split your project into parts (core, implementation 1, implementation 2, ...)
Ship the core API with the pre-processor
Have each implementation add the correct META-INF file to its .jar file.
The only configuration files that are necessary are the ones you package into your .jar files.
You can even have them automatically generated for you with an annotation:
package foo.bar;
import javax.annotation.processing.Processor;
#AutoService(Processor.class)
final class MyProcessor extends Processor {
// …
}
AutoService will generate the file
META-INF/services/javax.annotation.processing.Processor
in the output classes folder. The file will contain:
foo.bar.MyProcessor
I want to get all the package names from an separate Java project, how would I do this in Java preferably using reflections?
I have imported the project into my build path and I've tried using the code below to get the package names.
Package[] pack = Package.getPackages();
EDIT: I'm not using jar files, I have just imported the project as it will be in the same dir. I only need packages that I created, specifically packages which begin with a certain directory.
You really can't know every package name in the libraries by just using getPackages() as it only will list the ones known to the classloader. This means that if a class has not yet been loaded from a specific package, then it will be absent from the list.
Use the zip file handling routines to open the jar files, and read out the packages by directory name. With this technique, you will discover all package names, even those that are not yet "in use". Basically, any path that contains a .class file is a "package name".
---- Edited as further details indicates JAR files are not being used ---
Since you aren't using JAR files (and why not? they're really good!), you will have to scan directories. To "find" the directories, you will need to chop up the class path. The way to get the class path is:
String classpath = System.getProperty("java.class.path", null);
Then you do a search of all the directories under each "starting point" that has a .class file in it. Once you have all of those, you have all of the packages.
Again, it is not possible to know all the packages by just asking the classloader, unless you could somehow guarantee the class loader has loaded all of the classes (which it typically doesn't).
As long as you don't need empty packages, something like this should work:
/**
* Finds all package names
* #return Set of package names
*/
public Set<String> findAllPackages() {
List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
classLoadersList.add(ClasspathHelper.contextClassLoader());
classLoadersList.add(ClasspathHelper.staticClassLoader());
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setScanners(new SubTypesScanner(false), new ResourcesScanner())
.setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
.filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("my.base.package"))));
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
Set<String> packageNameSet = new TreeSet<String>();
for (Class classInstance : classes) {
packageNameSet.add(classInstance.getPackage().getName());
}
return packageNameSet;
}
It finds all classes, loops through them and gets the package name stored in a Set to avoid duplicates.
Unfortunately, we can't just use the code below with a normal Reflections object without specifying a custom configuration to allow Object scanning.
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
I have an application that allows, using an abstract class, people to write their own implementations. I load these implementations as .class-files from a directory. Currently, I have this solution:
File classDir = new File("/users/myproject/classes/");
URL[] url = { classDir.toURI().toURL() };
URLClassLoader urlLoader = new URLClassLoader(url);
String filename;
for (File file : classDir.listFiles()) {
filename = string.getFilenameWithoutExtension(file);
if (filename.equals(".") || filename.equals("..") || filename.startsWith("."))
continue;
AbstractClass instance = (AbstractClass)urlLoader
.loadClass("org.mypackage." + filename)
.getConstructor(ConfigUtil.class, DatabaseUtil.class, StringUtil.class)
.newInstance(config, database, string));
instance.doSomething();
}
As you see - I need to specify the package the classes are located in in order to correctly load them. Omitting the package, I get an
java.lang.NoClassDefFoundError:
MyClass (wrong name: org/mypackage/MyClass)
error.
Now, from a architectural POV, I think it is very ill-designed that classes other people designed have to be compiled to MY package when loading them.
So I ask you: Is there a way I can load classes form the file system without having to specify the package they reside in?
Yes; implement an interface (or use an annotation).
Then use any class-scanning library (there are lots of SO questions about this, like this one) to load the particular class in question. Searching for "Java class scanning" or "Java plugin mechanism" will help.
You might also just want to use the Java Plugin Framework and avoid some effort. Although it's not clear to me that it's maintained any more, I know people are still using it.
You can use the ServiceProvider to load implementations which you don't know.
I'm playing around with BCEL. I'm not using it to generate bytecode, but instead I'm trying to inspect the structure of existing compiled classes.
I need to be able to point to an arbitrary .class file anywhere on my hard drive and load a JavaClass object based on that. Ideally I'd like to avoid having to add the given class to my classpath.
The straightforward way is to create a ClassParser with the file name and call parse(). Alternatively you can use SyntheticRepository and supply a classpath (that is not your classpath, IYSWIM).
The existing .class can be class loaded to java lang class object. Then it can be converted into BCEL intermediate javaclass structure. The following code may help:-
Class<?> javaClass1 = null;
javaClass1 = ucl.loadClass("com.sample.Customer");
org.apache.bcel.classfile.JavaClass javaClazz1=org.apache.bcel.Repository.lookupClass(javaClass1);
new ClassParser(classfilebytearrayhere).parse()