Problems searching for annotated classes using Reflections API - java

I'm having a very difficult time using the Reflections API to find classes that are annotated with a custom annotation at runtime. The ultimate goal is to find all classes in the project that are annotated with my custom #Job annotation, collect them, and allow each of them to be run from one location without adding each one to the page manually. However, I'm finding it extremely difficult to get the initial search to work correctly, so I cannot move on with my project.
My current approach is to use:
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setUrls(ClasspathHelper.forPackage("jobs"))
.setScanners(new TypeAnnotationsScanner())
.filterInputsBy(new FilterBuilder().includePackage("jobs")));
Set<Class<?>> jobs = reflections.getTypesAnnotatedWith(Job.class);
where "jobs" is the package containing all of the job classes that I am searching for, which will be annotated with the custom #Job annotation. "jobs" is a base package in my project, but the overall url on my machine looks something like ".../(project)/app/jobs". This setup results in one url being searched, which is ".../(project)/app/" with the additional filter "+jobs.*" in the configuration object. This seems like it is working correctly, but clearly something is wrong because I do not get any classes in the set.
If it matters, the annotation is coded as:
package jobs;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Job {
String description();
}
The annotation class is located within the same "jobs" package as the job classes I am searching for. An example of a job definition with the annotation included is:
package jobs;
#Job(description = "Description of what the job will do")
public class ExampleJob extends MasterJob {...}
I cannot find what I need to change in order to get this search to function as intended. Thanks for the help, and please let me know if I can clarify anything further.
EDIT: I believe the problem is associated with how the Play Framework loads its classes. Fortunately, the framework provides its own annotation search function, which I used instead. According to a comment, the code I have listed here will work, given that you have all the dependencies to run it. Feel free to use it as a template and let me know if it works for you as well.

Related

loading classes from custom dependency in a custom gradle plugin

