I have a java project containing a spring boot application called processor. This project depends on a project called rules and a project called service. Every project has the same package pattern - my.com.package.
The processor and rules projects both contain classes annotated with a custom annotation #Condition. The annotation interface is annotated with #Retention(RetentionPolicy.RUNTIME). When I scan for classes annotated with #Condition from service or processor like this
private ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(
false);
scanner.addIncludeFilter(new AnnotationTypeFilter(Condition.class));
for (BeanDefinition bd : scanner.findCandidateComponents("my.com")) {
try {
Class<?> c = Class.forName(bd.getBeanClassName());
Condition condition = c.getAnnotation(Condition.class);
register(condition);
} catch (ClassNotFoundException | IOException e) {
logger.error(e.getLocalizedMessage());
}
}
The classes annotated with #Condition in the processor project have the correct class name(my.com.package.x.Class), but the classes annotated with #Condition in the rules project have an incorrect fully qualified class name(my.com.Class) and it only finds 2 out of 5 class names in the project that have the annotation.
If I change the argument to scanner.findCandidateComponents to the full package path in the rules project (my.com.package.rules) while scanning in either processor or service the scanner finds no candidates. If I use my.com.* as the argument it only finds the candidates in the processor project.
I saw a similar question here Using ClassPathScanningCandidateComponentProvider with multiple jar files? and the solution was to pass the class loader to the component provider. I tried getting the class loader of the class doing the scanning and passing it to the provider like this
scanner.setResourceLoader(new PathMatchingResourcePatternResolver(classLoader));
and it didn't change any results for me.
Silly mistake, the problem was I had the wrong version of the rules project defined in the pom for my processor project so it was using an older version of the code.
However this
Condition condition = c.getAnnotation(Condition.class);
returned null for the classes taken from the jar, so this concerns me a little if this code isn't being run from source in my workspace.
Related
Lets say i have a method in some class in my application's package NetBeans project:
package wuzzle.woozle;
import org.contoso.frobber.grob.Whiztactular;
#Whiztactular
public void testFizBuzz() {
if (1 != 0)
throw new Exception("Whiztactular failed");
}
package frob;
import org.contoso.frobber.grob.Whiztactular;
#Whiztactular
public void testfrobFizBuzz() {
if (1 != 0)
throw new Exception("Whiztactular failed");
}
package grob;
import org.contoso.frobber.grob.Whiztactular;
#Whiztactular
public void testGrobZoom() {
if (1 != 0)
throw new Exception("Whiztactular failed");
}
package contoso.gurundy;
import org.contoso.frobber.grob.Whiztactular;
#Whiztactular
public void testDingbatWoozle() {
if (1 != 0)
throw new Exception("Whiztactular failed");
throw new Exception("Whiztactular failed");
}
I want to:
enumerate all classes/methods
find methods tagged with a specified #Annotation
construct the class
call the (parameterless) method
How can i do this in Java?
In .NET it's easy
Here's how you do it in .NET (in pseudo-Java):
//Find all methods in all classes tagged with #Test annotation,
//and add them to a list.
List<MethodInfo> whiztactularMethods = new ArrayList<>();
//Enumerate all assemblies in the current application domain
for (Assembly a : AppDomain.currentDomain.getAssemblies()) {
//Look at each type (i.e. class) in the assembly
for (Type t : a.getTypes()) {
//Look at all methods in the class.
for (MethodInfo m : t.getMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly)) {
//If the method has our #Whiztactular annotation defined: add it
if (m.IsDefined(typeof(org.contoso.frobber.grob.Whiztactular), true))
whiztactularMethods .add(m);
}
}
}
And now that we have a List of all methods with the #Whiztactular annotation, it's just a matter of calling them:
//Call every test method found above
for (MethodInfo m : whiztactularMethods) {
Object o = Activator.CreateInstance(m.DeclaringType); //Construct the test object
m.Invoke(o, null); //call the parameterless Whiztactular method
}
What is the JRE equivalent of the above?
In Delphi it's easy to
When a Delphi application starts, the initializer of each unit is called:
initialization
WhiztactularRunner.registerWhiztactularClass(TWuzzleWoozle);
So then i can have all my test code register itself.
But Java doesn't have .java file initialization; nor does it have static constructors.
The Journey
I want JUnit to run tests
↓
JUnit requires tests to be in a special separate project
↓
Use reflection to find the test methods
↓
Reflection requires you to know the name of the packages that all developers have put their tests in
↓
Use Reflections library
↓
Reflections requires you to know the name of the packages that all developers have put their tests in
↓
Create my own Test Annotation, and use reflections to find all methods that are tagged with it
↓
Reflections requires you to know the name of the packages that all developers have put their tests in
↓
Create my own TestCase annotation, and use reflections to find all classes that are tagged with it
↓
Reflections requires you to know the name of the packages that all developers have put their tests in
↓
Create my own TestCase interface, and use reflections to find all classes that implement it
↓
Reflections requires you to know the name of the packages that all developers have put their tests in
↓
Create my own TestCase class, and use reflections to find all classes that extend it
↓
Reflections requires you to know the name of the packages that all developers have put their tests in
↓
Create a static list, and use a static class constructor to register the class with the my TestRunner
↓
Java doesn't have static class constructors
↓
Create a static list, and use the package initializer to register the class with the my TestRunner
↓
Java doesn't have package initializers
↓
Create a static list, and use the events to listen for when a package is loaded, and then register the package with my static list
↓
Java doesn't have package load events
↓
Enumerate all packages
↓
Reflection has no way to enumerate all packages
↓
Ask the class loader that loaded my current class for any other classes it has loaded
↓
Class loader won't know about classes until someone has actually needed them, and you might not even be using the same class loader instance
↓
Enumerate all packages in the current class path ⇐ in progress
↓
Enumerate all jar files on the local PC, use a custom class loader to load each one, then get a list of all packages in each one ⇐ in progress
↓
Spent 4 days so far trying to solve this problem that was solvable in .NET with 5 lines of code, and in Delphi with 3 lines of code
↓
Investigate converting 409 jsp, and 498 java code files to ASP.net and C# ⇐ in progress
↓
Give up on having automated unit, functional, and integration tests ⇐ in progress
Research Effort
Get all methods with a particular annotation in a package (Question isn't about the current package. Accepted answer uses 3rd party library.)
Java seek a method with specific annotation and its annotation element (Question is about a specific class, rather than finding the classes)
How to find annotated methods in a given package?
How to run all methods with a given annotation?
getConstructor with no parameters
Call Methods at Runtime Using Java Reflection
JavaDocs - Invoking Methods
Default access modifier for a Java constructor
Can you find all classes in a package using reflection?
Get all methods with a particular annotation in a package (explains what a package is)
How to find annotated methods in a given package? (explains what a package is)
Additional research effort
java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory even though I have the right dependencies
Oracle: Handling Initialization Status With Event Handlers
how to register a java class if the static initializer isn't called till the class is referenced
Dynamic object registration in Java
getConstructor with no parameters
Load Jar dynamically and register class(es) in applicationContext at runtime
Is it possible to determine descendants solely through Java reflection API?
Call Methods at Runtime Using Java Reflection
JavaDocs - Invoking Methods
At runtime, find all classes in a Java application that extend a base class
Default access modifier for a Java constructor
Find Java classes implementing an interface
Finding all classes implementing a specific interface
How does JUnit find tests?
Book: Unit Testing in Java
2/28/1998: JUnit 1.0
JUnit Cookbook
How can I get a list of all the implementations of an interface programmatically in Java?
How can I get all Class files in a specific package in Java?
Class Loaders in Java
How can I enumerate all classes in a package and add them to a List?
Java Reflection - Get List of Packages
Getting the list of packages in a java project
Tool to convert java to c# code
Package Initialization in Java
How to write a package-level static initializer in Kotlin?
https://stackoverflow.com/questions/72795950/java-initialize-all-classes-in-package-without-knowing-names
https://github.com/classgraph/classgraph
What is an initialization block?
Package Initialization in Java
Java and .Net are fundamentally different. The reason this task is not supported in the JVM out of the box is because of JVM's intrinsic lazy class loading; the JVM is not fully aware of every possible class on the classpath and loading all of them would be extraordinarily intensive CPU and Memory wise.
Essentially: What you are asking for is not possible without extensive writing of code that can scan avaialable classes on the classpath without loading them and examine their contents. You could implement this yourself, but it will be quite intensive. If you do wish to go that route, the answer you mentioned How to find annotated methods in a given package? is likely the easiest method, but will be non-optimal.
All is not lost though: I think the easiest way to accomplish the task you're wanting is to use a library like the following: https://github.com/ronmamo/reflections
The Reflections library is able to scan the classpath without actually performing a classload and is quite quick. I use this lib personally for writing frameworks and scanning for developer extensions on startup.
Example:
// MethodsAnnotated with #GetMapping
Set<Method> resources = reflections.get(MethodsAnnotated.with(GetMapping.class).as(Method.class));
Use ClassGraph. Unlike Reflections, it's (exceptionally) actively maintained and works in many more scenarios (e.g. Reflections will keel over if you try to use it with modules).
try (ScanResult scanResult = new ClassGraph()
.enableAllInfo() // Scan classes, methods, fields, annotations
.scan()) { // Start the scan
for (ClassInfo clazz : scanResult.getClassesWithMethodAnnotation(annotation)) {
//Just your run-of-the-mill reflection from here
Class<?> loaded = clazz.loadClass(); //SEE THE NOTE
//Assumes the default constructor exists. Do what's appropariate if it doesn't.
Object instance = loaded.getConstructor().newInstance(...);
//Use the usual clazz.getMethods() and filter by annotation, or use ClassInfo to get closer
Method annotatedMethod = ...;
annotatedMethod.invoke(instance);
}
}
NOTE: ClassGraph tries its best to figure out what classloader to use when loading classes, but you can still end up in bizarre situations. So, if you know already the correct classloader to use, I'd recommned going with the regular Java reflection here instead, e.g. Class.forName(clazz.getName(), loader).
I need to add an annotation to a class which is in a sperate dependency (in a separate jar) of the project that I am working on. I am wondering that can I do this in Java?
As an example, I need to add an annotation HisClass without touching the source code of HisClass.
public class HisClass{
// ...
}
So, that the above class should be look like below at the build time,
#MyAnnot ( me = MyClass.class )
public class HisClass{
// ...
}
There are many ways:
Write a compiler plugin for javac, and make it add the annotations. This will be quite difficult, as the plugin API has nearly no documentation.
2.(Maybe not possible) Inject the annotation after compiling. Add an extra buildstep after compiling and use a library like ASM to add this annotation to the classfile.
So, we have been trying to xploring bazel as a build system for our organization, since we have a huge monorepo.
One of the problems I'm facing is, we have some code-gen classes which use Jackson's annotation processors to generate immutable copies of some file types.
Eg :
#JsonSerialze
#JsonInclude(JsonInclude.Include.NON_EMPTY)
#Value.Immutable
#JsonDeserialize(as=ImmutableABC.class)
Public abstract class ABC {
...
}
So, for this , I include a java_plugin tag in bazel build file for this module as follows :
Java_plugin(
name="abcgen",
srcs=["src/.../ABC.java"],
Deps=[ {jackson-deps go here}],
processor_class = "org.immutables.processor.ProxyProcessor",
)
This always fails , saying cannot find the ImmutableABC.class file which is referenced in the annotation.
Any ideas? Am I missing the processor class for the Jackson annotations, and also is it possible to include multiple processor classes?
For anyone facing issues as such, ensure generates_api = 1 for your plugin, if the generated classes are used in the library, as for the jackson part, that wasn't really the problem
I have a web service we'll call service.war. It implements an interface we'll call ServicePluginInterface. During the startup of service.war, it reads in environment variables and uses them to search for a jar (MyPlugin.jar). When it finds that jar, it then uses a second environment variable to load the plugin within the jar. The class that it loads looks like this:
public class MyPlugin implements ServicePluginInterface {...}
The servlet attempts to load the plugin using code like:
try {
if (pluginClass == null) {
plugin = null;
}
else {
ZipClassLoader zipLoader = new ZipClassLoader(Main.class.getClassLoader(), pluginJar);
plugin = (ServicePluginInterface)zipLoader.loadClass(pluginClass).newInstance();
plugin.getAccount(null,null);
}
} catch (Exception e) {
...
}
The trick is that I don't have source or a jar for ServicePluginInterface. Not wanting to give up so easily, I pulled the class files out of the service.war files. By using those class files as dependencies, I was able to build, without compiler warnings, MyPlugin. However, when actually executed by Tomcat, the section of code above generates a runtime exception:
java.lang.ClassCastException: com.whatever.MyPlugin cannot be cast to com.whomever.ServicePluginInterface
As a second point of reference, I am also able to construct a synthetic class loader (separate java executable that uses the same class loading mechanism. Again, since I do not have the original source to ServicePluginInterface, I used the class files from the WAR. This second, synthetic loader, or faux-servlet if you will, CAN load MyPlugin just fine. So I would postulate that the Tomcat JVM seems to be detecting some sort of difference between the classes found inside the WAR, and extracted class files. However, since all I did to extract the class files was to open the WAR as a zip and copy them out, it is hard to imagine what that might be.
Javier made a helpful suggestion about removing the definition of ServicePluginInterface, the problem with that solution was that the ZipClassLoader that the servlet uses to load the plugin out of the jar overrides the ClassLoader findClass function to pull the class out of the JAR like so:
protected Class<?> findClass(String name) throws ClassNotFoundException
{
ZipEntry entry = this.myFile.getEntry(name.replace('.', '/') + ".class");
if (entry == null) {
throw new ClassNotFoundException(name);
}
...
}
The class ZipClassLoader then recursively loads all parent objects and interfaces from the jar. This means that if the plugin jar does not contain the definition for ServicePluginInterface, it will fail.
Classes defined by different class loaders are different:
At run time, several reference types with the same binary name may be
loaded simultaneously by different class loaders. These types may or
may not represent the same type declaration. Even if two such types do
represent the same type declaration, they are considered distinct. JLS
In that case zipLoader returns an instance of MyPlugin that implements the other ServicePluginInterface (is it loaded from the zip too?):
(ServicePluginInterface)zipLoader.loadClass(pluginClass).newInstance();
It seems that the application server already has a definition of ServicePluginInterface, then you don't need to redeploy it. It should be enough to add the required files (ServicePluginInterface, etc.) as non-deployed dependecies of your project.
Another approach goes by living with the fact, and accessing methods in ServicePluginInterface via reflection (use the Class object returned by zipLoader, instead of ServicePluginInterface.class).
Problem
I'm writing a standalone utility program which, given a jar containing a JPA-2 annotated persistence unit, needs to programmatically get a list of all my #Entity classes in a particular persistence unit.
I'd like to decide which of 2 approaches would be the way to go to get this information, and why; or if there is another better way I haven't thought of.
Solution 1
Java program puts jar on the classpath, creates persistence unit from the classes in the jar using JavaSE methodologies. Then it uses the javax.persistence classes to get the JPA Metamodel, pull back list of class tokens from that.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("MY_ PERSISTENCE_UNIT");
Metamodel mm = emf.getMetamodel();
// loop these, using getJavaType() from Type sub-interface to get
// Class tokens for managed classes.
mm.getManagedTypes();
Solution 2
Program scan the directories and files inside the specified jar for persistence.xml files, then finds one with the specified persistence unit name. Then XPath the file to get the list of <class> XML elements and read the fully qualified class names from there. From names, build class tokens.
Constraints/Concerns
I'd like to go with approach 1 if possible.
This utility will NOT run inside a container, but the jar is an EJB project designed to run inside one. How will this be a problem?
The utility will have Open-EJB available on the classpath to get implementations of all the Java EE 6 classes.
Even though the EJB project is built to run on Hibernate, the utility should not be Hibernate-specific.
Are there any stumbling blocks?
In case anyone's interested, Solution 1 worked. Here's essentially what I had to do:
public MySQLSchemaGenerator() throws ClassNotFoundException {
Properties mySQLDialectProps = new Properties();
mySQLDialectProps.setProperty("javax.persistence.transactionType", "RESOURCE_LOCAL");
mySQLDialectProps.setProperty("javax.persistence.jtaDataSource", "");
final EntityManagerFactory emf = Persistence.createEntityManagerFactory("<persistence_unit_name>", mySQLDialectProps);
final Metamodel mm = emf.getMetamodel();
for (final ManagedType<?> managedType : mm.getManagedTypes()) {
managedType.getJavaType(); // this returns the java class of the #Entity object
}
}
The key was to override my transaction type and blank out the jtaDataSource which had been defined in my persistence.xml. Turns out everything else was unnecessary.
If Your jar is well-formed (persistence.xml at the right place - in the META-INF folder), then all looks fine.
It is not necessary to run your utility inside a container, JPA is not a part of JavaEE specs.