Getting all Classes from a Package - java

Lets say I have a java package commands which contains classes that all inherit from ICommand can I get all of those classes somehow? I'm locking for something among the lines of:
Package p = Package.getPackage("commands");
Class<ICommand>[] c = p.getAllPackagedClasses(); //not real
Is something like that possible?

Here's a basic example, assuming that classes are not JAR-packaged:
// Prepare.
String packageName = "com.example.commands";
List<Class<ICommand>> commands = new ArrayList<Class<ICommand>>();
URL root = Thread.currentThread().getContextClassLoader().getResource(packageName.replace(".", "/"));
// Filter .class files.
File[] files = new File(root.getFile()).listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".class");
}
});
// Find classes implementing ICommand.
for (File file : files) {
String className = file.getName().replaceAll(".class$", "");
Class<?> cls = Class.forName(packageName + "." + className);
if (ICommand.class.isAssignableFrom(cls)) {
commands.add((Class<ICommand>) cls);
}
}

Below is an implementation using the JSR-199 API, i.e. classes from javax.tools.*:
List<Class> commands = new ArrayList<>();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(
null, null, null);
StandardLocation location = StandardLocation.CLASS_PATH;
String packageName = "commands";
Set<JavaFileObject.Kind> kinds = new HashSet<>();
kinds.add(JavaFileObject.Kind.CLASS);
boolean recurse = false;
Iterable<JavaFileObject> list = fileManager.list(location, packageName,
kinds, recurse);
for (JavaFileObject classFile : list) {
String name = classFile.getName().replaceAll(".*/|[.]class.*","");
commands.add(Class.forName(packageName + "." + name));
}
Works for all packages and classes on the class path, packaged in jar files or without. For classes not explicitly added to the class path, i.e. those loaded by the bootstrap class loader, try setting location to PLATFORM_CLASS_PATH instead.

Here is an utility method, using Spring.
Details about the pattern can be found here
public static List<Class> listMatchingClasses(String matchPattern) throws IOException {
List<Class> classes = new LinkedList<Class>();
PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver();
Resource[] resources = scanner.getResources(matchPattern);
for (Resource resource : resources) {
Class<?> clazz = getClassFromResource(resource);
classes.add(clazz);
}
return classes;
}
public static Class getClassFromResource(Resource resource) {
try {
String resourceUri = resource.getURI().toString();
resourceUri = resourceUri.replace(esourceUri.indexOf(".class"), "").replace("/", ".");
// try printing the resourceUri before calling forName, to see if it is OK.
return Class.forName(resourceUri);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}

If you do not want to use external depencies and you want to work on your IDE / on a JAR file, you can try this:
public static List<Class<?>> getClassesForPackage(final String pkgName) throws IOException, URISyntaxException {
final String pkgPath = pkgName.replace('.', '/');
final URI pkg = Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource(pkgPath)).toURI();
final ArrayList<Class<?>> allClasses = new ArrayList<Class<?>>();
Path root;
if (pkg.toString().startsWith("jar:")) {
try {
root = FileSystems.getFileSystem(pkg).getPath(pkgPath);
} catch (final FileSystemNotFoundException e) {
root = FileSystems.newFileSystem(pkg, Collections.emptyMap()).getPath(pkgPath);
}
} else {
root = Paths.get(pkg);
}
final String extension = ".class";
try (final Stream<Path> allPaths = Files.walk(root)) {
allPaths.filter(Files::isRegularFile).forEach(file -> {
try {
final String path = file.toString().replace('/', '.');
final String name = path.substring(path.indexOf(pkgName), path.length() - extension.length());
allClasses.add(Class.forName(name));
} catch (final ClassNotFoundException | StringIndexOutOfBoundsException ignored) {
}
});
}
return allClasses;
}
From: Can you find all classes in a package using reflection?

Start with public Classloader.getResources(String name). Ask the classloader for a class corresponding to each name in the package you are interested. Repeat for all classloaders of relevance.

Yes but its not the easiest thing to do. There are lots of issues with this. Not all of the classes are easy to find. Some classes could be in a: Jar, as a class file, over the network etc.
Take a look at this thread.
To make sure they were the ICommand type then you would have to use reflection to check for the inheriting class.

This would be a very useful tool we need, and JDK should provide some support.
But it's probably better done during build. You know where all your class files are and you can inspect them statically and build a graph. At runtime you can query this graph to get all subtypes. This requires more work, but I believe it really belongs to the build process.

Using Johannes Link's ClasspathSuite, I was able to do it like this:
import org.junit.extensions.cpsuite.ClassTester;
import org.junit.extensions.cpsuite.ClasspathClassesFinder;
public static List<Class<?>> getClasses(final Package pkg, final boolean includeChildPackages) {
return new ClasspathClassesFinder(new ClassTester() {
#Override public boolean searchInJars() { return true; }
#Override public boolean acceptInnerClass() { return false; }
#Override public boolean acceptClassName(String name) {
return name.startsWith(pkg.getName()) && (includeChildPackages || name.indexOf(".", pkg.getName().length()) != -1);
}
#Override public boolean acceptClass(Class<?> c) { return true; }
}, System.getProperty("java.class.path")).find();
}
The ClasspathClassesFinder looks for class files and jars in the system classpath.
In your specific case, you could modify acceptClass like this:
#Override public boolean acceptClass(Class<?> c) {
return ICommand.class.isAssignableFrom(c);
}
One thing to note: be careful what you return in acceptClassName, as the next thing ClasspathClassesFinder does is to load the class and call acceptClass. If acceptClassName always return true, you'll end up loading every class in the classpath and that may cause an OutOfMemoryError.

You could use OpenPojo and do this:
final List<PojoClass> pojoClasses = PojoClassFactory.getPojoClassesRecursively("my.package.path", null);
Then you can go over the list and perform any functionality you desire.

Related

URI for third party dependency

My Question:
Is it possible to get a Uri from an import of a third party dependency?
Question Context:
I am trying to get a list of classes accessed with the following.
import com.name.*
In the context in which I want to use it, I will not be able to use third party dependencies. I do however need to find all classes associated with a third party dependency import.
I have found one such answer to my issue in the following code, provided by the user tirz.
public static List<Class<?>> getClassesForPackage(final String pkgName) throws IOException, URISyntaxException {
final String pkgPath = pkgName.replace('.', '/');
final URI pkg = Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource(pkgPath)).toURI();
final ArrayList<Class<?>> allClasses = new ArrayList<Class<?>>();
Path root;
if (pkg.toString().startsWith("jar:")) {
try {
root = FileSystems.getFileSystem(pkg).getPath(pkgPath);
} catch (final FileSystemNotFoundException e) {
root = FileSystems.newFileSystem(pkg, Collections.emptyMap()).getPath(pkgPath);
}
} else {
root = Paths.get(pkg);
}
final String extension = ".class";
try (final Stream<Path> allPaths = Files.walk(root)) {
allPaths.filter(Files::isRegularFile).forEach(file -> {
try {
final String path = file.toString().replace('/', '.');
final String name = path.substring(path.indexOf(pkgName), path.length() - extension.length());
allClasses.add(Class.forName(name));
} catch (final ClassNotFoundException | StringIndexOutOfBoundsException ignored) {
}
});
}
return allClasses;
}
The problem I have with the code above is that where final URI pkg is assigned. This works with a package that exists within the project, but if an import for a third party dependency is used this throws a NullPointerException. Is it possible to make this code work for third party dependencies? Might this require some reference to an .m2 folder or other library resource?

Import of Index content with ReadOnlyPDOMProvider