I have a custom Gradle plugin, that works well, I have published a couple of versions already.
I have a new requirement, where I want to allow the plugin's users to provide an optional list of dependencies, in which they would provide classes, that can be loaded during the plugin execution.
Looking at the documentation, it seems to be quite close to what is described in https://docs.gradle.org/current/userguide/implementing_gradle_plugins.html#providing_default_dependencies_for_plugins (in my case there's no default though - or said differently, the default should be an empty list of extra dependencies), but I struggle to adapt it to my use case.
right now, my plugin can be configured like this :
archUnit {
preConfiguredRules = ["sample.SampleRule"]
}
the plugin already contains some rules, but I want to allow users to provide their own rules, they should be able to provide something like
dependencies {
archUnitDeps 'org.example:archunit-custom-rules:1.0'
}
if the provided class (sample.SampleRule) is in that extra dependency that we configure, the plugin should load it and apply it, just like it does with the rules that are currently packaged with the plugin.
Following the docs, I create a configuration in my plugin :
Configuration deps = project.getConfigurations().create("archUnitDeps", c -> {
c.setVisible(false);
c.setCanBeConsumed(false);
c.setCanBeResolved(true);
c.setDescription("The packaged extra rules you may want to add ");
});
but then what should I do ?
I've tried something like this, to get the dependencies that the user configures, and add them at project level :
DependencyHandler dependencies = project.getDependencies();
deps.getAllDependencies().stream().forEach( extraDep -> dependencies.create(extraDep));
but when using it in a project, deps.getAllDependencies() is empty, even though I configure the org.example:archunit-custom-rules:1.0 dependency as above. So nothing gets loaded, and I end up with a java.lang.ClassNotFoundException: sample.SampleRule.
what am I missing ? do you have a readily available example of a plugin that does something similar, that I could get inspiration from ?
Finally was able to solve this, thanks to the help I got on https://discuss.gradle.org/t/class-loaded-through-configuration-is-not-found-when-task-runs/42945/3
I am quite surprised there's not a simpler solution, but one way of doing, is to use Gradle worker API, which takes 2 main steps :
in the task, get access to the workQueue, and submit the parameters required for the task execution :
WorkQueue queue = getWorkerExecutor().classLoaderIsolation(spec -> spec.getClasspath().from(getClasspath()));
queue.submit(WorkerAction.class, params -> {
params.getMainClassesPath().set(someValue);
params.getTestClassesPath().set(someOtherValue);
etc..
})
.collect(Collectors.toList()));
});
On the worker side, get back those parameters, and launch what you were initially doing in the task :
public abstract class WorkerAction implements WorkAction<WorkerActionParams> {
#Override
public void execute() {
WorkerActionParams params = getParameters();
....
}
The parameters you pass need to be serializable - I was not able to make that part fully work with my original parameters, so I had to create new "wrapper" ones.
full example is available here : https://github.com/societe-generale/arch-unit-gradle-plugin/pull/24/files

Creating and using annotation in java

I am reviewing open source spring projects. I am confused about the use of annotations around here. I want to ask to clarify this.
#Target(ElementType.METHOD)
#Retention(RUNTIME)
#Bean
public #interface Merge {
#AliasFor("targetRef")
String value() default "";
#AliasFor("value")
String targetRef() default "";
Placement placement() default Placement.APPEND;
int position() default 0;
Class<MergeBeanStatusProvider> statusProvider() default MergeBeanStatusProvider.class;
boolean early() default false;
}
An annotation has been created here named Merge. It has different parameters and default values.
#Configuration
public class LocalConfiguration {
#Merge(targetRef = "mergedList", early = true)
public List<String> blLocalMerge() {
return Arrays.asList("local-config1", "local-config2");
}
}
And this is usage of #Merge annotation in any class I choosed randomly.
When I examined the code, I could not find any class related to the implementation of Merge annotation. By the way, this problem I'm having isn't just about this annotation. Almost all the annotations I have examined are used without being implemented in any way.
I think I will understand the others if we start from this annotation.
What does this anotation do? What kind of message does it give to the place where it is used. How does the application understand what that annotation does in runtime without being implemented anywhere.
Thanks.
Annotations don't have implementations. They are processed by external classes or tools depending on the RetentionPolicy. In this case, the Merge annotation has Runtime retention so it will be available via reflection once the class is loaded. At runtime any interested party (in this case I assume the Spring Framework) can use getAnnotations on your LocalConfiguration class to detect the Merge annotation and take whatever action that needs to be taken. The possibilities are really up to the framework that defined the annotation. A lot of Spring injection works like this with annotations but they are also used by many other frameworks such as Hibernate, Jersey, etc. The main idea is that annotations act as markers on specific code points to be used by an external entity at a later point.

When and how cglib-proxied component instance is created

I'd like to learn if there are some rules / conditions that a Spring component is wrapped (proxied) by CGLIB. For example, take this case:
#Component
public class TestComponent {
}
#Service
//#Transactional(rollbackFor = Throwable.class)
public class ProcessComponent {
#Autowired
private TestComponent testComponent;
public void doSomething(int key) {
// try to debug "testComponent" instance here ...
}
}
If we let it like this and debug the testComponent field inside the method, then we'll see that it's not wrapped by CGLIB.
Now if we uncomment the #Transactional annotation and debug, we'll find that the instance is wrapped: it's of type ProcessComponent$$EnhancerByCGLIB$$14456 or something like that. It's clearly because Spring needs to create a proxy class to handle the transaction support.
But I'm wondering, is there any way that we can detect how and when does this wrapping happen ? For example, some specific locations in Spring's source code to debug into to find more information; or some documentations on the rules of how they decide to create a proxy.
For your information, I need to know about this because I'm facing a situation where some component (not #Transactional, above example is just for demonstrating purpose) in my application suddenly becomes proxied (I found a revision a bit in the past where it is not). The most important issue is that this'll affect such components that also contain public final methods and another issue (also of importance) is that there must have been some unexpected changes in the design / structure of classes. For these kind of issues, of course we must try to find out what happened / who did the change that led to this etc...
One note is that we have just upgraded our application from Spring Boot 2.1.0RELEASE to 2.1.10RELEASE. And checking the code revision by revision up till now is not feasible, because there have been quite a lot of commits.
Any kind of help would be appreciated, thanks in advance.
You could debug into org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(Class, String, TargetSource).
If any advisor is found, the bean will be proxied.
If you use a #Lookup method injection it will also proxy the component class.

