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).
Related
Goal
I would like to create a plug-in wrapper to embed a Java code that will let me enable/disable methods annotated with JUnit5 Before* and After* annotations at runtime as well as handle in similar manner JUnit5 extensions (the ExtendWith annotation).
I chose ByteBuddy as primary instrumentation library.
I created a github repo here: https://github.com/azewiusz/bytebuddy_examples where I describe the idea in more detail.
Question
The problem is that I cannot get instrumentation working for inner classes.
I keep getting two exceptions:
Caused by: java.lang.IllegalStateException: Failed to invoke proxy for
public abstract java.lang.reflect.AnnotatedElement
net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$Executable.getAnnotatedReceiverType(java.lang.Object)
preceded by
Caused by: java.lang.IncompatibleClassChangeError
tests.TestSetWithInnerClasses and
tests.TestSetWithInnerClasses$HelperTestFilteredOutExtendWithInnerClass
disagree on InnerClasses attribute
A test class that reproduces is here:
https://github.com/azewiusz/bytebuddy_examples/blob/master/src/test/java/tests/CoreInstrumentationTest.java
-> Look there for test2
It fails at this transformation:
final Class strippedOffExtendWithAnnotation = filterOutJUnit5ExtendWithAnnotation(
testClass, classLoader );
Class beforeAll = stagedTypeTransform( strippedOffExtendWithAnnotation,
new ByteBuddy().rebase( strippedOffExtendWithAnnotation,
ClassFileLocator.ForClassLoader.of( classLoader ) )
.name( testClass.getName() + "BeforeAll" )
.method( ElementMatchers
.isAnnotatedWith( BeforeAll.class ) )
.intercept( MethodDelegation.to( InterceptorForBeforeAllAnnotation.class ) ).make() );
I found following that seem to refer to similar problem:
How to access type annotations on a receiver type's parameters
https://github.com/raphw/byte-buddy/issues/83
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=7003595 (seems
to be partially related)
But haven't found yet an exact solution.
Instrumenting inner classes is tricky. Java classes contain so-called inner class attributes that describe Java class properties that compiled Java classes cannot represent. For example, a compiled Java class cannot be private, but you would still want to see this modifier for the inner class when using reflection.
You can consider to make your new class a top-level class by using topLevelType() in the DSL. You should also consider noNestMate() to avoid clashes there.
I am using the maven dependency hive-hcatalog-core in my program
and this jar present in the project maven dependencies, with the interface (as in the image-top).
The interface ICacheableMetaStoreClient , though present the class, is NOT resolvable from import org.apache.hive.hcatalog.common. (image -bottom)
consequently, while doing a spark-submit, I am getting the exception :
com.google.common.util.concurrent.UncheckedExecutionException:
java.lang.IllegalArgumentException: interface
org.apache.hive.hcatalog.common.HiveClientCache$ICacheableMetaStoreClient
is not visible from class loader
What do I need to do for this to be visible from the program class path.
Lets look at the code:
class HiveClientCache {..}
The HiveClientCache has only package level visibility and it along with ICacheableMetaStoreClientwont wont be available for import outside of that package (this includes in your code).
Now lets look at ICacheableMetaStoreClient:
#InterfaceAudience.Private
public interface ICacheableMetaStoreClient extends IMetaStoreClient {....}
The interface is public but has annotation on it that makes the Hive/Hadoop additional preprocessing to check object type and throw IllegalArgumentException.
Here is the JavaDoc for InterfaceAudience:
Annotation to inform users of a package, class or method's intended
audience. Currently the audience can be InterfaceAudience.Public,
InterfaceAudience.LimitedPrivate or InterfaceAudience.Private. All
public classes must have InterfaceAudience annotation.
Public classes that are not marked with this annotation must be considered by default as InterfaceAudience.Private.
External applications must only use classes that are marked InterfaceAudience.Public. Avoid using non public classes as these
classes could be removed or change in incompatible ways.
Hadoop projects must only use classes that are marked InterfaceAudience.LimitedPrivate or InterfaceAudience.Public
Methods may have a different annotation that it is more restrictive compared to the audience classification of the class. Example: A class
might be InterfaceAudience.Public, but a method may be
InterfaceAudience.LimitedPrivate
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.
I am working with grails application. I want to perform methods like get(),save(),delete(),findBy() etc and associated with domains of grails application. When I execute Domain.get() method inside Utils or src/groovy package I get following error.
Caught: java.lang.IllegalStateException: Method on class [Domain Class] was used outside of a Grails application. If running in the context of a test using the mocking API or bootstrap Grails correctly.
java.lang.IllegalStateException: Method on class [Domain Class] was used outside of a Grails application. If running in the context of a test using the mocking API or bootstrap Grails correctly.
Any one help me how can I use domain class method inside Utils or src/groovy classes?
This error is caused by interacting with GORM entities that have not been properly registered with GORM. The error doesn't have anything to do with where the interaction is initiated from (src/groovy, grails-app/services/, etc...).
Not always, but most often this error occurs in a testing environment where the entity type has not been registered with GORM.
See the project at https://github.com/jeffbrown/illegalaccessgorm. That project contains the following.
A domain class:
// grails-app/domain/demo/Person.groovy
package demo
class Person {
String name
}
A service:
// grails-app/services/demo/HelperService.groovy
package demo
class HelperService {
int getNumberOfPeople() {
Person.count()
}
}
A utility class:
// src/groovy/demo/SomeHelper.groovy
package demo
class SomeHelper {
int getNumberOfPeople() {
Person.count()
}
}
A unit test for the service:
// test/unit/demo/HelperServiceSpec.grovy
package demo
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(HelperService)
#Mock(Person)
class HelperServiceSpec extends Specification {
void "test gorm method"() {
expect:
service.numberOfPeople == 0
}
}
A unit test for the utility class:
// test/unit/demo/SomeHelperSpec.groovy
package demo
import spock.lang.Specification
#Mock(Person)
class SomeHelperSpec extends Specification {
void "test gorm method"() {
given:
def helper = new SomeHelper()
expect:
helper.numberOfPeople == 0
}
}
Both of those tests pass. If you remove the #Mock(Person) from either of them, the test will fail with the following:
java.lang.IllegalStateException: Method on class [demo.Person] was used outside of a Grails application. If running in the context of a test using the mocking API or bootstrap Grails correctly.
If the problem is happening in some context other than a test, knowing more about the context would be helpful. Other likely suspects are Java entities that are not being initialized properly for some reason and in some cases domain classes brought in from plugins can be problematic.
Also, knowing what version of Grails you are using may be relevant.
I hope that all makes sense.
You shouldn't use domain classes inside src/groovy or inside utils. You should use services for it. Check out best practises for Grails architecture:
Domain
Favor placing model domain specific logic in its own domain. Anything that applies to a single domain with few dependencies should go in its domain class. But keep it restricted to the logic specific to that domain only - more complex business logic that deals with a group of domains belongs to a service.
To reuse common partial queries or decompose the complex logic, use named queries and chain them together as required, just like one commonly chains jQuery function calls.
Don't mix any other common utility classes or value objects in the domain folder, rather they can go in src/groovy. If these classes need to support validation, one can annotate them with #Validateable.
If you still want to do it, check this FAQ:
import org.codehaus.groovy.grails.commons.ApplicationHolder
//…
def book = ApplicationHolder.application.getClassForName("library.Book").findByTitle("Groovy in Action")
I've got some class with property marked as internal.
Then I try to set that property from test code which is in java.
How can I access those properties? test code and class code are in the same package.
example:
class MainActivity : AppCompatActivity() {
interal var someProperty = "test"
}
test code:
#Test
public void firstStartTest() {
val activity = MainActivity()
activity.setSomeProperty("something") //does not compile
}
Android Studio is suggesting activity.setSomeProperty$production_sources_for_module_app();
but this also does not compile.
Both classes (MainActivity and test class) must be in one module. This is a module definition:
More specifically, a module is a set of Kotlin files compiled together:
an IntelliJ IDEA module;
a Maven or Gradle project;
a set of files
compiled with one invocation of the Ant task.
https://kotlinlang.org/docs/reference/visibility-modifiers.html
It means, check your project structure.
add #JvmField annotation.
It treats variable as java protected
There are two ways of doing this:
Make the property protected. Note on how Java & Kotlin treat protected differently. In Java it's possible that other classes in the same package access protected members. Thus your test class (in Java) can access it.
Access the property via its ugly name. It should be sort of like activity.setSomeProperty$production_.... Make use the autocomplete. From the documentation:
Members of internal classes go through name mangling, to make it
harder to accidentally use them from Java and to allow overloading for
members with the same signature that don't see each other according to
Kotlin rules;