This articles describes how to generate and import a PDOM index.
After invoking the generation application GeneratePDOM I got a pdom file /home/sadik/eclipse-2019-06/eclipse/pdomExample.pdom. But I have problem importing the file.
The command to generate is this:
java -jar plugins/org.eclipse.equinox.launcher_1.5.400.v20190515-0925.jar -application "org.eclipse.cdt.core.GeneratePDOM" -target /home/sadik/eclipse-2019-06/eclipse/pdomExample.pdom -source /home/sadik/my-plugin-runtime-2019-06/CDTTest_Local/ -id cdttest_01 -indexer org.eclipse.cdt.core.myfastIndexer
Note the target and source arguments.
To test the import I wrote a class that implements IReadOnlyPDOMProvider
public class MyReadOnlyPDOMProvider implements IReadOnlyPDOMProvider {
public MyReadOnlyPDOMProvider() {
System.out.println("PDOMProvider");
}
#Override
public boolean providesFor(ICProject project) throws CoreException {
return true;
}
#Override
public IPDOMDescriptor[] getDescriptors(ICConfigurationDescription config) {
final IPath fileBase = Path.fromOSString("/home/sadik/eclipse-2019-06/eclipse/");
final IPath projectBase = Path.fromOSString("/home/sadik/my-plugin-runtime-2019-06/CDTTest_Local/");
return new IPDOMDescriptor[] { new IPDOMDescriptor() {
public IIndexLocationConverter getIndexLocationConverter() {
return new URIRelativeLocationConverter(URIUtil.toURI(projectBase));
}
public IPath getLocation() {
IPath path = fileBase.append("pdomExample.pdom");
return path;
}
}};
}
Are the paths correct? I actually don't know what location is supposed to be returned here.
I defined that class in the CDT extension point CIndex in my Plugin's plugin.xml:
<extension
point="org.eclipse.cdt.core.CIndex">
<ReadOnlyPDOMProvider
class="de.blub.plugin.MyReadOnlyPDOMProvider">
</ReadOnlyPDOMProvider>
</extension>
I'm testing with this file (/home/sadik/my-plugin-runtime-2019-06/CDTTest_Local/tests/indexer/usage.cc):
#include <declaration.h>
int main() {
int a = testThis();
}
When I right click testThis() and chose go to declaration, I expect to go to the function declaration in /home/sadik/my-plugin-runtime-2019-06/CDTTest_Local/tests/indexer/declaration.h. Both files are located in the same directory.
But what happens is that an editor is opened with an empty file. The editor even tells me the path: /home/soezoguz/rtt-plugin-runtime-2019-06/tests/indexer/declaration.h.
The path is missing the project name. So I guess the pdom file stores locations below the specified source directory. How can I tell the PDOMProvider to look into the correct directory for the indexed files?
For some reason the trailing "/" has been ommited by URIUtil.toURI(...). But in the description of URIRealtiveLocationConverter it says
Note: The supplied base URI must end with a forward slash
So I create an URI instance from String and append a "/" to the String.
#Override
public IPDOMDescriptor[] getDescriptors(ICConfigurationDescription config) {
final IPath fileBase = Path.fromOSString("/home/sadik/eclipse-2019-06/eclipse/");
final IPath projectBase = config.getProjectDescription().getProject().getFullPath();
return new IPDOMDescriptor[] { new IPDOMDescriptor() {
public IIndexLocationConverter getIndexLocationConverter() {
URI baseURI;
try {
baseURI = new URI(projectBase.toString()+"/");
return new URIRelativeLocationConverter(baseURI);
} catch (URISyntaxException e) {
e.printStackTrace();
}
baseURI = URIUtil.toURI(projectBase);
return new URIRelativeLocationConverter(URIUtil.toURI(projectBase));
}
public IPath getLocation() {
IPath path = fileBase.append("pdomExample.pdom");
return path;
}
}};
}

Java - How to Load Classes your Main does'nt know the name from? [duplicate]

Is it possible to find all classes or interfaces in a given package? (Quickly looking at e.g. Package, it would seem like no.)
Due to the dynamic nature of class loaders, this is not possible. Class loaders are not required to tell the VM which classes it can provide, instead they are just handed requests for classes, and have to return a class or throw an exception.
However, if you write your own class loaders, or examine the classpaths and it's jars, it's possible to find this information. This will be via filesystem operations though, and not reflection. There might even be libraries that can help you do this.
If there are classes that get generated, or delivered remotely, you will not be able to discover those classes.
The normal method is instead to somewhere register the classes you need access to in a file, or reference them in a different class. Or just use convention when it comes to naming.
Addendum: The Reflections Library will allow you to look up classes in the current classpath. It can be used to get all classes in a package:
Reflections reflections = new Reflections("my.project.prefix");
Set<Class<? extends Object>> allClasses =
reflections.getSubTypesOf(Object.class);
You should probably take a look at the open source Reflections library. With it you can easily achieve what you want.
First, setup the reflections index (it's a bit messy since searching for all classes is disabled by default):
List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
classLoadersList.add(ClasspathHelper.contextClassLoader());
classLoadersList.add(ClasspathHelper.staticClassLoader());
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
.setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
.filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("org.your.package"))));
Then you can query for all objects in a given package:
Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);
Google Guava 14 includes a new class ClassPath with three methods to scan for top level classes:
getTopLevelClasses()
getTopLevelClasses(String packageName)
getTopLevelClassesRecursive(String packageName)
See the ClassPath javadocs for more info.
You could use this method1 that uses the ClassLoader.
/**
* Scans all classes accessible from the context class loader which belong to the given package and subpackages.
*
* #param packageName The base package
* #return The classes
* #throws ClassNotFoundException
* #throws IOException
*/
private static Class[] getClasses(String packageName)
throws ClassNotFoundException, IOException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
assert classLoader != null;
String path = packageName.replace('.', '/');
Enumeration<URL> resources = classLoader.getResources(path);
List<File> dirs = new ArrayList<File>();
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
dirs.add(new File(resource.getFile()));
}
ArrayList<Class> classes = new ArrayList<Class>();
for (File directory : dirs) {
classes.addAll(findClasses(directory, packageName));
}
return classes.toArray(new Class[classes.size()]);
}
/**
* Recursive method used to find all classes in a given directory and subdirs.
*
* #param directory The base directory
* #param packageName The package name for classes found inside the base directory
* #return The classes
* #throws ClassNotFoundException
*/
private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
List<Class> classes = new ArrayList<Class>();
if (!directory.exists()) {
return classes;
}
File[] files = directory.listFiles();
for (File file : files) {
if (file.isDirectory()) {
assert !file.getName().contains(".");
classes.addAll(findClasses(file, packageName + "." + file.getName()));
} else if (file.getName().endsWith(".class")) {
classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
}
}
return classes;
}
__________
1 This method was taken originally from http://snippets.dzone.com/posts/show/4831, which was archived by the Internet Archive, as linked to now. The snippet is also available at https://dzone.com/articles/get-all-classes-within-package.
Spring
This example is for Spring 4, but you can find the classpath scanner in earlier versions as well.
// create scanner and disable default filters (that is the 'false' argument)
final ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
// add include filters which matches all the classes (or use your own)
provider.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*")));
// get matching classes defined in the package
final Set<BeanDefinition> classes = provider.findCandidateComponents("my.package.name");
// this is how you can load the class type from BeanDefinition instance
for (BeanDefinition bean: classes) {
Class<?> clazz = Class.forName(bean.getBeanClassName());
// ... do your magic with the class ...
}
Google Guava
Note: In version 14, the API is still marked as #Beta, so beware in production code.
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
for (final ClassPath.ClassInfo info : ClassPath.from(loader).getTopLevelClasses()) {
if (info.getName().startsWith("my.package.")) {
final Class<?> clazz = info.load();
// do something with your clazz
}
}
Hello. I always have had some issues with the solutions above (and on other sites).
I, as a developer, am programming a addon for a API. The API prevents the use of any external libraries or 3rd party tools. The setup also consists of a mixture of code in jar or zip files and class files located directly in some directories. So my code had to be able to work arround every setup. After a lot of research I have come up with a method that will work in at least 95% of all possible setups.
The following code is basically the overkill method that will always work.
The code:
This code scans a given package for all classes that are included in it. It will only work for all classes in the current ClassLoader.
/**
* Private helper method
*
* #param directory
* The directory to start with
* #param pckgname
* The package name to search for. Will be needed for getting the
* Class object.
* #param classes
* if a file isn't loaded but still is in the directory
* #throws ClassNotFoundException
*/
private static void checkDirectory(File directory, String pckgname,
ArrayList<Class<?>> classes) throws ClassNotFoundException {
File tmpDirectory;
if (directory.exists() && directory.isDirectory()) {
final String[] files = directory.list();
for (final String file : files) {
if (file.endsWith(".class")) {
try {
classes.add(Class.forName(pckgname + '.'
+ file.substring(0, file.length() - 6)));
} catch (final NoClassDefFoundError e) {
// do nothing. this class hasn't been found by the
// loader, and we don't care.
}
} else if ((tmpDirectory = new File(directory, file))
.isDirectory()) {
checkDirectory(tmpDirectory, pckgname + "." + file, classes);
}
}
}
}
/**
* Private helper method.
*
* #param connection
* the connection to the jar
* #param pckgname
* the package name to search for
* #param classes
* the current ArrayList of all classes. This method will simply
* add new classes.
* #throws ClassNotFoundException
* if a file isn't loaded but still is in the jar file
* #throws IOException
* if it can't correctly read from the jar file.
*/
private static void checkJarFile(JarURLConnection connection,
String pckgname, ArrayList<Class<?>> classes)
throws ClassNotFoundException, IOException {
final JarFile jarFile = connection.getJarFile();
final Enumeration<JarEntry> entries = jarFile.entries();
String name;
for (JarEntry jarEntry = null; entries.hasMoreElements()
&& ((jarEntry = entries.nextElement()) != null);) {
name = jarEntry.getName();
if (name.contains(".class")) {
name = name.substring(0, name.length() - 6).replace('/', '.');
if (name.contains(pckgname)) {
classes.add(Class.forName(name));
}
}
}
}
/**
* Attempts to list all the classes in the specified package as determined
* by the context class loader
*
* #param pckgname
* the package name to search
* #return a list of classes that exist within that package
* #throws ClassNotFoundException
* if something went wrong
*/
public static ArrayList<Class<?>> getClassesForPackage(String pckgname)
throws ClassNotFoundException {
final ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
try {
final ClassLoader cld = Thread.currentThread()
.getContextClassLoader();
if (cld == null)
throw new ClassNotFoundException("Can't get class loader.");
final Enumeration<URL> resources = cld.getResources(pckgname
.replace('.', '/'));
URLConnection connection;
for (URL url = null; resources.hasMoreElements()
&& ((url = resources.nextElement()) != null);) {
try {
connection = url.openConnection();
if (connection instanceof JarURLConnection) {
checkJarFile((JarURLConnection) connection, pckgname,
classes);
} else if (connection instanceof FileURLConnection) {
try {
checkDirectory(
new File(URLDecoder.decode(url.getPath(),
"UTF-8")), pckgname, classes);
} catch (final UnsupportedEncodingException ex) {
throw new ClassNotFoundException(
pckgname
+ " does not appear to be a valid package (Unsupported encoding)",
ex);
}
} else
throw new ClassNotFoundException(pckgname + " ("
+ url.getPath()
+ ") does not appear to be a valid package");
} catch (final IOException ioex) {
throw new ClassNotFoundException(
"IOException was thrown when trying to get all resources for "
+ pckgname, ioex);
}
}
} catch (final NullPointerException ex) {
throw new ClassNotFoundException(
pckgname
+ " does not appear to be a valid package (Null pointer exception)",
ex);
} catch (final IOException ioex) {
throw new ClassNotFoundException(
"IOException was thrown when trying to get all resources for "
+ pckgname, ioex);
}
return classes;
}
These three methods provide you with the ability to find all classes in a given package.
You use it like this:
getClassesForPackage("package.your.classes.are.in");
The explanation:
The method first gets the current ClassLoader. It then fetches all resources that contain said package and iterates of these URLs. It then creates a URLConnection and determines what type of URl we have. It can either be a directory (FileURLConnection) or a directory inside a jar or zip file (JarURLConnection). Depending on what type of connection we have two different methods will be called.
First lets see what happens if it is a FileURLConnection.
It first checks if the passed File exists and is a directory. If that's the case it checks if it is a class file. If so a Class object will be created and put in the ArrayList. If it is not a class file but is a directory, we simply iterate into it and do the same thing. All other cases/files will be ignored.
If the URLConnection is a JarURLConnection the other private helper method will be called. This method iterates over all Entries in the zip/jar archive. If one entry is a class file and is inside of the package a Class object will be created and stored in the ArrayList.
After all resources have been parsed it (the main method) returns the ArrayList containig all classes in the given package, that the current ClassLoader knows about.
If the process fails at any point a ClassNotFoundException will be thrown containg detailed information about the exact cause.
The most robust mechanism for listing all classes in a given package is currently ClassGraph, because it handles the widest possible array of classpath specification mechanisms, including the new JPMS module system. (I am the author.)
List<String> classNames = new ArrayList<>();
try (ScanResult scanResult = new ClassGraph().acceptPackages("my.package")
.enableClassInfo().scan()) {
classNames.addAll(scanResult.getAllClasses().getNames());
}
Without using any extra libraries:
package test;
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) throws Exception{
List<Class> classes = getClasses(Test.class.getClassLoader(),"test");
for(Class c:classes){
System.out.println("Class: "+c);
}
}
public static List<Class> getClasses(ClassLoader cl,String pack) throws Exception{
String dottedPackage = pack.replaceAll("[/]", ".");
List<Class> classes = new ArrayList<Class>();
URL upackage = cl.getResource(pack);
DataInputStream dis = new DataInputStream((InputStream) upackage.getContent());
String line = null;
while ((line = dis.readLine()) != null) {
if(line.endsWith(".class")) {
classes.add(Class.forName(dottedPackage+"."+line.substring(0,line.lastIndexOf('.'))));
}
}
return classes;
}
}
In general class loaders do not allow for scanning through all the classes on the classpath. But usually the only used class loader is UrlClassLoader from which we can retrieve the list of directories and jar files (see getURLs) and open them one by one to list available classes. This approach, called class path scanning, is implemented in Scannotation and Reflections.
Reflections reflections = new Reflections("my.package");
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
Another approach is to use Java Pluggable Annotation Processing API to write annotation processor which will collect all annotated classes at compile time and build the index file for runtime use. This mechanism is implemented in ClassIndex library:
// package-info.java
#IndexSubclasses
package my.package;
// your code
Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");
Notice that no additional setup is needed as the scanning is fully automated thanks to Java compiler automatically discovering any processors found on the classpath.
What about this:
public static List<Class<?>> getClassesForPackage(final String pkgName) throws IOException, URISyntaxException {
final String pkgPath = pkgName.replace('.', '/');
final URI pkg = Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource(pkgPath)).toURI();
final ArrayList<Class<?>> allClasses = new ArrayList<Class<?>>();
Path root;
if (pkg.toString().startsWith("jar:")) {
try {
root = FileSystems.getFileSystem(pkg).getPath(pkgPath);
} catch (final FileSystemNotFoundException e) {
root = FileSystems.newFileSystem(pkg, Collections.emptyMap()).getPath(pkgPath);
}
} else {
root = Paths.get(pkg);
}
final String extension = ".class";
try (final Stream<Path> allPaths = Files.walk(root)) {
allPaths.filter(Files::isRegularFile).forEach(file -> {
try {
final String path = file.toString().replace('/', '.');
final String name = path.substring(path.indexOf(pkgName), path.length() - extension.length());
allClasses.add(Class.forName(name));
} catch (final ClassNotFoundException | StringIndexOutOfBoundsException ignored) {
}
});
}
return allClasses;
}
You can then overload the function:
public static List<Class<?>> getClassesForPackage(final Package pkg) throws IOException, URISyntaxException {
return getClassesForPackage(pkg.getName());
}
If you need to test it:
public static void main(final String[] argv) throws IOException, URISyntaxException {
for (final Class<?> cls : getClassesForPackage("my.package")) {
System.out.println(cls);
}
for (final Class<?> cls : getClassesForPackage(MyClass.class.getPackage())) {
System.out.println(cls);
}
}
If your IDE does not have import helper:
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
It works:
from your IDE
for a JAR file
without external dependencies
Here's how I do it. I scan all the subfolders (sub-packages) and I don't try to load anonymous classes:
/**
* Attempts to list all the classes in the specified package as determined
* by the context class loader, recursively, avoiding anonymous classes
*
* #param pckgname
* the package name to search
* #return a list of classes that exist within that package
* #throws ClassNotFoundException
* if something went wrong
*/
private static List<Class> getClassesForPackage(String pckgname) throws ClassNotFoundException {
// This will hold a list of directories matching the pckgname. There may be more than one if a package is split over multiple jars/paths
ArrayList<File> directories = new ArrayList<File>();
String packageToPath = pckgname.replace('.', '/');
try {
ClassLoader cld = Thread.currentThread().getContextClassLoader();
if (cld == null) {
throw new ClassNotFoundException("Can't get class loader.");
}
// Ask for all resources for the packageToPath
Enumeration<URL> resources = cld.getResources(packageToPath);
while (resources.hasMoreElements()) {
directories.add(new File(URLDecoder.decode(resources.nextElement().getPath(), "UTF-8")));
}
} catch (NullPointerException x) {
throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Null pointer exception)");
} catch (UnsupportedEncodingException encex) {
throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Unsupported encoding)");
} catch (IOException ioex) {
throw new ClassNotFoundException("IOException was thrown when trying to get all resources for " + pckgname);
}
ArrayList<Class> classes = new ArrayList<Class>();
// For every directoryFile identified capture all the .class files
while (!directories.isEmpty()){
File directoryFile = directories.remove(0);
if (directoryFile.exists()) {
// Get the list of the files contained in the package
File[] files = directoryFile.listFiles();
for (File file : files) {
// we are only interested in .class files
if ((file.getName().endsWith(".class")) && (!file.getName().contains("$"))) {
// removes the .class extension
int index = directoryFile.getPath().indexOf(packageToPath);
String packagePrefix = directoryFile.getPath().substring(index).replace('/', '.');;
try {
String className = packagePrefix + '.' + file.getName().substring(0, file.getName().length() - 6);
classes.add(Class.forName(className));
} catch (NoClassDefFoundError e)
{
// do nothing. this class hasn't been found by the loader, and we don't care.
}
} else if (file.isDirectory()){ // If we got to a subdirectory
directories.add(new File(file.getPath()));
}
}
} else {
throw new ClassNotFoundException(pckgname + " (" + directoryFile.getPath() + ") does not appear to be a valid package");
}
}
return classes;
}
I put together a simple github project that solves this problem:
https://github.com/ddopson/java-class-enumerator
It should work for BOTH file-based classpaths AND for jar files.
If you run 'make' after checking out the project it will print this out:
Cleaning...
rm -rf build/
Building...
javac -d build/classes src/pro/ddopson/ClassEnumerator.java src/test/ClassIShouldFindOne.java src/test/ClassIShouldFindTwo.java src/test/subpkg/ClassIShouldFindThree.java src/test/TestClassEnumeration.java
Making JAR Files...
jar cf build/ClassEnumerator_test.jar -C build/classes/ .
jar cf build/ClassEnumerator.jar -C build/classes/ pro
Running Filesystem Classpath Test...
java -classpath build/classes test.TestClassEnumeration
ClassDiscovery: Package: 'test' becomes Resource: 'file:/Users/Dopson/work/other/java-class-enumeration/build/classes/test'
ClassDiscovery: Reading Directory '/Users/Dopson/work/other/java-class-enumeration/build/classes/test'
ClassDiscovery: FileName 'ClassIShouldFindOne.class' => class 'test.ClassIShouldFindOne'
ClassDiscovery: FileName 'ClassIShouldFindTwo.class' => class 'test.ClassIShouldFindTwo'
ClassDiscovery: FileName 'subpkg' => class 'null'
ClassDiscovery: Reading Directory '/Users/Dopson/work/other/java-class-enumeration/build/classes/test/subpkg'
ClassDiscovery: FileName 'ClassIShouldFindThree.class' => class 'test.subpkg.ClassIShouldFindThree'
ClassDiscovery: FileName 'TestClassEnumeration.class' => class 'test.TestClassEnumeration'
Running JAR Classpath Test...
java -classpath build/ClassEnumerator_test.jar test.TestClassEnumeration
ClassDiscovery: Package: 'test' becomes Resource: 'jar:file:/Users/Dopson/work/other/java-class-enumeration/build/ClassEnumerator_test.jar!/test'
ClassDiscovery: Reading JAR file: '/Users/Dopson/work/other/java-class-enumeration/build/ClassEnumerator_test.jar'
ClassDiscovery: JarEntry 'META-INF/' => class 'null'
ClassDiscovery: JarEntry 'META-INF/MANIFEST.MF' => class 'null'
ClassDiscovery: JarEntry 'pro/' => class 'null'
ClassDiscovery: JarEntry 'pro/ddopson/' => class 'null'
ClassDiscovery: JarEntry 'pro/ddopson/ClassEnumerator.class' => class 'null'
ClassDiscovery: JarEntry 'test/' => class 'null'
ClassDiscovery: JarEntry 'test/ClassIShouldFindOne.class' => class 'test.ClassIShouldFindOne'
ClassDiscovery: JarEntry 'test/ClassIShouldFindTwo.class' => class 'test.ClassIShouldFindTwo'
ClassDiscovery: JarEntry 'test/subpkg/' => class 'null'
ClassDiscovery: JarEntry 'test/subpkg/ClassIShouldFindThree.class' => class 'test.subpkg.ClassIShouldFindThree'
ClassDiscovery: JarEntry 'test/TestClassEnumeration.class' => class 'test.TestClassEnumeration'
Tests Passed.
See also my other answer
Yeah using few API's you can, here is how I like doing it, faced this problem which I was using hibernate core & had to find classes which where annotated with a certain annotation.
Make these an custom annotation using which you will mark which classes you want to be picked up.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface EntityToBeScanned {
}
Then mark your class with it like
#EntityToBeScanned
public MyClass{
}
Make this utility class which has the following method
public class ClassScanner {
public static Set<Class<?>> allFoundClassesAnnotatedWithEntityToBeScanned(){
Reflections reflections = new Reflections(".*");
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(EntityToBeScanned.class);
return annotated;
}
}
Call the allFoundClassesAnnotatedWithEntityToBeScanned() method to get a Set of Classes found.
You will need libs given below
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>21.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.22.0-CR1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
If you're in Spring-land you can use PathMatchingResourcePatternResolver;
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath*:some/package/name/*.class");
Arrays.asList(resources).forEach(r->{
...
});
You need to look up every class loader entry in the class path:
String pkg = "org/apache/commons/lang";
ClassLoader cl = ClassLoader.getSystemClassLoader();
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url : urls) {
System.out.println(url.getFile());
File jar = new File(url.getFile());
// ....
}
If entry is directory, just look up in the right subdirectory:
if (jar.isDirectory()) {
File subdir = new File(jar, pkg);
if (!subdir.exists())
continue;
File[] files = subdir.listFiles();
for (File file : files) {
if (!file.isFile())
continue;
if (file.getName().endsWith(".class"))
System.out.println("Found class: "
+ file.getName().substring(0,
file.getName().length() - 6));
}
}
If the entry is the file, and it's jar, inspect the ZIP entries of it:
else {
// try to open as ZIP
try {
ZipFile zip = new ZipFile(jar);
for (Enumeration<? extends ZipEntry> entries = zip
.entries(); entries.hasMoreElements();) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
if (!name.startsWith(pkg))
continue;
name = name.substring(pkg.length() + 1);
if (name.indexOf('/') < 0 && name.endsWith(".class"))
System.out.println("Found class: "
+ name.substring(0, name.length() - 6));
}
} catch (ZipException e) {
System.out.println("Not a ZIP: " + e.getMessage());
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
Now once you have all class names withing package, you can try loading them with reflection and analyze if they are classes or interfaces, etc.
I've been trying to use the Reflections library, but had some problems using it, and there were too many jars I should include just to simply obtain the classes on a package.
I'll post a solution I've found in this duplicate question: How to get all classes names in a package?
The answer was written by sp00m; I've added some corrections to make it work:
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
public final class ClassFinder {
private final static char DOT = '.';
private final static char SLASH = '/';
private final static String CLASS_SUFFIX = ".class";
private final static String BAD_PACKAGE_ERROR = "Unable to get resources from path '%s'. Are you sure the given '%s' package exists?";
public final static List<Class<?>> find(final String scannedPackage) {
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
final String scannedPath = scannedPackage.replace(DOT, SLASH);
final Enumeration<URL> resources;
try {
resources = classLoader.getResources(scannedPath);
} catch (IOException e) {
throw new IllegalArgumentException(String.format(BAD_PACKAGE_ERROR, scannedPath, scannedPackage), e);
}
final List<Class<?>> classes = new LinkedList<Class<?>>();
while (resources.hasMoreElements()) {
final File file = new File(resources.nextElement().getFile());
classes.addAll(find(file, scannedPackage));
}
return classes;
}
private final static List<Class<?>> find(final File file, final String scannedPackage) {
final List<Class<?>> classes = new LinkedList<Class<?>>();
if (file.isDirectory()) {
for (File nestedFile : file.listFiles()) {
classes.addAll(find(nestedFile, scannedPackage));
}
//File names with the $1, $2 holds the anonymous inner classes, we are not interested on them.
} else if (file.getName().endsWith(CLASS_SUFFIX) && !file.getName().contains("$")) {
final int beginIndex = 0;
final int endIndex = file.getName().length() - CLASS_SUFFIX.length();
final String className = file.getName().substring(beginIndex, endIndex);
try {
final String resource = scannedPackage + DOT + className;
classes.add(Class.forName(resource));
} catch (ClassNotFoundException ignore) {
}
}
return classes;
}
}
To use it just call the find method as sp00n mentioned in this example:
I've added the creation of instances of the classes if needed.
List<Class<?>> classes = ClassFinder.find("com.package");
ExcelReporting excelReporting;
for (Class<?> aClass : classes) {
Constructor constructor = aClass.getConstructor();
//Create an object of the class type
constructor.newInstance();
//...
}
I just wrote a util class, it include test methods, you can have a check ~
IteratePackageUtil.java:
package eric.j2se.reflect;
import java.util.Set;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
/**
* an util to iterate class in a package,
*
* #author eric
* #date Dec 10, 2013 12:36:46 AM
*/
public class IteratePackageUtil {
/**
* <p>
* Get set of all class in a specified package recursively. this only support lib
* </p>
* <p>
* class of sub package will be included, inner class will be included,
* </p>
* <p>
* could load class that use the same classloader of current class, can't load system packages,
* </p>
*
* #param pkg
* path of a package
* #return
*/
public static Set<Class<? extends Object>> getClazzSet(String pkg) {
// prepare reflection, include direct subclass of Object.class
Reflections reflections = new Reflections(new ConfigurationBuilder().setScanners(new SubTypesScanner(false), new ResourcesScanner())
.setUrls(ClasspathHelper.forClassLoader(ClasspathHelper.classLoaders(new ClassLoader[0])))
.filterInputsBy(new FilterBuilder().includePackage(pkg)));
return reflections.getSubTypesOf(Object.class);
}
public static void test() {
String pkg = "org.apache.tomcat.util";
Set<Class<? extends Object>> clazzSet = getClazzSet(pkg);
for (Class<? extends Object> clazz : clazzSet) {
System.out.println(clazz.getName());
}
}
public static void main(String[] args) {
test();
}
}
Almost all the answers either uses Reflections or reads class files from file system. If you try to read classes from file system, you may get errors when you package your application as JAR or other. Also you may not want to use a separate library for that purpose.
Here is another approach which is pure java and not depends on file system.
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public class PackageUtil {
public static Collection<Class> getClasses(final String pack) throws Exception {
final StandardJavaFileManager fileManager = ToolProvider.getSystemJavaCompiler().getStandardFileManager(null, null, null);
return StreamSupport.stream(fileManager.list(StandardLocation.CLASS_PATH, pack, Collections.singleton(JavaFileObject.Kind.CLASS), false).spliterator(), false)
.map(javaFileObject -> {
try {
final String[] split = javaFileObject.getName()
.replace(".class", "")
.replace(")", "")
.split(Pattern.quote(File.separator));
final String fullClassName = pack + "." + split[split.length - 1];
return Class.forName(fullClassName);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
})
.collect(Collectors.toCollection(ArrayList::new));
}
}
Java 8 is not a must. You can use for loops instead of streams.
And you can test it like this
public static void main(String[] args) throws Exception {
final String pack = "java.nio.file"; // Or any other package
PackageUtil.getClasses(pack).stream().forEach(System.out::println);
}
Aleksander Blomskøld's solution did not work for me for parameterized tests #RunWith(Parameterized.class) when using Maven. The tests were named correctly and also where found but not executed:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running some.properly.named.test.run.with.maven.SomeTest
Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.123 sec
A similar issue has been reported here.
In my case #Parameters is creating instances of each class in a package. The tests worked well when run locally in the IDE. However, when running Maven no classes where found with Aleksander Blomskøld's solution.
I did make it work with the following snipped which was inspired by David Pärsson's comment on Aleksander Blomskøld's answer:
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
.addUrls(ClasspathHelper.forJavaClassPath())
.filterInputsBy(new FilterBuilder()
.include(FilterBuilder.prefix(basePackage))));
Set<Class<?>> subTypesOf = reflections.getSubTypesOf(Object.class);
I couldn't find a short working snippet for something so simple. So here it is, I made it myself after screwing around for a while:
Reflections reflections =
new Reflections(new ConfigurationBuilder()
.filterInputsBy(new FilterBuilder().includePackage(packagePath))
.setUrls(ClasspathHelper.forPackage(packagePath))
.setScanners(new SubTypesScanner(false)));
Set<String> typeList = reflections.getAllTypes();
It uses org.reflections.
Define classes to be scanning in the package test
package test;
public class A {
private class B {}
enum C {}
record D() {}
}
For org.reflections:reflections:0.10.2, it works for me as follows:
Use reflection lib to scan classes in package test
#Test
void t() {
final String packagePath = "test";
final Reflections reflections =
new Reflections(packagePath, Scanners.SubTypes.filterResultsBy(v -> true));
reflections.getAll(Scanners.SubTypes).forEach(System.out::println);
}
Output
java.lang.constant.Constable
java.lang.Enum
java.lang.Comparable
java.lang.Record
java.lang.Object
java.io.Serializable
test.A$C
test.A$D
test.A$B
test.A
For io.github.classgraph:classgraph:4.8.146, it works for me as follows:
#Test
void t() {
final String packagePath = "test";
try (ScanResult scanResult = new ClassGraph()
.enableClassInfo()
.ignoreClassVisibility()
.acceptPackages(packagePath)
.scan()) {
scanResult.getAllClasses()
.forEach(v -> {
System.out.println(v.getName());
});
}
}
Output
test.A
test.A$B
test.A$C
test.A$D
Provided you are not using any dynamic class loaders you can search the classpath and for each entry search the directory or JAR file.
Worth mentioning
If you want to have a list of all classes under some package, you can use Reflection the following way:
List<Class> myTypes = new ArrayList<>();
Reflections reflections = new Reflections("com.package");
for (String s : reflections.getStore().get(SubTypesScanner.class).values()) {
myTypes.add(Class.forName(s));
}
This will create a list of classes that later you can use them as you wish.
It is very possible, but without additional libraries like Reflections it is hard...
It is hard because you haven't full instrument for get class name.
And, I take the code of my ClassFinder class:
package play.util;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* Created by LINKOR on 26.05.2017 in 15:12.
* Date: 2017.05.26
*/
public class FileClassFinder {
private JarFile file;
private boolean trouble;
public FileClassFinder(String filePath) {
try {
file = new JarFile(filePath);
} catch (IOException e) {
trouble = true;
}
}
public List<String> findClasses(String pkg) {
ArrayList<String> classes = new ArrayList<>();
Enumeration<JarEntry> entries = file.entries();
while (entries.hasMoreElements()) {
JarEntry cls = entries.nextElement();
if (!cls.isDirectory()) {
String fileName = cls.getName();
String className = fileName.replaceAll("/", ".").replaceAll(File.pathSeparator, ".").substring(0, fileName.lastIndexOf('.'));
if (className.startsWith(pkg)) classes.add(className.substring(pkg.length() + 1));
}
}
return classes;
}
}
this scans the class loaders and all parent loaders for jar files and directories.
the jar files and directories referred by the Class-Path of the jars are also loaded.
this code is testet with Java 8,11,18.
on 8 everything works perfectly using the URLClassLoader and the getURLs() method.
on 11 it works fine using reflections, but the JVM prints a warning on the stderr stream (not redirectible with System.setErr() with my JVM)
on 18 the reflections are useless (throws NoSuchMethod/Field), and the only thing (where I know that it works) is to use the getResource() method. When the class loader loades the resources of the given package from the file system a simple path url is returned. When the class loader loades the resources from a jar a url like 'jar:file:[jar-path]!/[in-jar-path]' is returned.
I have used the answer https://stackoverflow.com/a/1157352/18252455 (from a duplicate question) and added the functionality to read the Class-Path and also search for directory URLs.
/**
* orig description:<br>
* Scans all classloaders for the current thread for loaded jars, and then scans
* each jar for the package name in question, listing all classes directly under
* the package name in question. Assumes directory structure in jar file and class
* package naming follow java conventions (i.e. com.example.test.MyTest would be in
* /com/example/test/MyTest.class)
* <p>
* in addition this method also scans for directories, where also is assumed, that the classes are
* placed followed by the java conventions. (i.e. <code>com.example.test.MyTest</code> would be in
* <code>directory/com/example/test/MyTest.class</code>)
* <p>
* this method also reads the jars Class-Path for other jars and directories. for the jars and
* directories referred in the jars are scanned with the same rules as defined here.<br>
* it is ensured that no jar/directory is scanned exactly one time.
* <p>
* if {#code bailError} is <code>true</code> all errors will be wrapped in a
* {#link RuntimeException}
* and then thrown.<br>
* a {#link RuntimeException} will also be thrown if something unexpected happens.<br>
*
* #param packageName
* the name of the package for which the classes should be searched
* #param allowSubPackages
* <code>true</code> is also classes in sub packages should be found
* #param loader
* the {#link ClassLoader} which should be used to find the URLs and to load classes
* #param bailError
* if all {#link Exception} should be re-thrown wrapped in {#link RuntimeException} and
* if a {#link RuntimeException} should be thrown, when something is not as expected.
* #see https://stackoverflow.com/questions/1156552/java-package-introspection
* #see https://stackoverflow.com/a/1157352/18252455
* #see https://creativecommons.org/licenses/by-sa/2.5/
* #see https://creativecommons.org/licenses/by-sa/2.5/legalcode
*/
public static Set <Class <?>> tryGetClassesForPackage(String packageName, boolean allowSubPackages, ClassLoader loader, boolean bailError) {
Set <URL> jarUrls = new HashSet <URL>();
Set <Path> directorys = new HashSet <Path>();
findClassPools(loader, jarUrls, directorys, bailError, packageName);
Set <Class <?>> jarClasses = findJarClasses(allowSubPackages, packageName, jarUrls, directorys, loader, bailError);
Set <Class <?>> dirClasses = findDirClasses(allowSubPackages, packageName, directorys, loader, bailError);
jarClasses.addAll(dirClasses);
return jarClasses;
}
private static Set <Class <?>> findDirClasses(boolean subPackages, String packageName, Set <Path> directorys, ClassLoader loader, boolean bailError) {
Filter <Path> filter;
Set <Class <?>> result = new HashSet <>();
for (Path startPath : directorys) {
String packagePath = packageName.replace(".", startPath.getFileSystem().getSeparator());
final Path searchPath = startPath.resolve(packagePath).toAbsolutePath();
if (subPackages) {
filter = p -> {
p = p.toAbsolutePath();
Path other;
if (p.getNameCount() >= searchPath.getNameCount()) {
other = searchPath;
} else {
other = searchPath.subpath(0, p.getNameCount());
}
if (p.startsWith(other)) {
return true;
} else {
return false;
}
};
} else {
filter = p -> {
p = p.toAbsolutePath();
if (p.getNameCount() > searchPath.getNameCount() + 1) {
return false;
} else if (p.toAbsolutePath().startsWith(searchPath)) {
return true;
} else {
return false;
}
};
}
if (Files.exists(searchPath)) {
findDirClassFilesRecursive(filter, searchPath, startPath, result, loader, bailError);
} // the package does not have to exist in every directory
}
return result;
}
private static void findDirClassFilesRecursive(Filter <Path> filter, Path path, Path start, Set <Class <?>> classes, ClassLoader loader, boolean bailError) {
try (DirectoryStream <Path> dirStream = Files.newDirectoryStream(path, filter)) {
for (Path p : dirStream) {
if (Files.isDirectory(p)) {
findDirClassFilesRecursive(filter, p, start, classes, loader, bailError);
} else {
Path subp = p.subpath(start.getNameCount(), p.getNameCount());
String str = subp.toString();
if (str.endsWith(".class")) {
str = str.substring(0, str.length() - 6);
String sep = p.getFileSystem().getSeparator();
if (str.startsWith(sep)) {
str = str.substring(sep.length());
}
if (str.endsWith(sep)) {
str = str.substring(0, str.length() - sep.length());
}
String fullClassName = str.replace(sep, ".");
try {
Class <?> cls = Class.forName(fullClassName, false, loader);
classes.add(cls);
} catch (ClassNotFoundException e) {
if (bailError) {
throw new RuntimeException(e);
}
}
}
}
}
} catch (IOException e) {
if (bailError) {
throw new RuntimeException(e);
}
}
}
private static Set <Class <?>> findJarClasses(boolean subPackages, String packageName, Set <URL> nextJarUrls, Set <Path> directories, ClassLoader loader, boolean bailError) {
String packagePath = packageName.replace('.', '/');
Set <Class <?>> result = new HashSet <>();
Set <URL> allJarUrls = new HashSet <>();
while (true) {
Set <URL> thisJarUrls = new HashSet <>(nextJarUrls);
thisJarUrls.removeAll(allJarUrls);
if (thisJarUrls.isEmpty()) {
break;
}
allJarUrls.addAll(thisJarUrls);
for (URL url : thisJarUrls) {
try (JarInputStream stream = new JarInputStream(url.openStream())) {
// may want better way to open url connections
readJarClassPath(stream, nextJarUrls, directories, bailError);
JarEntry entry = stream.getNextJarEntry();
while (entry != null) {
String name = entry.getName();
int i = name.lastIndexOf("/");
if (i > 0 && name.endsWith(".class")) {
try {
if (subPackages) {
if (name.substring(0, i).startsWith(packagePath)) {
Class <?> cls = Class.forName(name.substring(0, name.length() - 6).replace("/", "."), false, loader);
result.add(cls);
}
} else {
if (name.substring(0, i).equals(packagePath)) {
Class <?> cls = Class.forName(name.substring(0, name.length() - 6).replace("/", "."), false, loader);
result.add(cls);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
entry = stream.getNextJarEntry();
}
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result;
}
private static void readJarClassPath(JarInputStream stream, Set <URL> jarUrls, Set <Path> directories, boolean bailError) {
Object classPathObj = stream.getManifest().getMainAttributes().get(new Name("Class-Path"));
if (classPathObj == null) {
return;
}
if (classPathObj instanceof String) {
String[] entries = ((String) classPathObj).split("\\s+");// should also work with a single space (" ")
for (String entry : entries) {
try {
URL url = new URL(entry);
addFromUrl(jarUrls, directories, url, bailError);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
} else if (bailError) {
throw new RuntimeException("the Class-Path attribute is no String: " + classPathObj.getClass().getName() + " tos='" + classPathObj + "'");
}
}
private static void findClassPools(ClassLoader classLoader, Set <URL> jarUrls, Set <Path> directoryPaths, boolean bailError, String packageName) {
packageName = packageName.replace('.', '/');
while (classLoader != null) {
if (classLoader instanceof URLClassLoader) {
for (URL url : ((URLClassLoader) classLoader).getURLs()) {
addFromUrl(jarUrls, directoryPaths, url, bailError);
System.out.println("rurl-class-loade.url[n]r->'" + url + "'");
}
} else {
URL res = classLoader.getResource("");
if (res != null) {
addFromUrl(jarUrls, directoryPaths, res, bailError);
}
res = classLoader.getResource("/");
if (res != null) {
addFromUrl(jarUrls, directoryPaths, res, bailError);
}
res = classLoader.getResource("/" + packageName);
if (res != null) {
res = removePackageFromUrl(res, packageName, bailError);
if (res != null) {
addFromUrl(jarUrls, directoryPaths, res, bailError);
}
}
res = classLoader.getResource(packageName);
if (res != null) {
res = removePackageFromUrl(res, packageName, bailError);
if (res != null) {
addFromUrl(jarUrls, directoryPaths, res, bailError);
}
}
addFromUnknownClass(classLoader, jarUrls, directoryPaths, bailError, 8);
}
classLoader = classLoader.getParent();
}
}
private static URL removePackageFromUrl(URL res, String packagePath, boolean bailError) {
packagePath = "/" + packagePath;
String urlStr = res.toString();
if ( !urlStr.endsWith(packagePath)) {
if (bailError) {
throw new RuntimeException("the url string does not end with the packagepath! packagePath='" + packagePath + "' urlStr='" + urlStr + "'");
} else {
return null;
}
}
urlStr = urlStr.substring(0, urlStr.length() - packagePath.length());
if (urlStr.endsWith("!")) {
urlStr = urlStr.substring(0, urlStr.length() - 1);
}
if (urlStr.startsWith("jar:")) {
urlStr = urlStr.substring(4);
}
try {
return new URL(urlStr);
} catch (MalformedURLException e) {
if (bailError) {
throw new RuntimeException(e);
} else {
return null;
}
}
}
private static void addFromUnknownClass(Object instance, Set <URL> jarUrls, Set <Path> directoryPaths, boolean bailError, int maxDeep) {
Class <?> cls = instance.getClass();
while (cls != null) {
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
Class <?> type = field.getType();
Object value;
try {
value = getValue(instance, field);
if (value != null) {
addFromUnknownValue(value, jarUrls, directoryPaths, bailError, type, field.getName(), maxDeep - 1);
}
} catch (IllegalArgumentException | IllegalAccessException | SecurityException e) {
if (bailError) {
final String version = System.getProperty("java.version");
String vers = version;
if (vers.startsWith("1.")) {
vers = vers.substring(2);
}
int dotindex = vers.indexOf('.');
if (dotindex != -1) {
vers = vers.substring(0, dotindex);
}
int versNum;
try {
versNum = Integer.parseInt(vers);
} catch (NumberFormatException e1) {
throw new RuntimeException("illegal version: '" + version + "' lastError: " + e.getMessage(), e);
}
if (versNum <= 11) {
throw new RuntimeException(e);
}
}
}
}
cls = cls.getSuperclass();
}
}
private static Object getValue(Object instance, Field field) throws IllegalArgumentException, IllegalAccessException, SecurityException {
try {
boolean flag = field.isAccessible();
boolean newflag = flag;
try {
field.setAccessible(true);
newflag = true;
} catch (Exception e) {}
try {
return field.get(instance);
} finally {
if (flag != newflag) {
field.setAccessible(flag);
}
}
} catch (IllegalArgumentException | IllegalAccessException | SecurityException e) {
try {
Field override = AccessibleObject.class.getDeclaredField("override");
boolean flag = override.isAccessible();
boolean newFlag = flag;
try {
override.setAccessible(true);
flag = true;
} catch (Exception s) {}
override.setBoolean(field, true);
if (flag != newFlag) {
override.setAccessible(flag);
}
return field.get(instance);
} catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e1) {
e.addSuppressed(e1);
throw e;
}
}
}
private static void addFromUnknownValue(Object value, Set <URL> jarUrls, Set <Path> directoryPaths, boolean bailError, Class <?> type, String fieldName, int maxDeep) {
if (Collection.class.isAssignableFrom(type)) {
for (Object obj : (Collection <?>) value) {
URL url = null;
try {
if (obj instanceof URL) {
url = (URL) obj;
} else if (obj instanceof Path) {
url = ((Path) obj).toUri().toURL();
} else if (obj instanceof File) {
url = ((File) obj).toURI().toURL();
}
} catch (MalformedURLException e) {
if (bailError) {
throw new RuntimeException(e);
}
}
if (url != null) {
addFromUrl(jarUrls, directoryPaths, url, bailError);
}
}
} else if (URL[].class.isAssignableFrom(type)) {
for (URL url : (URL[]) value) {
addFromUrl(jarUrls, directoryPaths, url, bailError);
}
} else if (Path[].class.isAssignableFrom(type)) {
for (Path path : (Path[]) value) {
try {
addFromUrl(jarUrls, directoryPaths, path.toUri().toURL(), bailError);
} catch (MalformedURLException e) {
if (bailError) {
throw new RuntimeException(e);
}
}
}
} else if (File[].class.isAssignableFrom(type)) {
for (File file : (File[]) value) {
try {
addFromUrl(jarUrls, directoryPaths, file.toURI().toURL(), bailError);
} catch (MalformedURLException e) {
if (bailError) {
throw new RuntimeException(e);
}
}
}
} else if (maxDeep > 0) {
addFromUnknownClass(value, jarUrls, directoryPaths, bailError, maxDeep - 1);
}
}
private static void addFromUrl(Set <URL> jarUrls, Set <Path> directoryPaths, URL url, boolean bailError) {
if (url.getFile().endsWith(".jar") || url.getFile().endsWith(".zip")) {
// may want better way to detect jar files
jarUrls.add(url);
} else {
try {
Path path = Paths.get(url.toURI());
if (Files.isDirectory(path)) {
directoryPaths.add(path);
} else if (bailError) {
throw new RuntimeException("unknown url for class loading: " + url);
}
} catch (URISyntaxException e) {
if (bailError) {
throw new RuntimeException(e);
}
}
}
}
imports:
import java.io.File;
import java.io.IOException;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.DirectoryStream;
import java.nio.file.DirectoryStream.Filter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.Attributes.Name;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
Based on #Staale's answer, and in an attempt not to rely on third party libraries, I would implement the File System approach by inspecting first package physical location with:
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
...
Class<?>[] foundClasses = new Class<?>[0];
final ArrayList<Class<?>> foundClassesDyn = new ArrayList<Class<?>>();
new java.io.File(
klass.getResource(
"/" + curPackage.replace( "." , "/")
).getFile()
).listFiles(
new java.io.FileFilter() {
public boolean accept(java.io.File file) {
final String classExtension = ".class";
if ( file.isFile()
&& file.getName().endsWith(classExtension)
// avoid inner classes
&& ! file.getName().contains("$") )
{
try {
String className = file.getName();
className = className.substring(0, className.length() - classExtension.length());
foundClassesDyn.add( Class.forName( curPackage + "." + className ) );
} catch (ClassNotFoundException e) {
e.printStackTrace(System.out);
}
}
return false;
}
}
);
foundClasses = foundClassesDyn.toArray(foundClasses);
plain java: FindAllClassesUsingPlainJavaReflectionTest.java
#Slf4j
class FindAllClassesUsingPlainJavaReflectionTest {
private static final Function<Throwable, RuntimeException> asRuntimeException = throwable -> {
log.error(throwable.getLocalizedMessage());
return new RuntimeException(throwable);
};
private static final Function<String, Collection<Class<?>>> findAllPackageClasses = basePackageName -> {
Locale locale = Locale.getDefault();
Charset charset = StandardCharsets.UTF_8;
val fileManager = ToolProvider.getSystemJavaCompiler()
.getStandardFileManager(/* diagnosticListener */ null, locale, charset);
StandardLocation location = StandardLocation.CLASS_PATH;
JavaFileObject.Kind kind = JavaFileObject.Kind.CLASS;
Set<JavaFileObject.Kind> kinds = Collections.singleton(kind);
val javaFileObjects = Try.of(() -> fileManager.list(location, basePackageName, kinds, /* recurse */ true))
.getOrElseThrow(asRuntimeException);
String pathToPackageAndClass = basePackageName.replace(".", File.separator);
Function<String, String> mapToClassName = s -> {
String prefix = Arrays.stream(s.split(pathToPackageAndClass))
.findFirst()
.orElse("");
return s.replaceFirst(prefix, "")
.replaceAll(File.separator, ".");
};
return StreamSupport.stream(javaFileObjects.spliterator(), /* parallel */ true)
.filter(javaFileObject -> javaFileObject.getKind().equals(kind))
.map(FileObject::getName)
.map(fileObjectName -> fileObjectName.replace(".class", ""))
.map(mapToClassName)
.map(className -> Try.of(() -> Class.forName(className))
.getOrElseThrow(asRuntimeException))
.collect(Collectors.toList());
};
#Test
#DisplayName("should get classes recursively in given package")
void test() {
Collection<Class<?>> classes = findAllPackageClasses.apply(getClass().getPackage().getName());
assertThat(classes).hasSizeGreaterThan(4);
classes.stream().map(String::valueOf).forEach(log::info);
}
}
PS: to simplify boilerplates for handling errors, etc, I'm using here vavr and lombok libraries
other implementations could be found in my GitHub daggerok/java-reflection-find-annotated-classes-or-methods repo
As of org.reflections version 0.10 :
org.reflections.scanners.SubTypesScanner
and
org.reflections.Reflections.getAllTypes()
are deprecated. I userd:
public Set<String> getEntityNamesInPackage(String packagePath) {
Reflections reflections = new Reflections(new ConfigurationBuilder()
.filterInputsBy(new FilterBuilder().includePackage(packagePath))
.setUrls(ClasspathHelper.forPackage(packagePath))
.setScanners(SubTypes.filterResultsBy(s -> true)));
return reflections.getAll(SubTypes).stream()
.filter(s -> s.startsWith(packagePath))
.collect(Collectors.toSet());
}
If you are merely looking to load a group of related classes, then Spring can help you.
Spring can instantiate a list or map of all classes that implement a given interface in one line of code. The list or map will contain instances of all the classes that implement that interface.
That being said, as an alternative to loading the list of classes out of the file system, instead just implement the same interface in all the classes you want to load, regardless of package and use Spring to provide you instances of all of them. That way, you can load (and instantiate) all the classes you desire regardless of what package they are in.
On the other hand, if having them all in a package is what you want, then simply have all the classes in that package implement a given interface.
Note that the interface itself doesn't have to declare any methods - it can be completely empty.
To inject a list of classes implementing a given interface, use the following lines of code...
#Autowired
private List<ISomeInterface> implementationList;
It is also possible to inject a Map of classes using Spring. Read the docs if interested to see how.
Finally, I will offer one other solution that is a bit more elegant than searching the entire file system tree.
Create a custom annotation that builds a catalog of the classes to which it is applied - something like #ClassCatalog.
It is not possible, since all classes in the package might not be loaded, while you always knows package of a class.

Can't load a BufferedImage

I have a form with that code:
public Form()
{
initComponents();
try
{
File file= new File("avatar.jpg");
BufferedImage image= ImageIO.read(file);
}
catch (IOException ex)
{
System.out.println("Failed to load image");
}
}
The problem is that the code always throws the IOException and enters in the catch block.
So the file isn't read.
I have created the project with Netbeans 7.2, and the directory looks like this:
What's the problem? Maybe the file shouldn't be there but in the father directory? Or what?
Is your image being packaged within your jar? to find this out, extract you jar file like you would an ordinary zip file and check if the image is anywhere there (normally located by jarname\packagename\filename. If so then you'll need to extract your image as a resource using getResourceAsStream().
It would be something like:
public class Test {
private static final String absName = "/yourpackage/yourimage.jpg";
public static void main(String[] args) {
Class c=null;
try {
c = Class.forName("yourpackage.Test");//pkg is the package name in which the resource lies
} catch (Exception ex) {
// This should not happen.
}
InputStream s = c.getResourceAsStream(absName);
// do something with it.
}
public InputStream getResourceAsStream(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader();
if (cl==null) {
return ClassLoader.getSystemResourceAsStream(name); // A system class.
}
return cl.getResourceAsStream(name);
}
public java.net.URL getResource(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader();
if (cl==null) {
return ClassLoader.getSystemResource(name); // A system class.
}
return cl.getResource(name);
}
private String resolveName(String name) {
if (name == null) {
return name;
}
if (!name.startsWith("/")) {
Class c = this;
while (c.isArray()) {
c = c.getComponentType();
}
String baseName = c.getName();
int index = baseName.lastIndexOf('.');
if (index != -1) {
name = baseName.substring(0, index).replace('.', '/') + "/" + name;
}
} else {
name = name.substring(1);
}
return name;
}
}
Reference:
Accessing Resources
It looks like you have a namespace of poker.*
It all depends on where the jvm is initialized from.
Where is your main? Is it in /Users/ramy/NetBeansProjects/Poker/src?
Also, I suggest you use getResource() for all of your file loading needs, especially inside jars.
this.getClass().getResource("/resource/buttons1.png")
or
this.getClass().getResourceAsStream("/resource/TX_Jello2.ttf")
You can find out where your programs default path is by doing the following:
System.getProperty("user.dir");
Without seeing the error I would say the most likely cause is it can't find the file. So I suggest you replace "avatar.jpg" in the File constructor with the absolute file path to it. e.g.
File file = new File("INSERT_PATH_TO_FILE/avatar.jpg");
You cannot assume the image will be "there" because the relative path between your .java and the image seems ok.
Accessing a resource depends of your "kind" of project (Web, standalone....). In your case, you can try to get the image from your classpath
final File inputFile = new ClassPathResource("....").getFile();
final BufferedImage inputImg = ImageIO.read(inputFile);

Can I dynamically discover xml files in the classpath inside an EJB 3 container?

Background:
One of the components of our project operates using spring. Some SQL code is dynamically generated, based on a given XML spring configuration.
At first it was fine to store all the XML configurations in the same package on the classpath, (and then load it as a resource when the service is called) but over time we ended up with a large number of configurations. It came time to separate the configurations into different namespaces.
The Goal
What I want is, given a starting package on the classpath, to recursively walk the directory structure and discover any spring XML files dynamically. (So that as new configurations / packages are added, the files will still be found by the service).
The Problem
I was able to accomplish my goal fine when running outside an EJB container by using Thread.getContextClassloader().getResource(myBasePackage), then getting a File object and using it to walk the tree on the filesystem. Clunky, I know, but it was still classpath relative and it worked.
However, you cannot do this inside an EJB container (you can't interact with the filesystem at all), so I had to use the rather annoying workaround in which I maintain a list of hardcoded packages to search.
The Question
Is there a way (running inside an EJB container) to dynamically walk the classpath (from a given starting location) searching for arbitrary resources?
Short answer: Not while staying in compliance with the EJB spec. Because the spec envisions containers running in all kinds of non-standard situations, it does not make this possible.
Longer answer: Since you are not creating these resources dynamically, I would write a routine that gives you a list of all of the resources at build time and puts them in a dynamically generated file that your EJB knows how to reference. So you basically create a directory listing of packages and files that you can load in the EJB that are referenced in one master file.
Spring answer: Spring supports finding resources on the classpath, although I have no idea how well this works in the EJB context (and I doubt its EJB compliant, but I haven't checked). Some details here.
DISCLAIMER: As already pointed out, creating resources in the classpath is not recommended and depending on the EJB container explicitly forbidden. This may cause you a lot of problems because containers may explode your resources into another folder or even replicate the resources throughout the cluster (if thats the case). In order to create resources dynamically you have to create a custom classloader. So, I would never do it. It is better to access the filesystem directly than the classpath. It is less ugly and eventually cluster-safe if you use a remote filesystem + file locks.
If even after all I explained you still want to play with the classpath, you can try to do something like: get the classloader via
ClassLoader cld = Thread.currentThread().getContextClassLoader();
Starting from a base package enumerate all occurrences
Enumeration<URL> basePackageUrls = cld.getResources(basePackagePath);
Each URL is generally either a file link (file:///home/scott/.../MyResource.properties) or a jar link (file:///lib.jar!/com/domain/MyResource.properties). You have to check the pattern in the URL. Using that, enumerate the contents of the folder using the normal java API and find the subpackages. Proceed until you have scanned all packages.
See the class below (will be released with an open-source project of mine soon). It implemens a classpath scanner that you can pass in a selector. It works like a visitor. It my work for you, if not, get ideas from it. See the sample annotation selector at the end.
public class ClasspathScanner
{
private static final Log log = LogFactory.getLog(ClasspathScanner.class);
private static final String JAR_FILE_PATTERN = ".jar!";
private ClassSelector selector;
private Set<Class<?>> classes;
// PUBLIC METHODS ------------------------------------------------------------------------------
public synchronized Set<Class<?>> scanPackage(String basePackage, ClassSelector selector)
throws Exception
{
if (selector == null)
{
throw new NullPointerException("Selector cannot be NULL");
}
this.selector = selector;
this.classes = new HashSet<Class<?>>();
Set<Class<?>> aux;
try
{
scanClasses0(basePackage);
aux = this.classes;
}
finally
{
this.selector = null;
this.classes = null;
}
return aux;
}
// HELPER CLASSES ------------------------------------------------------------------------------
private void scanClasses0(String basePackage)
throws IOException, ClassNotFoundException, FileNotFoundException
{
File packageDirectory = null;
ClassLoader cld = getLoader();
String basePackagePath = basePackage.replace('.', '/');
Enumeration<URL> basePackageUrls = cld.getResources(basePackagePath);
if (basePackageUrls == null || !basePackageUrls.hasMoreElements())
{
throw new ClassNotFoundException("Base package path not found: [" + basePackagePath
+ "]");
}
while (basePackageUrls.hasMoreElements())
{
String packagePath = basePackageUrls.nextElement().getFile();
if (packagePath.contains(JAR_FILE_PATTERN))
{
scanJarFile(basePackagePath, packagePath);
}
else
{
packageDirectory = new File(packagePath);
scanDirectory(basePackage, packageDirectory);
}
}
}
private void scanDirectory(String packageName, File packagePath)
throws ClassNotFoundException, FileNotFoundException
{
if (packagePath.exists())
{
File[] packageFiles = packagePath.listFiles();
for (File file : packageFiles)
{
if (file.isFile() && file.getName().endsWith(".class"))
{
String fullFileName = packageName + '.' + file.getName();
checkClass(fullFileName);
}
else if (file.isDirectory())
{
scanDirectory(packageName + "." + file.getName(), file);
}
}
}
else
{
throw new FileNotFoundException(packagePath.getPath());
}
}
private void scanJarFile(String basePackagePath, String jarFileUrl)
throws IOException, ClassNotFoundException
{
String jarFilePath = jarFileUrl.substring("file:".length(), jarFileUrl
.indexOf(JAR_FILE_PATTERN)
+ JAR_FILE_PATTERN.length() - 1);
log.debug("URL JAR file path: [" + jarFilePath + "]");
jarFilePath = URLDecoder.decode(jarFilePath, "UTF-8");
log.debug("Decoded JAR file path: [" + jarFilePath + "]");
JarFile jar = new JarFile(new File(jarFilePath));
for (Enumeration<JarEntry> jarFiles = jar.entries(); jarFiles.hasMoreElements();)
{
JarEntry file = jarFiles.nextElement();
String fileName = file.getName();
if (!file.isDirectory() && fileName.endsWith(".class")
&& fileName.startsWith(basePackagePath))
{
String className = fileName.replace('/', '.');
checkClass(className);
}
}
}
private void checkClass(String fullFilePath) throws ClassNotFoundException
{
String className = fullFilePath.substring(0, fullFilePath.length() - 6);
Class<?> c = getLoader().loadClass(className);
if (selector.select(c))
{
classes.add(c);
}
}
private ClassLoader getLoader()
{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null)
{
loader = getClass().getClassLoader();
}
return loader;
}
// INNER CLASSES -------------------------------------------------------------------------------
public interface ClassSelector
{
boolean select(Class<?> clazz);
}
public static class AnnotatedClassSelector implements ClassSelector
{
private final Class<? extends Annotation>[] annotations;
public AnnotatedClassSelector(Class<? extends Annotation>... annotations)
{
this.annotations = annotations;
}
public boolean select(Class<?> clazz)
{
for (Class<? extends Annotation> ac : annotations)
{
if (clazz.isAnnotationPresent(ac))
{
return true;
}
}
return false;
}
}
}

Categories

Resources