JAXB - #XmlJavaTypeAdapter on a WS return value

I am actually trying to build a process for refactoring a large number of existing webservices.
The idea is to use JAXB/JAXWS tools to automate this as much as possible.
Most of our issues are resolved except for one blocking problem :
JAXB by default serializes boolean types as "true"/"false". I need those values to be "0"/"1".
On the existing classes of our codebase, I'm trying to use as much annotations as possible, and of course not modifying at all what is auto generated by JAX tools.
After adding correct annotations, I run wsgen so it generates JAX-WS necessary classes for deployment.
Here is a concrete example of an annotated webservice method:
public #WebResult(name = "success")
#XmlJavaTypeAdapter(value = lu.ept.common.xmlSerializers.BooleanAdapter.class, type = boolean.class)
boolean modifyStatus(#WebParam(name = "processActionId") String processActionId, #WebParam(name = "newStatus") int newStatus)
throws BusinessMessage, SystemMessage {
When running wsgen, it picks up correctly the WebResult and WebParam attributes, but it refuses to use my XmlJavaTypeAdapter.
My question is very simple : has someone managed to use XmlJAvaTypAdapter annotation on a webservice method return value? Is it possible?
(on the documentation I read, I haven't seen anything concerning the use of that annotation on return values)
Actually the only solution I have found is to manually add the XmlJavaTypeADapter annotation to the classes generated by wsgen. This is not a viable solution, because those classes will be generated after each build...

IClassFile annotations

How do you check for Annotations when using IClassFile in Eclipse?
This doesnt seem to work classFile.getClass().isAnnotationPresent? Any help is appreciated.
The problem with using
for (final IClassFile classFile : classFiles) {
IAnnotation[] annotations = classFile.getType().getAnnotations();
Is that I have to get All the Packages, then get the Class Files in that package then get the Annotations. It will require 3 loops. Is there a way to minimize this?
I would say that the easiest way for you to find annotations is through a triple loop, but it might be slightly faster (assuming you are looking for a specific annotation) to use a 'SearchEngineinstead. Take a look at the source code for theorg.eclipse.jdt.internal.junit.launcher.JUnit4TestFinder` class. It looks for (source) classes annotated with #Test or #RunWith, which is similar to what you want to do, but for binary classes.
You would do something like this:
IJavaElement[] allPackagesToSearch = ...
SearchRequestor requestor = <implement the SearchRequestor abstract class and store all matches>
IJavaSearchScope scope= SearchEngine.createJavaSearchScope(binaryPackages, IJavaSearchScope.APPLICATION_LIBRARIES);
int matchRule= SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE;
SearchPattern runWithPattern= SearchPattern.createPattern("com.foo.MyAnnotation", IJavaSearchConstants.ANNOTATION_TYPE, IJavaSearchConstants.ANNOTATION_TYPE_REFERENCE, matchRule);
SearchParticipant[] searchParticipants= new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() };
new SearchEngine().search(annotationsPattern, searchParticipants, scope, requestor, new SubProgressMonitor(pm, 2));
It's a bit of a mouthful, and to figure out how this works, I'd recommend reading the JavaDoc for SearchEngine, SearchPattern, and SearchRequestor.
If you want to find all annotations, then change the match rule, and instead of "com.foo.MyAnnotation", use "*".

Categories