Get package names using java reflections - java

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);

Related

How do I get the directory of the 'input' source files in an annotation processor?

I want to get the directories of the source files which are getting compiled after annotation processing while doing the annotation processing without relying on directory/build tool conventions.
public class MyProcessor extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// Get the directories with source files to be compiled here
// For example, I should be able to find all classes which I obtain via the following in the directories I want:
roundEnv.getElementsAnnotatedWith(MyAnnotation.class)
}
}
I have tried:
A relative path which I got with Paths.get("."). But not only would I still have to rely on directory/build tool conventions to get to the source directories, it also doesn't work when the build is started from anywhere else but the project root.
Checking all properties in System.getProperties() and System.getenv() to see if there is anything useful being set.
Checked javax.lang.model.element.TypeElement, javax.lang.model.type.TypeMirror and the corresponding utility methods to see if I can get an instance's location (Did I miss something?)
Checked various combinations of StandardJavaFileManager's methods, some of which suggest to return what I want, but returning either null/empty collections or throwing exceptions for module related parameters. Example: standardJavaFileManager.list(StandardLocation.SOURCE_PATH, "", Set.of(Kind.SOURCE), true).
// edit
I'm trying to create a functionality where classes can be picked up based on super-types. For that, I created an Annotation where I can specify these super-types. I now need to scan all source files to check for classes that are sub-types of X, for which I am using a library. Said library needs to be pointed to a directory containing the classes to scan, which is why I need the source folder.

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.

How to reference a class in an external JAR in Eclipse?

I would like to reference a class Bag in a JAR file, but Eclipse is telling me that Bag cannot be resolved to a type. I have added the JAR in which Bag is defined to the classpath for the project, but the error is still there. What am I doing wrong?
I think you can't do that, because the Bag class in algs4.jar is inside the default package.
Before J2SE 1.4, we still can import classes from the default package using a syntax like this:
import Unfinished;
But from J2SE 1.5, that's no longer allowed. So if we want to access a default package class from within a packaged class requires moving the default package class into a package of its own. Read here for more detail explanation :
How to access java-classes in the default-package?
Some options you can choose :
Access the class via reflection or some other indirect method. But it is a little bit hard, something like this :
Class fooClass = Class.forName("FooBar");
Method fooMethod = fooClass.getMethod("fooMethod", new Class[] { String.class });
String fooReturned = fooMethod.invoke(fooClass.newInstance(), new String("I did it"));
If you own the source code of that jar library, you need to put it in properly package and wrap it again as a new jar library.
You may need to either fully qualify the Bag class, or import it.

load class only

I have developed eclipse plugin which for any given java project create GUI in form of package structure. I have successfully run my plugin for different java project.
Now, I thought should try my code in some open source project, therefore, I download JDOM Framework.
However, I found that the JDOM source code has this structure.
JDOM -> contrib -> src -> java -> org -> jdom2......
where as i assume that the project will have always below structure
Project Name -> Src -> PACKAGE NAME STARTS HERE.....
I load the classes using below code,
IPackageFragment[] packages = javaProject.getPackageFragments();
for (IPackageFragment mypackage : packages) {
if (mypackage.getKind() == IPackageFragmentRoot.K_SOURCE) {
for (ICompilationUnit unit : mypackage.getCompilationUnits()) {
// unit.getPath().toString() give me path, but how to extract only class name with package
// save it in to MAP with Package as key
}
}
}
Now, I want to show classes with only package name, therefore, I remove first two string (PROJECT NAME, SRC), but this cannot be always the case as for JDOM Framework.
Therefore, how can I get only package name along with class name using my method above? Or should I use different mechanism?
Looking at the directory structure alone seems to be an awfully error-prone way to go about it. Who knows how deep the directory tree goes? If instead you scan for Java source files, you should be able to construct a reader that finds the package declaration at the beginning of the file. If there isn't one, you don't need to worry about it. Do I need to say you can store package names in a HashSet to avoid duplicate package declarations?
The ICompilationUnit has a findPrimaryType method:
IType primaryType = unit.findPrimaryType();
and IType has getFullyQualifiedName():
String name = primaryType.getFullyQualifiedName();

Load classes from folder without specifying the package

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.

Categories

Resources