How to instrument/rebase inner classes with Byte Buddy? - java

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.

Related

org.reflections without specifying a package [duplicate]

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

Access Java class defined in a file with many classes from Kotlin

I have a package which contains a public Kotlin class and a Java file with many package-private top-level classes like so:
com.example.mypackage
- KotlinClass.kt
- JavaClasses.java
-- Class1
-- Class2
-- ...
If I try to access any class from JavaClasses in KotlinClass I get Unresolved reference error. Is it possible to access these classes?
I cannot change JavaClasses.java because it's generated.
It should be possible to access these classes with the setup that you described, the only time I get a an Unresolved reference error is when there are errors within the java file. Perhaps because JavaClasses.java is generated it doesn't have the correct package declaration at the top of the file, which in your case should be package com.example.mypackage;

Java : Interface in a class is not accessible while importing

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

how to access internal properties from java test code

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;

jaxb.index and nested classes (and OSGi)

When I try to refer to nested classes within my jaxb.index file, an exception is thrown during serialization. How can this be avoided?
This is in an Eclipse RCP application. The classes causing the exception are in a different plug-in than the one that creates the JAXB context and initiates serialization. The classes are in one of the plug-in's exported packages.
The class structure looks like this (names have been changed):
#XmlRootElement(name="foo")
#XmlAccessorType (XmlAccessType.FIELD)
public class Foo extends AbstractFoo {
...
#XmlRootElement(name="fooMetric")
#XmlAccessorType (XmlAccessType.FIELD)
public static class FooMetric implements IFooMetric {
...
}
}
The jaxb.index file contains these:
Foo
Foo.FooMetric
During serialization, the exception says to use "OuterClass.InnerClass" -- which I'm doing.
javax.xml.bind.JAXBException: error loading class "Foo.FooMetric" listed in com/mypackage/jaxb.index, make sure that entries are accessable on CLASSPATH and of the form "ClassName" or "OuterClass.InnerClass", not "ClassName.class" or "fully.qualified.ClassName"
- with linked exception:
[java.lang.ClassNotFoundException: com.mypackage.Foo.FooMetric]
The javadocs ("Format for jaxb.index") also suggests that jaxb.index can contain entries of the form OuterClass.InnerClass.
Constraints on class name occuring in a jaxb.index file are:
Must not end with ".class".
Class names are resolved relative to package containing jaxb.index file. Only classes occuring directly in package containing jaxb.index file are allowed.
Fully qualified class names are not allowed. A qualified class name,relative to current package, is only allowed to specify a nested or inner class.
However, this does not appear to work. What will make it work?
The solution I found (by trial and error) was to use OuterClass$InnerClass in jaxb.index instead of OuterClass.InnerClass. This allows serialization to complete successfully.
However, I haven't found any authoritative source that recommends this.
[I'm posting this solution per stackoverflow guidelines, but would love to see and accept a better answer.]

Categories

Resources